From 9366a2948399007b227a7130e8ce580eb3aacb24 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 10 Jan 2016 13:04:55 +0000 Subject: [PATCH 01/20] Implement core device storage (fixes #219). --- artiq/coredevice/core.py | 24 ++++++++++++ artiq/gateware/amp/kernel_cpu.py | 2 +- artiq/runtime/Makefile | 10 +++-- artiq/runtime/kloader.h | 4 +- artiq/runtime/ksupport.c | 53 ++++++++++++++++++++++++++ artiq/runtime/ksupport.h | 8 ++++ artiq/runtime/ksupport.ld | 7 ++-- artiq/runtime/main.c | 4 ++ artiq/runtime/messages.h | 28 ++++++++++++++ artiq/runtime/runtime.ld | 6 ++- artiq/runtime/session.c | 64 ++++++++++++++++++++++++++++++++ 11 files changed, 198 insertions(+), 12 deletions(-) diff --git a/artiq/coredevice/core.py b/artiq/coredevice/core.py index 2be0ccac2..7d0b379c2 100644 --- a/artiq/coredevice/core.py +++ b/artiq/coredevice/core.py @@ -37,6 +37,18 @@ class CompileError(Exception): def rtio_get_counter() -> TInt64: raise NotImplementedError("syscall not simulated") +@syscall +def cache_get(TStr) -> TList(TInt32): + raise NotImplementedError("syscall not simulated") + +@syscall +def cache_put(TStr, TList(TInt32)): + raise NotImplementedError("syscall not simulated") + +@syscall +def cache_clear(TStr): + raise NotImplementedError("syscall not simulated") + class Core: """Core device driver. @@ -108,3 +120,15 @@ class Core: min_now = rtio_get_counter() + 125000 if now_mu() < min_now: at_mu(min_now) + + @kernel + def get_cache(self, key): + return cache_get(key) + + @kernel + def put_cache(self, key, value): + return cache_put(key, value) + + @kernel + def clear_cache(self, key): + return cache_clear(key) diff --git a/artiq/gateware/amp/kernel_cpu.py b/artiq/gateware/amp/kernel_cpu.py index 6aca00d32..b8aa29803 100644 --- a/artiq/gateware/amp/kernel_cpu.py +++ b/artiq/gateware/amp/kernel_cpu.py @@ -7,7 +7,7 @@ from misoc.integration.soc_core import mem_decoder class KernelCPU(Module): def __init__(self, platform, - exec_address=0x40800000, + exec_address=0x42000000, main_mem_origin=0x40000000, l2_size=8192): self._reset = CSRStorage(reset=1) diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index 3b81d5af8..460f1275c 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -9,7 +9,8 @@ OBJECTS := isr.o clock.o rtiocrg.o flash_storage.o mailbox.o \ OBJECTS_KSUPPORT := ksupport.o artiq_personality.o mailbox.o \ bridge.o rtio.o ttl.o dds.o -CFLAGS += -I$(MISOC_DIRECTORY)/software/include/dyld \ +CFLAGS += -I$(LIBALLOC_DIRECTORY) \ + -I$(MISOC_DIRECTORY)/software/include/dyld \ -I$(LIBDYLD_DIRECTORY)/include \ -I$(LIBUNWIND_DIRECTORY) \ -I$(LIBUNWIND_DIRECTORY)/../unwinder/include \ @@ -31,10 +32,11 @@ runtime.elf: $(OBJECTS) -N -o $@ \ ../libbase/crt0-$(CPU).o \ $(OBJECTS) \ - -L../libbase \ -L../libcompiler_rt \ + -L../libbase \ + -L../liballoc \ -L../liblwip \ - -lbase -lcompiler_rt -llwip + -lbase -lcompiler_rt -lalloc -llwip @chmod -x $@ ksupport.elf: $(OBJECTS_KSUPPORT) @@ -48,7 +50,7 @@ ksupport.elf: $(OBJECTS_KSUPPORT) -L../libcompiler_rt \ -L../libunwind \ -L../libdyld \ - -lbase -lcompiler_rt -lunwind -ldyld + -lbase -lcompiler_rt -ldyld -lunwind @chmod -x $@ ksupport_data.o: ksupport.elf diff --git a/artiq/runtime/kloader.h b/artiq/runtime/kloader.h index 92f353df0..fffcdb36a 100644 --- a/artiq/runtime/kloader.h +++ b/artiq/runtime/kloader.h @@ -3,8 +3,8 @@ #include "artiq_personality.h" -#define KERNELCPU_EXEC_ADDRESS 0x40800000 -#define KERNELCPU_PAYLOAD_ADDRESS 0x40820000 +#define KERNELCPU_EXEC_ADDRESS 0x42000000 +#define KERNELCPU_PAYLOAD_ADDRESS 0x42020000 #define KERNELCPU_LAST_ADDRESS (0x4fffffff - 1024*1024) #define KSUPPORT_HEADER_SIZE 0x80 diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index f67509c1b..af2610413 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -122,6 +122,10 @@ static const struct symbol runtime_exports[] = { {"dds_batch_exit", &dds_batch_exit}, {"dds_set", &dds_set}, + {"cache_get", &cache_get}, + {"cache_put", &cache_put}, + {"cache_clear", &cache_clear}, + /* end */ {NULL, NULL} }; @@ -444,6 +448,55 @@ void attribute_writeback(void *utypes) { } } +struct artiq_list cache_get(const char *key) +{ + struct msg_cache_get_request request; + struct msg_cache_get_reply *reply; + + request.type = MESSAGE_TYPE_CACHE_GET_REQUEST; + request.key = key; + mailbox_send_and_wait(&request); + + reply = mailbox_wait_and_receive(); + if(reply->type != MESSAGE_TYPE_CACHE_GET_REPLY) { + log("Malformed MESSAGE_TYPE_CACHE_GET_REQUEST reply type %d", + reply->type); + while(1); + } + + return (struct artiq_list) { reply->length, reply->elements }; +} + +void cache_put(const char *key, struct artiq_list value) +{ + struct msg_cache_put_request request; + struct msg_cache_put_reply *reply; + + request.type = MESSAGE_TYPE_CACHE_PUT_REQUEST; + request.key = key; + request.elements = value.elements; + request.length = value.length; + mailbox_send_and_wait(&request); + + reply = mailbox_wait_and_receive(); + if(reply->type != MESSAGE_TYPE_CACHE_PUT_REPLY) { + log("Malformed MESSAGE_TYPE_CACHE_PUT_REQUEST reply type %d", + reply->type); + while(1); + } + + if(!reply->succeeded) { + artiq_raise_from_c("CacheError", + "cannot put into a busy cache row", + 0, 0, 0); + } +} + +void cache_clear(const char *key) +{ + cache_put(key, (struct artiq_list) { 0, NULL }); +} + void lognonl(const char *fmt, ...) { struct msg_log request; diff --git a/artiq/runtime/ksupport.h b/artiq/runtime/ksupport.h index 88dc7e2a0..144392a15 100644 --- a/artiq/runtime/ksupport.h +++ b/artiq/runtime/ksupport.h @@ -1,12 +1,20 @@ #ifndef __KSTARTUP_H #define __KSTARTUP_H +struct artiq_list { + int32_t length; + int32_t *elements; +}; + long long int now_init(void); void now_save(long long int now); int watchdog_set(int ms); void watchdog_clear(int id); void send_rpc(int service, const char *tag, ...); int recv_rpc(void *slot); +struct artiq_list cache_get(const char *key); +void cache_put(const char *key, struct artiq_list value); +void cache_clear(const char *key); void lognonl(const char *fmt, ...); void log(const char *fmt, ...); diff --git a/artiq/runtime/ksupport.ld b/artiq/runtime/ksupport.ld index 016b9d008..da231b69b 100644 --- a/artiq/runtime/ksupport.ld +++ b/artiq/runtime/ksupport.ld @@ -3,11 +3,12 @@ ENTRY(_start) INCLUDE generated/regions.ld -/* First 8M of main memory are reserved for runtime code/data - * then comes kernel memory. First 128K of kernel memory are for support code. +/* First 32M of main memory are reserved for runtime + * code/data/heap, then comes kernel memory. + * First 128K of kernel memory are for support code. */ MEMORY { - ksupport (RWX) : ORIGIN = 0x40800000, LENGTH = 0x20000 + ksupport (RWX) : ORIGIN = 0x42000000, LENGTH = 0x20000 } /* On AMP systems, kernel stack is at the end of main RAM, diff --git a/artiq/runtime/main.c b/artiq/runtime/main.c index 2e8f7f15e..008a18b71 100644 --- a/artiq/runtime/main.c +++ b/artiq/runtime/main.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -263,6 +264,8 @@ static int check_test_mode(void) return 0; } +extern void _fheap, _eheap; + int main(void) { irq_setmask(0); @@ -271,6 +274,7 @@ int main(void) puts("ARTIQ runtime built "__DATE__" "__TIME__"\n"); + alloc_give(&_fheap, &_eheap - &_fheap); clock_init(); rtiocrg_init(); puts("Press 't' to enter test mode..."); diff --git a/artiq/runtime/messages.h b/artiq/runtime/messages.h index 2d055763e..c3a7c0a11 100644 --- a/artiq/runtime/messages.h +++ b/artiq/runtime/messages.h @@ -3,6 +3,7 @@ #include #include +#include enum { MESSAGE_TYPE_LOAD_REPLY, @@ -18,6 +19,10 @@ enum { MESSAGE_TYPE_RPC_RECV_REQUEST, MESSAGE_TYPE_RPC_RECV_REPLY, MESSAGE_TYPE_RPC_BATCH, + MESSAGE_TYPE_CACHE_GET_REQUEST, + MESSAGE_TYPE_CACHE_GET_REPLY, + MESSAGE_TYPE_CACHE_PUT_REQUEST, + MESSAGE_TYPE_CACHE_PUT_REPLY, MESSAGE_TYPE_LOG, MESSAGE_TYPE_BRG_READY, @@ -105,6 +110,29 @@ struct msg_rpc_batch { void *ptr; }; +struct msg_cache_get_request { + int type; + const char *key; +}; + +struct msg_cache_get_reply { + int type; + size_t length; + int32_t *elements; +}; + +struct msg_cache_put_request { + int type; + const char *key; + size_t length; + int32_t *elements; +}; + +struct msg_cache_put_reply { + int type; + int succeeded; +}; + struct msg_log { int type; const char *fmt; diff --git a/artiq/runtime/runtime.ld b/artiq/runtime/runtime.ld index ad575a097..011fda2a3 100644 --- a/artiq/runtime/runtime.ld +++ b/artiq/runtime/runtime.ld @@ -7,7 +7,7 @@ INCLUDE generated/regions.ld * ld does not allow this expression here. */ MEMORY { - runtime : ORIGIN = 0x40000000, LENGTH = 0x800000 /* 8M */ + runtime : ORIGIN = 0x40000000, LENGTH = 0x2000000 /* 32M */ } /* Kernel memory space start right after the runtime, @@ -65,5 +65,7 @@ SECTIONS *(.eh_frame) } - _heapstart = .; + _fheap = .; + . += 0x1800000; + _eheap = .; } diff --git a/artiq/runtime/session.c b/artiq/runtime/session.c index c6e215ace..fddf41781 100644 --- a/artiq/runtime/session.c +++ b/artiq/runtime/session.c @@ -908,6 +908,16 @@ static int send_rpc_request(int service, const char *tag, va_list args) return 1; } +struct cache_row { + struct cache_row *next; + char *key; + size_t length; + int32_t *elements; + int borrowed; +}; + +static struct cache_row *cache; + /* assumes output buffer is empty when called */ static int process_kmsg(struct msg_base *umsg) { @@ -984,6 +994,60 @@ static int process_kmsg(struct msg_base *umsg) break; } + case MESSAGE_TYPE_CACHE_GET_REQUEST: { + struct msg_cache_get_request *request = (struct msg_cache_get_request *)umsg; + struct msg_cache_get_reply reply; + + reply.type = MESSAGE_TYPE_CACHE_GET_REPLY; + reply.length = 0; + reply.elements = NULL; + + for(struct cache_row *iter = cache; iter; iter = iter->next) { + if(!strcmp(iter->key, request->key)) { + reply.length = iter->length; + reply.elements = iter->elements; + iter->borrowed = 1; + break; + } + } + + mailbox_send(&reply); + } + + case MESSAGE_TYPE_CACHE_PUT_REQUEST: { + struct msg_cache_put_request *request = (struct msg_cache_put_request *)umsg; + struct msg_cache_put_reply reply; + + reply.type = MESSAGE_TYPE_CACHE_PUT_REPLY; + + struct cache_row *row = NULL; + for(struct cache_row *iter = cache; iter; iter = iter->next) { + if(!strcmp(iter->key, request->key)) { + free(iter->elements); + row = iter; + break; + } + } + + if(!row) { + struct cache_row *row = calloc(1, sizeof(struct cache_row)); + row->key = calloc(strlen(request->key) + 1, 1); + strcpy(row->key, request->key); + } + + if(!row->borrowed) { + row->length = request->length; + row->elements = calloc(row->length, sizeof(int32_t)); + memcpy(row->elements, request->elements, + sizeof(int32_t) * row->length); + reply.succeeded = 1; + } else { + reply.succeeded = 0; + } + + mailbox_send(&reply); + } + default: { log("Received invalid message type %d from kernel CPU", umsg->type); From cc45694f5a3802b2cc872ccb27eaddafc36a9069 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 10 Jan 2016 13:08:26 +0000 Subject: [PATCH 02/20] Commit missing parts of 9366a29. --- artiq/coredevice/core.py | 6 +++--- artiq/runtime/session.c | 11 +++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/artiq/coredevice/core.py b/artiq/coredevice/core.py index 7d0b379c2..bd8483098 100644 --- a/artiq/coredevice/core.py +++ b/artiq/coredevice/core.py @@ -38,15 +38,15 @@ def rtio_get_counter() -> TInt64: raise NotImplementedError("syscall not simulated") @syscall -def cache_get(TStr) -> TList(TInt32): +def cache_get(key: TStr) -> TList(TInt32): raise NotImplementedError("syscall not simulated") @syscall -def cache_put(TStr, TList(TInt32)): +def cache_put(key: TStr, value: TList(TInt32)): raise NotImplementedError("syscall not simulated") @syscall -def cache_clear(TStr): +def cache_clear(key: TStr): raise NotImplementedError("syscall not simulated") class Core: diff --git a/artiq/runtime/session.c b/artiq/runtime/session.c index fddf41781..a41002a02 100644 --- a/artiq/runtime/session.c +++ b/artiq/runtime/session.c @@ -1036,10 +1036,13 @@ static int process_kmsg(struct msg_base *umsg) } if(!row->borrowed) { - row->length = request->length; - row->elements = calloc(row->length, sizeof(int32_t)); - memcpy(row->elements, request->elements, - sizeof(int32_t) * row->length); + if(request->length != 0) { + row->length = request->length; + row->elements = calloc(row->length, sizeof(int32_t)); + memcpy(row->elements, request->elements, + sizeof(int32_t) * row->length); + } + reply.succeeded = 1; } else { reply.succeeded = 0; From 72a3a12d529aebf3b01b2b7d5a2a57c263b818e2 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 10 Jan 2016 13:42:04 +0000 Subject: [PATCH 03/20] Fix versioneer (closes #199). --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index c68b6c04c..608dda10d 100755 --- a/setup.py +++ b/setup.py @@ -44,6 +44,7 @@ scripts = [ setup( name="artiq", version=versioneer.get_version(), + cmdclass=versioneer.get_cmdclass(), author="M-Labs / NIST Ion Storage Group", author_email="sb@m-labs.hk", url="http://m-labs.hk/artiq", From f8e50f2b0c493f29b11bc27bacf6e10e8c8072e4 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 10 Jan 2016 14:27:46 +0000 Subject: [PATCH 04/20] Remove redundant ksupport API. --- artiq/coredevice/core.py | 8 -------- artiq/runtime/ksupport.c | 6 ------ artiq/runtime/ksupport.h | 1 - 3 files changed, 15 deletions(-) diff --git a/artiq/coredevice/core.py b/artiq/coredevice/core.py index bd8483098..67b704ac5 100644 --- a/artiq/coredevice/core.py +++ b/artiq/coredevice/core.py @@ -45,10 +45,6 @@ def cache_get(key: TStr) -> TList(TInt32): def cache_put(key: TStr, value: TList(TInt32)): raise NotImplementedError("syscall not simulated") -@syscall -def cache_clear(key: TStr): - raise NotImplementedError("syscall not simulated") - class Core: """Core device driver. @@ -128,7 +124,3 @@ class Core: @kernel def put_cache(self, key, value): return cache_put(key, value) - - @kernel - def clear_cache(self, key): - return cache_clear(key) diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index af2610413..b35033ee8 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -124,7 +124,6 @@ static const struct symbol runtime_exports[] = { {"cache_get", &cache_get}, {"cache_put", &cache_put}, - {"cache_clear", &cache_clear}, /* end */ {NULL, NULL} @@ -492,11 +491,6 @@ void cache_put(const char *key, struct artiq_list value) } } -void cache_clear(const char *key) -{ - cache_put(key, (struct artiq_list) { 0, NULL }); -} - void lognonl(const char *fmt, ...) { struct msg_log request; diff --git a/artiq/runtime/ksupport.h b/artiq/runtime/ksupport.h index 144392a15..2194ef0db 100644 --- a/artiq/runtime/ksupport.h +++ b/artiq/runtime/ksupport.h @@ -14,7 +14,6 @@ void send_rpc(int service, const char *tag, ...); int recv_rpc(void *slot); struct artiq_list cache_get(const char *key); void cache_put(const char *key, struct artiq_list value); -void cache_clear(const char *key); void lognonl(const char *fmt, ...); void log(const char *fmt, ...); From 211af77e7731525b6dcfb695eac517bd2067bb4c Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 10 Jan 2016 14:43:21 +0000 Subject: [PATCH 05/20] validators.escape: cache_get result lives forever. --- artiq/compiler/types.py | 9 +++++++-- artiq/compiler/validators/escape.py | 8 +++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/artiq/compiler/types.py b/artiq/compiler/types.py index 25a8e9699..f5adf79e5 100644 --- a/artiq/compiler/types.py +++ b/artiq/compiler/types.py @@ -610,8 +610,13 @@ def is_function(typ): def is_rpc_function(typ): return isinstance(typ.find(), TRPCFunction) -def is_c_function(typ): - return isinstance(typ.find(), TCFunction) +def is_c_function(typ, name=None): + typ = typ.find() + if name is None: + return isinstance(typ, TCFunction) + else: + return isinstance(typ, TCFunction) and \ + typ.name == name def is_builtin(typ, name=None): typ = typ.find() diff --git a/artiq/compiler/validators/escape.py b/artiq/compiler/validators/escape.py index 346f0c130..857fcd02a 100644 --- a/artiq/compiler/validators/escape.py +++ b/artiq/compiler/validators/escape.py @@ -87,7 +87,13 @@ class RegionOf(algorithm.Visitor): return None visit_BinOpT = visit_sometimes_allocating - visit_CallT = visit_sometimes_allocating + + def visit_CallT(self, node): + if types.is_c_function(node.func.type, "cache_get"): + # The cache is borrow checked dynamically + return None + else: + self.visit_sometimes_allocating(node) # Value lives as long as the object/container, if it's mutable, # or else forever From 4198601abb4df9ccfeb1c77617ff58a6d8a0fd12 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 10 Jan 2016 14:43:30 +0000 Subject: [PATCH 06/20] coredevice.exceptions: add CacheError. --- artiq/coredevice/exceptions.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/artiq/coredevice/exceptions.py b/artiq/coredevice/exceptions.py index c595d2fcc..46402a31f 100644 --- a/artiq/coredevice/exceptions.py +++ b/artiq/coredevice/exceptions.py @@ -11,6 +11,10 @@ class InternalError(Exception): """Raised when the runtime encounters an internal error condition.""" +class CacheError(Exception): + """Raised when putting a value into a cache row would violate memory safety.""" + + class RTIOUnderflow(Exception): """Raised when the CPU fails to submit a RTIO event early enough (with respect to the event's timestamp). From b29817097335a9e9c4ccebad113a6a2b0e03684c Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 10 Jan 2016 15:45:20 +0000 Subject: [PATCH 07/20] transforms.artiq_ir_generator: annotate embedded functions as internal. --- artiq/compiler/transforms/artiq_ir_generator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/artiq/compiler/transforms/artiq_ir_generator.py b/artiq/compiler/transforms/artiq_ir_generator.py index eb80f14fc..49826b11c 100644 --- a/artiq/compiler/transforms/artiq_ir_generator.py +++ b/artiq/compiler/transforms/artiq_ir_generator.py @@ -320,7 +320,8 @@ class ARTIQIRGenerator(algorithm.Visitor): return self.append(ir.Closure(func, self.current_env)) def visit_FunctionDefT(self, node, in_class=None): - func = self.visit_function(node, is_lambda=False, is_internal=len(self.name) > 2) + func = self.visit_function(node, is_lambda=False, + is_internal=len(self.name) > 0 or '.' in node.name) if in_class is None: self._set_local(node.name, func) else: From c46fccdf4bf74facca8fe80febebc2f263a6ad17 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 10 Jan 2016 15:45:34 +0000 Subject: [PATCH 08/20] transforms.llvm_ir_generator: annotate invariant loads as such. --- artiq/compiler/transforms/llvm_ir_generator.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index 10291f820..4563509ca 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -176,6 +176,7 @@ class LLVMIRGenerator: self.llobject_map = {} self.phis = [] self.debug_info_emitter = DebugInfoEmitter(self.llmodule) + self.empty_metadata = self.llmodule.add_metadata([]) def needs_sret(self, lltyp, may_be_large=True): if isinstance(lltyp, ll.VoidType): @@ -628,6 +629,7 @@ class LLVMIRGenerator: outer_index = list(env_ty.params.keys()).index("$outer") llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)]) llouterenv = self.llbuilder.load(llptr) + llouterenv.metadata['invariant.load'] = self.empty_metadata return self.llptr_to_var(llouterenv, env_ty.params["$outer"], var_name) def process_GetLocal(self, insn): @@ -638,7 +640,9 @@ class LLVMIRGenerator: def process_GetConstructor(self, insn): env = insn.environment() llptr = self.llptr_to_var(self.map(env), env.type, insn.var_name, insn.type) - return self.llbuilder.load(llptr) + llconstr = self.llbuilder.load(llptr) + llconstr.metadata['invariant.load'] = self.empty_metadata + return llconstr def process_SetLocal(self, insn): env = insn.environment() @@ -874,6 +878,7 @@ class LLVMIRGenerator: outer_index = list(env_ty.params.keys()).index("$outer") llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)]) llouterenv = self.llbuilder.load(llptr) + llouterenv.metadata['invariant.load'] = self.empty_metadata return self.llptr_to_var(llouterenv, env_ty.params["$outer"], var_name) else: return llenv From bbd0ca7827025789afc865a7b67fd22a2abb9862 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 10 Jan 2016 15:45:56 +0000 Subject: [PATCH 09/20] Commit missing parts of 9366a29. --- artiq/runtime/session.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/artiq/runtime/session.c b/artiq/runtime/session.c index a41002a02..b32634ff1 100644 --- a/artiq/runtime/session.c +++ b/artiq/runtime/session.c @@ -1012,6 +1012,7 @@ static int process_kmsg(struct msg_base *umsg) } mailbox_send(&reply); + break; } case MESSAGE_TYPE_CACHE_PUT_REQUEST: { @@ -1049,6 +1050,7 @@ static int process_kmsg(struct msg_base *umsg) } mailbox_send(&reply); + break; } default: { From f92ce291efa18be5d3788addc1d237a041bd8c1e Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 10 Jan 2016 15:54:46 +0000 Subject: [PATCH 10/20] transforms.llvm_ir_generator: call devirtualized functions directly. --- artiq/compiler/transforms/llvm_ir_generator.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index 4563509ca..45632bf6a 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -925,10 +925,13 @@ class LLVMIRGenerator: return llvalue def _prepare_closure_call(self, insn): - llclosure = self.map(insn.target_function()) llargs = [self.map(arg) for arg in insn.arguments()] + llclosure = self.map(insn.target_function()) llenv = self.llbuilder.extract_value(llclosure, 0) - llfun = self.llbuilder.extract_value(llclosure, 1) + if insn.static_target_function is None: + llfun = self.llbuilder.extract_value(llclosure, 1) + else: + llfun = self.map(insn.static_target_function) return llfun, [llenv] + list(llargs) def _prepare_ffi_call(self, insn): From a2a00e8b35eb906beb0938631d95b7b96d4c7be6 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 10 Jan 2016 16:15:23 +0000 Subject: [PATCH 11/20] Set LLVM inlining threshold explicitly. llvmlite's pass manager builder does not use a default when it is not specified explicitly, unlike the C++ one. --- artiq/compiler/targets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/artiq/compiler/targets.py b/artiq/compiler/targets.py index 0b9a382e3..506a52bea 100644 --- a/artiq/compiler/targets.py +++ b/artiq/compiler/targets.py @@ -97,6 +97,7 @@ class Target: llpassmgrbuilder = llvm.create_pass_manager_builder() llpassmgrbuilder.opt_level = 2 # -O2 llpassmgrbuilder.size_level = 1 # -Os + llpassmgrbuilder.inlining_threshold = 75 # -Os threshold llpassmgr = llvm.create_module_pass_manager() llpassmgrbuilder.populate(llpassmgr) From edb7423a4f243fd3bff03c1088ddceacb27079c8 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 10 Jan 2016 16:36:04 +0000 Subject: [PATCH 12/20] transforms.llvm_ir_generator: use getelementptr inbounds. --- .../compiler/transforms/llvm_ir_generator.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index 45632bf6a..25b572c2d 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -616,7 +616,8 @@ class LLVMIRGenerator: llalloc = self.llbuilder.alloca(self.llty_of_type(insn.type, bare=True)) for index, operand in enumerate(insn.operands): lloperand = self.map(operand) - llfieldptr = self.llbuilder.gep(llalloc, [self.llindex(0), self.llindex(index)]) + llfieldptr = self.llbuilder.gep(llalloc, [self.llindex(0), self.llindex(index)], + inbounds=True) self.llbuilder.store(lloperand, llfieldptr) return llalloc @@ -624,10 +625,12 @@ class LLVMIRGenerator: if var_name in env_ty.params and (var_type is None or env_ty.params[var_name] == var_type): var_index = list(env_ty.params.keys()).index(var_name) - return self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(var_index)]) + return self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(var_index)], + inbounds=True) else: outer_index = list(env_ty.params.keys()).index("$outer") - llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)]) + llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)], + inbounds=True) llouterenv = self.llbuilder.load(llptr) llouterenv.metadata['invariant.load'] = self.empty_metadata return self.llptr_to_var(llouterenv, env_ty.params["$outer"], var_name) @@ -671,14 +674,14 @@ class LLVMIRGenerator: else: llptr = self.llbuilder.gep(self.map(insn.object()), [self.llindex(0), self.llindex(self.attr_index(insn))], - name=insn.name) + inbounds=True, name=insn.name) return self.llbuilder.load(llptr) def process_SetAttr(self, insn): assert builtins.is_allocated(insn.object().type) llptr = self.llbuilder.gep(self.map(insn.object()), [self.llindex(0), self.llindex(self.attr_index(insn))], - name=insn.name) + inbounds=True, name=insn.name) return self.llbuilder.store(self.map(insn.value()), llptr) def process_GetElem(self, insn): @@ -876,7 +879,8 @@ class LLVMIRGenerator: def get_outer(llenv, env_ty): if "$outer" in env_ty.params: outer_index = list(env_ty.params.keys()).index("$outer") - llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)]) + llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)], + inbounds=True) llouterenv = self.llbuilder.load(llptr) llouterenv.metadata['invariant.load'] = self.empty_metadata return self.llptr_to_var(llouterenv, env_ty.params["$outer"], var_name) @@ -1258,7 +1262,8 @@ class LLVMIRGenerator: cleanup=True) llrawexn = self.llbuilder.extract_value(lllandingpad, 1) llexn = self.llbuilder.bitcast(llrawexn, self.llty_of_type(insn.type)) - llexnnameptr = self.llbuilder.gep(llexn, [self.llindex(0), self.llindex(0)]) + llexnnameptr = self.llbuilder.gep(llexn, [self.llindex(0), self.llindex(0)], + inbounds=True) llexnname = self.llbuilder.load(llexnnameptr) for target, typ in insn.clauses(): From 7f914a057c407a8ee786607a99d3dc0f8097295b Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 10 Jan 2016 17:31:59 +0000 Subject: [PATCH 13/20] transforms.llvm_ir_generator: use sret attribute. --- .../compiler/transforms/llvm_ir_generator.py | 42 +++++++++---------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index 25b572c2d..685918981 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -193,6 +193,10 @@ class LLVMIRGenerator: else: return True + def has_sret(self, functy): + llretty = self.llty_of_type(functy.ret, for_return=True) + return self.needs_sret(llretty) + def llty_of_type(self, typ, bare=False, for_return=False): typ = typ.find() if types.is_tuple(typ): @@ -219,22 +223,14 @@ class LLVMIRGenerator: for arg in typ.optargs], return_type=llretty) - # TODO: actually mark the first argument as sret (also noalias nocapture). - # llvmlite currently does not have support for this; - # https://github.com/numba/llvmlite/issues/91. - if sretarg: - llty.__has_sret = True - else: - llty.__has_sret = False - if bare: return llty else: return ll.LiteralStructType([envarg, llty.as_pointer()]) elif types.is_method(typ): - llfuncty = self.llty_of_type(types.get_method_function(typ)) + llfunty = self.llty_of_type(types.get_method_function(typ)) llselfty = self.llty_of_type(types.get_method_self(typ)) - return ll.LiteralStructType([llfuncty, llselfty]) + return ll.LiteralStructType([llfunty, llselfty]) elif builtins.is_none(typ): if for_return: return llvoid @@ -400,8 +396,13 @@ class LLVMIRGenerator: elif isinstance(value, ir.Function): llfun = self.llmodule.get_global(value.name) if llfun is None: - llfun = ll.Function(self.llmodule, self.llty_of_type(value.type, bare=True), - value.name) + llfunty = self.llty_of_type(value.type, bare=True) + llfun = ll.Function(self.llmodule, llfunty, value.name) + + llretty = self.llty_of_type(value.type.ret, for_return=True) + if self.needs_sret(llretty): + llfun.args[0].add_attribute('sret') + return llfun else: assert False @@ -516,11 +517,7 @@ class LLVMIRGenerator: def process_function(self, func): try: - self.llfunction = self.llmodule.get_global(func.name) - - if self.llfunction is None: - llfunty = self.llty_of_type(func.type, bare=True) - self.llfunction = ll.Function(self.llmodule, llfunty, func.name) + self.llfunction = self.map(func) if func.is_internal: self.llfunction.linkage = 'internal' @@ -533,7 +530,7 @@ class LLVMIRGenerator: disubprogram = self.debug_info_emitter.emit_subprogram(func, self.llfunction) # First, map arguments. - if self.llfunction.type.pointee.__has_sret: + if self.has_sret(func.type): llactualargs = self.llfunction.args[1:] else: llactualargs = self.llfunction.args @@ -1095,7 +1092,7 @@ class LLVMIRGenerator: else: llfun, llargs = self._prepare_closure_call(insn) - if llfun.type.pointee.__has_sret: + if self.has_sret(insn.target_function().type): llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), []) llresultslot = self.llbuilder.alloca(llfun.type.pointee.args[0].pointee) @@ -1221,11 +1218,12 @@ class LLVMIRGenerator: if builtins.is_none(insn.value().type): return self.llbuilder.ret_void() else: - if self.llfunction.type.pointee.__has_sret: - self.llbuilder.store(self.map(insn.value()), self.llfunction.args[0]) + llvalue = self.map(insn.value()) + if self.needs_sret(llvalue): + self.llbuilder.store(llvalue, self.llfunction.args[0]) return self.llbuilder.ret_void() else: - return self.llbuilder.ret(self.map(insn.value())) + return self.llbuilder.ret(llvalue) def process_Unreachable(self, insn): return self.llbuilder.unreachable() From d436093317808f68370edab32832da9c2394d6ab Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 10 Jan 2016 17:42:40 +0000 Subject: [PATCH 14/20] transforms.llvm_ir_generator: use sret attribute on FFI calls too. --- .../compiler/transforms/llvm_ir_generator.py | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index 685918981..a507ffbcc 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -940,10 +940,17 @@ class LLVMIRGenerator: llfunname = insn.target_function().type.name llfun = self.llmodule.get_global(llfunname) if llfun is None: - llfunty = ll.FunctionType(self.llty_of_type(insn.type, for_return=True), - [llarg.type for llarg in llargs]) - llfun = ll.Function(self.llmodule, llfunty, - insn.target_function().type.name) + llretty = self.llty_of_type(insn.type, for_return=True) + if self.needs_sret(llretty): + llfunty = ll.FunctionType(llvoid, [llretty.as_pointer()] + + [llarg.type for llarg in llargs]) + else: + llfunty = ll.FunctionType(llretty, [llarg.type for llarg in llargs]) + + llfun = ll.Function(self.llmodule, llfunty, + insn.target_function().type.name) + if self.needs_sret(llretty): + llfun.args[0].add_attribute('sret') return llfun, list(llargs) # See session.c:{send,receive}_rpc_value and comm_generic.py:_{send,receive}_rpc_value. @@ -1087,24 +1094,22 @@ class LLVMIRGenerator: llnormalblock=None, llunwindblock=None) elif types.is_c_function(insn.target_function().type): llfun, llargs = self._prepare_ffi_call(insn) - return self.llbuilder.call(llfun, llargs, - name=insn.name) else: llfun, llargs = self._prepare_closure_call(insn) - if self.has_sret(insn.target_function().type): - llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), []) + if self.has_sret(insn.target_function().type): + llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), []) - llresultslot = self.llbuilder.alloca(llfun.type.pointee.args[0].pointee) - self.llbuilder.call(llfun, [llresultslot] + llargs) - llresult = self.llbuilder.load(llresultslot) + llresultslot = self.llbuilder.alloca(llfun.type.pointee.args[0].pointee) + self.llbuilder.call(llfun, [llresultslot] + llargs) + llresult = self.llbuilder.load(llresultslot) - self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr]) + self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr]) - return llresult - else: - return self.llbuilder.call(llfun, llargs, - name=insn.name) + return llresult + else: + return self.llbuilder.call(llfun, llargs, + name=insn.name) def process_Invoke(self, insn): llnormalblock = self.map(insn.normal_target()) From ee1ef551bc8521c19de481b13c5d1195aee69e9f Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 10 Jan 2016 18:04:10 +0000 Subject: [PATCH 15/20] Fix symbolizer invocation with no addresses. --- artiq/compiler/targets.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/artiq/compiler/targets.py b/artiq/compiler/targets.py index 506a52bea..d6b3aa8c2 100644 --- a/artiq/compiler/targets.py +++ b/artiq/compiler/targets.py @@ -148,6 +148,9 @@ class Target: return results["output"].read() def symbolize(self, library, addresses): + if addresses == []: + return [] + # Addresses point one instruction past the jump; offset them back by 1. offset_addresses = [hex(addr - 1) for addr in addresses] with RunTool([self.triple + "-addr2line", "--functions", "--inlines", From b669e835540e7483e085580533ea0bedd1f50bb9 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 10 Jan 2016 18:06:28 +0000 Subject: [PATCH 16/20] Inject parameters into coredevice exception message when mapping. --- artiq/coredevice/comm_generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/coredevice/comm_generic.py b/artiq/coredevice/comm_generic.py index 637f94d32..8d8dccf87 100644 --- a/artiq/coredevice/comm_generic.py +++ b/artiq/coredevice/comm_generic.py @@ -512,7 +512,7 @@ class CommGeneric: assert exception.id != 0 python_exn_type = object_map.retrieve(exception.id) - python_exn = python_exn_type(message) + python_exn = python_exn_type(message.format(*params)) python_exn.artiq_exception = exception raise python_exn From d1319b83e5ea69f53d21b6a49a5ce154189ca727 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 10 Jan 2016 18:07:08 +0000 Subject: [PATCH 17/20] runtime: properly format addresses in InternalError message. --- artiq/runtime/ksupport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index b35033ee8..f9688d781 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -225,7 +225,7 @@ void exception_handler(unsigned long vect, unsigned long *regs, unsigned long pc, unsigned long ea) { artiq_raise_from_c("InternalError", - "Hardware exception {0} at PC {1}, EA {2}", + "Hardware exception {0} at PC 0x{1:08x}, EA 0x{2:08x}", vect, pc, ea); } From 1be9e7576de6491f11de79a06f903afbd7118ffe Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 10 Jan 2016 18:31:35 +0000 Subject: [PATCH 18/20] transforms.llvm_ir_generator: use byval for FFI calls where appropriate. --- artiq/compiler/transforms/llvm_ir_generator.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index a507ffbcc..c86118c45 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -936,7 +936,18 @@ class LLVMIRGenerator: return llfun, [llenv] + list(llargs) def _prepare_ffi_call(self, insn): - llargs = [self.map(arg) for arg in insn.arguments()] + llargs = [] + byvals = [] + for i, arg in enumerate(insn.arguments()): + llarg = self.map(arg) + if isinstance(llarg.type, (ll.LiteralStructType, ll.IdentifiedStructType)): + llslot = self.llbuilder.alloca(llarg.type) + self.llbuilder.store(llarg, llslot) + llargs.append(llslot) + byvals.append(i) + else: + llargs.append(llarg) + llfunname = insn.target_function().type.name llfun = self.llmodule.get_global(llfunname) if llfun is None: @@ -951,6 +962,10 @@ class LLVMIRGenerator: insn.target_function().type.name) if self.needs_sret(llretty): llfun.args[0].add_attribute('sret') + byvals = [i + 1 for i in byvals] + for i in byvals: + llfun.args[i].add_attribute('byval') + return llfun, list(llargs) # See session.c:{send,receive}_rpc_value and comm_generic.py:_{send,receive}_rpc_value. From 225f7d73022dbcc8abc314ea911190d8ddf388ac Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 10 Jan 2016 20:01:20 +0000 Subject: [PATCH 19/20] Commit missing parts of 9366a29. --- artiq/coredevice/core.py | 2 +- artiq/runtime/session.c | 9 +++++-- artiq/test/coredevice/cache.py | 44 ++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 artiq/test/coredevice/cache.py diff --git a/artiq/coredevice/core.py b/artiq/coredevice/core.py index 67b704ac5..b7f1f7276 100644 --- a/artiq/coredevice/core.py +++ b/artiq/coredevice/core.py @@ -42,7 +42,7 @@ def cache_get(key: TStr) -> TList(TInt32): raise NotImplementedError("syscall not simulated") @syscall -def cache_put(key: TStr, value: TList(TInt32)): +def cache_put(key: TStr, value: TList(TInt32)) -> TNone: raise NotImplementedError("syscall not simulated") class Core: diff --git a/artiq/runtime/session.c b/artiq/runtime/session.c index b32634ff1..94ed98591 100644 --- a/artiq/runtime/session.c +++ b/artiq/runtime/session.c @@ -940,9 +940,12 @@ static int process_kmsg(struct msg_base *umsg) case MESSAGE_TYPE_FINISHED: out_packet_empty(REMOTEMSG_TYPE_KERNEL_FINISHED); + for(struct cache_row *iter = cache; iter; iter = iter->next) + iter->borrowed = 0; + kloader_stop(); user_kernel_state = USER_KERNEL_LOADED; - mailbox_acknowledge(); + break; case MESSAGE_TYPE_EXCEPTION: { @@ -1031,9 +1034,11 @@ static int process_kmsg(struct msg_base *umsg) } if(!row) { - struct cache_row *row = calloc(1, sizeof(struct cache_row)); + row = calloc(1, sizeof(struct cache_row)); row->key = calloc(strlen(request->key) + 1, 1); strcpy(row->key, request->key); + row->next = cache; + cache = row; } if(!row->borrowed) { diff --git a/artiq/test/coredevice/cache.py b/artiq/test/coredevice/cache.py new file mode 100644 index 000000000..68033b142 --- /dev/null +++ b/artiq/test/coredevice/cache.py @@ -0,0 +1,44 @@ +from artiq.language import * +from artiq.coredevice.exceptions import * +from artiq.test.hardware_testbench import ExperimentCase + + +class _Cache(EnvExperiment): + def build(self): + self.setattr_device("core") + self.print = lambda x: print(x) + + @kernel + def get(self, key): + return self.core.get_cache(key) + + @kernel + def put(self, key, value): + self.core.put_cache(key, value) + + @kernel + def get_put(self, key, value): + self.get(key) + self.put(key, value) + +class CacheTest(ExperimentCase): + def test_get_empty(self): + exp = self.create(_Cache) + self.assertEqual(exp.get("x1"), []) + + def test_put_get(self): + exp = self.create(_Cache) + exp.put("x2", [1, 2, 3]) + self.assertEqual(exp.get("x2"), [1, 2, 3]) + + def test_replace(self): + exp = self.create(_Cache) + exp.put("x3", [1, 2, 3]) + exp.put("x3", [1, 2, 3, 4, 5]) + self.assertEqual(exp.get("x3"), [1, 2, 3, 4, 5]) + + def test_borrow(self): + exp = self.create(_Cache) + exp.put("x4", [1, 2, 3]) + with self.assertRaises(CacheError): + exp.get_put("x4", []) From 63f7899f93e7de6720d3b9b9bbe5c85d4676ecc2 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 10 Jan 2016 20:25:58 +0000 Subject: [PATCH 20/20] Commit missing parts of 7f914a0. --- artiq/compiler/transforms/llvm_ir_generator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index c86118c45..23ead974e 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -191,6 +191,7 @@ class LLVMIRGenerator: and len(lltyp.elements) <= 2: return not any([self.needs_sret(elt, may_be_large=False) for elt in lltyp.elements]) else: + assert isinstance(lltyp, ll.Type) return True def has_sret(self, functy): @@ -1239,7 +1240,7 @@ class LLVMIRGenerator: return self.llbuilder.ret_void() else: llvalue = self.map(insn.value()) - if self.needs_sret(llvalue): + if self.needs_sret(llvalue.type): self.llbuilder.store(llvalue, self.llfunction.args[0]) return self.llbuilder.ret_void() else: