diff --git a/artiq/compiler/targets.py b/artiq/compiler/targets.py index 0b9a382e3..d6b3aa8c2 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) @@ -147,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", 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: diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index 10291f820..23ead974e 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): @@ -190,8 +191,13 @@ 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): + 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): @@ -218,22 +224,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 @@ -399,8 +397,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 @@ -515,11 +518,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' @@ -532,7 +531,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 @@ -615,7 +614,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 @@ -623,11 +623,14 @@ 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) def process_GetLocal(self, insn): @@ -638,7 +641,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() @@ -667,14 +672,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): @@ -872,8 +877,10 @@ 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) else: return llenv @@ -920,21 +927,46 @@ 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): - 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: - 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') + 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. @@ -1078,24 +1110,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 llfun.type.pointee.__has_sret: - 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()) @@ -1209,11 +1239,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.type): + 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() @@ -1250,7 +1281,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(): 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 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 diff --git a/artiq/coredevice/core.py b/artiq/coredevice/core.py index 2be0ccac2..b7f1f7276 100644 --- a/artiq/coredevice/core.py +++ b/artiq/coredevice/core.py @@ -37,6 +37,14 @@ class CompileError(Exception): def rtio_get_counter() -> TInt64: raise NotImplementedError("syscall not simulated") +@syscall +def cache_get(key: TStr) -> TList(TInt32): + raise NotImplementedError("syscall not simulated") + +@syscall +def cache_put(key: TStr, value: TList(TInt32)) -> TNone: + raise NotImplementedError("syscall not simulated") + class Core: """Core device driver. @@ -108,3 +116,11 @@ 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) 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). 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..f9688d781 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -122,6 +122,9 @@ static const struct symbol runtime_exports[] = { {"dds_batch_exit", &dds_batch_exit}, {"dds_set", &dds_set}, + {"cache_get", &cache_get}, + {"cache_put", &cache_put}, + /* end */ {NULL, NULL} }; @@ -222,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); } @@ -444,6 +447,50 @@ 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 lognonl(const char *fmt, ...) { struct msg_log request; diff --git a/artiq/runtime/ksupport.h b/artiq/runtime/ksupport.h index 88dc7e2a0..2194ef0db 100644 --- a/artiq/runtime/ksupport.h +++ b/artiq/runtime/ksupport.h @@ -1,12 +1,19 @@ #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 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..94ed98591 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) { @@ -930,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: { @@ -984,6 +997,67 @@ 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); + break; + } + + 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) { + 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) { + 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; + } + + mailbox_send(&reply); + break; + } + default: { log("Received invalid message type %d from kernel CPU", umsg->type); 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", []) 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",