From fd8b11532f3252698a2a8c999719810330dfeb42 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 3 Feb 2017 11:46:45 +0000 Subject: [PATCH] compiler, firmware: use Pascal strings everywhere. This removes a large amount of very ugly code, and also simplifies the compiler and runtime. --- artiq/compiler/ir.py | 7 - .../compiler/transforms/artiq_ir_generator.py | 36 ++--- .../compiler/transforms/llvm_ir_generator.py | 147 +++++++++--------- artiq/firmware/Cargo.lock | 1 + artiq/firmware/ksupport/api.rs | 7 +- artiq/firmware/ksupport/lib.rs | 57 +++---- artiq/firmware/liballoc_artiq/lib.rs | 2 - artiq/firmware/libstd_artiq/lib.rs | 3 +- artiq/firmware/runtime/Cargo.toml | 1 + artiq/firmware/runtime/kernel_proto.rs | 12 +- artiq/firmware/runtime/lib.rs | 1 + artiq/firmware/runtime/rpc_proto.rs | 26 ++-- artiq/firmware/runtime/session.rs | 66 +++----- artiq/runtime/artiq_personality.c | 31 ++-- artiq/runtime/artiq_personality.h | 31 +--- artiq/test/libartiq_support/artiq_terminate.c | 11 +- artiq/test/lit/integration/str.py | 1 + 17 files changed, 188 insertions(+), 252 deletions(-) diff --git a/artiq/compiler/ir.py b/artiq/compiler/ir.py index e62ca3dfc..6ae9bdf76 100644 --- a/artiq/compiler/ir.py +++ b/artiq/compiler/ir.py @@ -36,13 +36,6 @@ class TKeyword(types.TMono): def is_keyword(typ): return isinstance(typ, TKeyword) -class TExceptionTypeInfo(types.TMono): - def __init__(self): - super().__init__("exntypeinfo") - -def is_exn_typeinfo(typ): - return isinstance(typ, TExceptionTypeInfo) - class Value: """ An SSA value that keeps track of its uses. diff --git a/artiq/compiler/transforms/artiq_ir_generator.py b/artiq/compiler/transforms/artiq_ir_generator.py index 3ad09f300..518335e2f 100644 --- a/artiq/compiler/transforms/artiq_ir_generator.py +++ b/artiq/compiler/transforms/artiq_ir_generator.py @@ -1332,10 +1332,6 @@ class ARTIQIRGenerator(algorithm.Visitor): rhs_length = self.iterable_len(rhs) result_length = self.append(ir.Arith(ast.Add(loc=None), lhs_length, rhs_length)) - if builtins.is_str(node.left.type): - result_last = result_length - result_length = self.append(ir.Arith(ast.Add(loc=None), result_length, - ir.Constant(1, self._size_type))) result = self.append(ir.Alloc([result_length], node.type)) # Copy lhs @@ -1359,10 +1355,6 @@ class ARTIQIRGenerator(algorithm.Visitor): lambda index: self.append(ir.Compare(ast.Lt(loc=None), index, rhs_length)), body_gen) - if builtins.is_str(node.left.type): - self.append(ir.SetElem(result, result_last, - ir.Constant(0, builtins.TInt(types.TValue(8))))) - return result else: assert False @@ -1564,13 +1556,13 @@ class ARTIQIRGenerator(algorithm.Visitor): # Keep this function with builtins.TException.attributes. def alloc_exn(self, typ, message=None, param0=None, param1=None, param2=None): typ = typ.find() + name = "{}:{}".format(typ.id, typ.name) attributes = [ - ir.Constant("{}:{}".format(typ.id, typ.name), - ir.TExceptionTypeInfo()), # typeinfo - ir.Constant("", builtins.TStr()), # file - ir.Constant(0, builtins.TInt32()), # line - ir.Constant(0, builtins.TInt32()), # column - ir.Constant("", builtins.TStr()), # function + ir.Constant(name, builtins.TStr()), # typeinfo + ir.Constant("", builtins.TStr()), # file + ir.Constant(0, builtins.TInt32()), # line + ir.Constant(0, builtins.TInt32()), # column + ir.Constant("", builtins.TStr()), # function ] if message is None: @@ -1892,7 +1884,7 @@ class ARTIQIRGenerator(algorithm.Visitor): else: explanation = node.loc.source() self.append(ir.Builtin("printf", [ - ir.Constant("assertion failed at %s: %s\n", builtins.TStr()), + ir.Constant("assertion failed at %.*s: %.*s\n\x00", builtins.TStr()), ir.Constant(str(node.loc.begin()), builtins.TStr()), ir.Constant(str(explanation), builtins.TStr()), ], builtins.TNone())) @@ -1905,7 +1897,7 @@ class ARTIQIRGenerator(algorithm.Visitor): subexpr_body = self.current_block = self.add_block("assert.subexpr.body") self.append(ir.Builtin("printf", [ - ir.Constant(" (%s) = ", builtins.TStr()), + ir.Constant(" (%.*s) = \x00", builtins.TStr()), ir.Constant(subexpr_node.loc.source(), builtins.TStr()) ], builtins.TNone())) subexpr_value = self.append(ir.Builtin("unwrap", [subexpr_value_opt], @@ -1937,7 +1929,7 @@ class ARTIQIRGenerator(algorithm.Visitor): def flush(): nonlocal format_string, args if format_string != "": - printf(format_string, *args) + printf(format_string + "\x00", *args) format_string = "" args = [] @@ -1961,7 +1953,7 @@ class ARTIQIRGenerator(algorithm.Visitor): elif builtins.is_none(value.type): format_string += "None" elif builtins.is_bool(value.type): - format_string += "%s" + format_string += "%.*s" args.append(self.append(ir.Select(value, ir.Constant("True", builtins.TStr()), ir.Constant("False", builtins.TStr())))) @@ -1979,9 +1971,9 @@ class ARTIQIRGenerator(algorithm.Visitor): args.append(value) elif builtins.is_str(value.type): if as_repr: - format_string += "\"%s\"" + format_string += "\"%.*s\"" else: - format_string += "%s" + format_string += "%.*s" args.append(value) elif builtins.is_listish(value.type): if builtins.is_list(value.type): @@ -2000,7 +1992,7 @@ class ARTIQIRGenerator(algorithm.Visitor): head = self.current_block if_last = self.current_block = self.add_block("print.comma") - printf(", ") + printf(", \x00") tail = self.current_block = self.add_block("print.tail") if_last.append(ir.Branch(tail)) @@ -2032,7 +2024,7 @@ class ARTIQIRGenerator(algorithm.Visitor): param2 = self.append(ir.GetAttr(value, "__param1__")) param3 = self.append(ir.GetAttr(value, "__param2__")) - format_string += "%s(%s, %lld, %lld, %lld)" + format_string += "%.*s(%.*s, %lld, %lld, %lld)" args += [name, message, param1, param2, param3] else: assert False diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index 706afbadc..b51c9e288 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -20,6 +20,8 @@ lli64 = ll.IntType(64) lldouble = ll.DoubleType() llptr = ll.IntType(8).as_pointer() llptrptr = ll.IntType(8).as_pointer().as_pointer() +llslice = ll.LiteralStructType([llptr, lli32]) +llsliceptr = ll.LiteralStructType([llptr, lli32]).as_pointer() llmetadata = ll.MetaData() @@ -216,8 +218,6 @@ class LLVMIRGenerator: return ll.IntType(builtins.get_int_width(typ)) elif builtins.is_float(typ): return lldouble - elif builtins.is_str(typ) or ir.is_exn_typeinfo(typ): - return llptr elif builtins.is_listish(typ): lleltty = self.llty_of_type(builtins.get_iterable_elt(typ)) return ll.LiteralStructType([lleltty.as_pointer(), lli32]) @@ -229,7 +229,7 @@ class LLVMIRGenerator: elif ir.is_option(typ): return ll.LiteralStructType([lli1, self.llty_of_type(typ.params["value"])]) elif ir.is_keyword(typ): - return ll.LiteralStructType([llptr, self.llty_of_type(typ.params["value"])]) + return ll.LiteralStructType([llslice, self.llty_of_type(typ.params["value"])]) elif ir.is_environment(typ): llty = self.llcontext.get_identified_type("env.{}".format(typ.env_name)) if llty.elements is None: @@ -262,8 +262,7 @@ class LLVMIRGenerator: def llstr_of_str(self, value, name=None, linkage="private", unnamed_addr=True): if isinstance(value, str): - assert "\0" not in value - as_bytes = (value + "\0").encode("utf-8") + as_bytes = value.encode("utf-8") else: as_bytes = value @@ -295,19 +294,14 @@ class LLVMIRGenerator: elif isinstance(const.value, (int, float)): return ll.Constant(llty, const.value) elif isinstance(const.value, (str, bytes)): - if ir.is_exn_typeinfo(const.type): - # Exception typeinfo; should be merged with identical others - name = "__artiq_exn_" + const.value - linkage = "linkonce" - unnamed_addr = False + if isinstance(const.value, str): + value = const.value.encode('utf-8') else: - # Just a string - name = None - linkage = "private" - unnamed_addr = True + value = const.value - return self.llstr_of_str(const.value, name=name, - linkage=linkage, unnamed_addr=unnamed_addr) + llptr = self.llstr_of_str(const.value, linkage="private", unnamed_addr=True) + lllen = ll.Constant(lli32, len(const.value)) + return ll.Constant(llty, (llptr, lllen)) else: assert False @@ -318,8 +312,6 @@ class LLVMIRGenerator: if name in "llvm.donothing": llty = ll.FunctionType(llvoid, []) - elif name in "llvm.trap": - llty = ll.FunctionType(llvoid, []) elif name == "llvm.floor.f64": llty = ll.FunctionType(lldouble, [lldouble]) elif name == "llvm.round.f64": @@ -344,14 +336,14 @@ class LLVMIRGenerator: llty = ll.FunctionType(llvoid, [self.llty_of_type(builtins.TException())]) elif name == "__artiq_reraise": llty = ll.FunctionType(llvoid, []) - elif name == "strlen": - llty = ll.FunctionType(lli32, [llptr]) - elif name == "strcmp": - llty = ll.FunctionType(lli32, [llptr, llptr]) + elif name in "abort": + llty = ll.FunctionType(llvoid, []) + elif name == "memcmp": + llty = ll.FunctionType(lli32, [llptr, llptr, lli32]) elif name == "send_rpc": - llty = ll.FunctionType(llvoid, [lli32, llptr, llptrptr]) + llty = ll.FunctionType(llvoid, [lli32, llsliceptr, llptrptr]) elif name == "send_async_rpc": - llty = ll.FunctionType(llvoid, [lli32, llptr, llptrptr]) + llty = ll.FunctionType(llvoid, [lli32, llsliceptr, llptrptr]) elif name == "recv_rpc": llty = ll.FunctionType(lli32, [llptr]) elif name == "now": @@ -423,7 +415,7 @@ class LLVMIRGenerator: llobjects[obj_typ].append(llobject.bitcast(llptr)) llrpcattrty = self.llcontext.get_identified_type("A") - llrpcattrty.elements = [lli32, llptr, llptr] + llrpcattrty.elements = [lli32, llslice, llslice] lldescty = self.llcontext.get_identified_type("D") lldescty.elements = [llrpcattrty.as_pointer().as_pointer(), llptr.as_pointer()] @@ -445,15 +437,14 @@ class LLVMIRGenerator: if not (types.is_function(typ) or types.is_method(typ) or types.is_rpc(typ) or name == "__objectid__"): - rpctag = b"Os" + self._rpc_tag(typ, error_handler=rpc_tag_error) + b":n\x00" - llrpctag = self.llstr_of_str(rpctag) + rpctag = b"Os" + self._rpc_tag(typ, error_handler=rpc_tag_error) + b":n" else: - llrpctag = ll.Constant(llptr, None) + rpctag = b"" llrpcattrinit = ll.Constant(llrpcattrty, [ ll.Constant(lli32, offset), - llrpctag, - self.llstr_of_str(name) + self.llconst_of_const(ir.Constant(rpctag, builtins.TStr())), + self.llconst_of_const(ir.Constant(name, builtins.TStr())) ]) if name == "__objectid__": @@ -615,10 +606,6 @@ class LLVMIRGenerator: name=insn.name) else: assert False - elif builtins.is_str(insn.type): - llsize = self.map(insn.operands[0]) - llvalue = self.llbuilder.alloca(lli8, size=llsize) - return llvalue elif builtins.is_listish(insn.type): llsize = self.map(insn.operands[0]) lleltty = self.llty_of_type(builtins.get_iterable_elt(insn.type)) @@ -829,13 +816,9 @@ class LLVMIRGenerator: def process_GetElem(self, insn): lst, idx = insn.list(), insn.index() lllst, llidx = map(self.map, (lst, idx)) - if builtins.is_str(lst.type): - llelt = self.llbuilder.gep(lllst, [llidx], inbounds=True) - llvalue = self.llbuilder.load(llelt) - else: - llelts = self.llbuilder.extract_value(lllst, 0) - llelt = self.llbuilder.gep(llelts, [llidx], inbounds=True) - llvalue = self.llbuilder.load(llelt) + llelts = self.llbuilder.extract_value(lllst, 0) + llelt = self.llbuilder.gep(llelts, [llidx], inbounds=True) + llvalue = self.llbuilder.load(llelt) if isinstance(llvalue.type, ll.PointerType): self.mark_dereferenceable(llvalue) return llvalue @@ -843,11 +826,8 @@ class LLVMIRGenerator: def process_SetElem(self, insn): lst, idx = insn.list(), insn.index() lllst, llidx = map(self.map, (lst, idx)) - if builtins.is_str(lst.type): - llelt = self.llbuilder.gep(lllst, [llidx], inbounds=True) - else: - llelts = self.llbuilder.extract_value(lllst, 0) - llelt = self.llbuilder.gep(llelts, [llidx], inbounds=True) + llelts = self.llbuilder.extract_value(lllst, 0) + llelt = self.llbuilder.gep(llelts, [llidx], inbounds=True) return self.llbuilder.store(self.map(insn.value()), llelt) def process_Coerce(self, insn): @@ -1029,7 +1009,7 @@ class LLVMIRGenerator: if insn.op == "nop": return self.llbuilder.call(self.llbuiltin("llvm.donothing"), []) if insn.op == "abort": - return self.llbuilder.call(self.llbuiltin("llvm.trap"), []) + return self.llbuilder.call(self.llbuiltin("abort"), []) elif insn.op == "is_some": lloptarg = self.map(insn.operands[0]) return self.llbuilder.extract_value(lloptarg, 0, @@ -1066,15 +1046,21 @@ class LLVMIRGenerator: return get_outer(self.map(env), env.type) elif insn.op == "len": collection, = insn.operands - if builtins.is_str(collection.type): - return self.llbuilder.call(self.llbuiltin("strlen"), [self.map(collection)]) - else: - return self.llbuilder.extract_value(self.map(collection), 1) + return self.llbuilder.extract_value(self.map(collection), 1) elif insn.op in ("printf", "rtio_log"): # We only get integers, floats, pointers and strings here. - llargs = map(self.map, insn.operands) + lloperands = [] + for i, operand in enumerate(insn.operands): + lloperand = self.map(operand) + if i == 0 and insn.op == "printf" or i == 1 and insn.op == "rtio_log": + lloperands.append(self.llbuilder.extract_value(lloperand, 0)) + elif builtins.is_str(operand.type): + lloperands.append(self.llbuilder.extract_value(lloperand, 1)) + lloperands.append(self.llbuilder.extract_value(lloperand, 0)) + else: + lloperands.append(lloperand) func_name = self.target.print_function if insn.op == "printf" else insn.op - return self.llbuilder.call(self.llbuiltin(func_name), llargs, + return self.llbuilder.call(self.llbuiltin(func_name), lloperands, name=insn.name) elif insn.op == "exncast": # This is an identity cast at LLVM IR level. @@ -1231,9 +1217,10 @@ class LLVMIRGenerator: fun_loc) self.engine.process(diag) tag += self._rpc_tag(fun_type.ret, ret_error_handler) - tag += b"\x00" - lltag = self.llstr_of_str(tag) + lltag = self.llconst_of_const(ir.Constant(tag, builtins.TStr())) + lltagptr = self.llbuilder.alloca(lltag.type) + self.llbuilder.store(lltag, lltagptr) llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), [], name="rpc.stack") @@ -1256,10 +1243,10 @@ class LLVMIRGenerator: if fun_type.async: self.llbuilder.call(self.llbuiltin("send_async_rpc"), - [llservice, lltag, llargs]) + [llservice, lltagptr, llargs]) else: self.llbuilder.call(self.llbuiltin("send_rpc"), - [llservice, lltag, llargs]) + [llservice, lltagptr, llargs]) # Don't waste stack space on saved arguments. self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr]) @@ -1446,19 +1433,16 @@ class LLVMIRGenerator: elif builtins.is_float(typ): assert isinstance(value, float) return ll.Constant(llty, value) - elif builtins.is_str(typ): - assert isinstance(value, (str, bytes)) - return self.llstr_of_str(value) elif builtins.is_listish(typ): - assert isinstance(value, (list, numpy.ndarray)) + assert isinstance(value, (str, bytes, list, numpy.ndarray)) elt_type = builtins.get_iterable_elt(typ) llelts = [self._quote(value[i], elt_type, lambda: path() + [str(i)]) for i in range(len(value))] lleltsary = ll.Constant(ll.ArrayType(self.llty_of_type(elt_type), len(llelts)), list(llelts)) - llglobal = ll.GlobalVariable(self.llmodule, lleltsary.type, - self.llmodule.scope.deduplicate("quoted.list")) + name = self.llmodule.scope.deduplicate("quoted.{}".format(typ.name)) + llglobal = ll.GlobalVariable(self.llmodule, lleltsary.type, name) llglobal.initializer = lleltsary llglobal.linkage = "private" @@ -1556,23 +1540,38 @@ class LLVMIRGenerator: for target, typ in insn.clauses(): if typ is None: - llclauseexnname = ll.Constant( - self.llty_of_type(ir.TExceptionTypeInfo()), None) + llclauseexnnameptr = ll.Constant( + self.llty_of_type(builtins.TStr()).as_pointer(), None) else: + exnname = "{}:{}".format(typ.id, typ.name) llclauseexnname = self.llconst_of_const( - ir.Constant("{}:{}".format(typ.id, typ.name), - ir.TExceptionTypeInfo())) - lllandingpad.add_clause(ll.CatchClause(llclauseexnname)) + ir.Constant(exnname, builtins.TStr())) + + llclauseexnnameptr = self.llmodule.get_global("exn.{}".format(exnname)) + if llclauseexnnameptr is None: + llclauseexnnameptr = ll.GlobalVariable(self.llmodule, llclauseexnname.type, + name="exn.{}".format(exnname)) + llclauseexnnameptr.global_constant = True + llclauseexnnameptr.initializer = llclauseexnname + llclauseexnnameptr.linkage = "private" + llclauseexnnameptr.unnamed_addr = True + lllandingpad.add_clause(ll.CatchClause(llclauseexnnameptr)) if typ is None: self.llbuilder.branch(self.map(target)) else: - llexnmatch = self.llbuilder.call(self.llbuiltin("strcmp"), - [llexnname, llclauseexnname]) - llmatchingclause = self.llbuilder.icmp_unsigned('==', - llexnmatch, ll.Constant(lli32, 0)) - with self.llbuilder.if_then(llmatchingclause): - self.llbuilder.branch(self.map(target)) + llexnlen = self.llbuilder.extract_value(llexnname, 1) + llclauseexnlen = self.llbuilder.extract_value(llclauseexnname, 1) + llmatchinglen = self.llbuilder.icmp_unsigned('==', llexnlen, llclauseexnlen) + with self.llbuilder.if_then(llmatchinglen): + llexnptr = self.llbuilder.extract_value(llexnname, 0) + llclauseexnptr = self.llbuilder.extract_value(llclauseexnname, 0) + llcomparedata = self.llbuilder.call(self.llbuiltin("memcmp"), + [llexnptr, llclauseexnptr, llexnlen]) + llmatchingdata = self.llbuilder.icmp_unsigned('==', llcomparedata, + ll.Constant(lli32, 0)) + with self.llbuilder.if_then(llmatchingdata): + self.llbuilder.branch(self.map(target)) if self.llbuilder.basic_block.terminator is None: self.llbuilder.branch(self.map(insn.cleanup())) diff --git a/artiq/firmware/Cargo.lock b/artiq/firmware/Cargo.lock index 196fef089..683195487 100644 --- a/artiq/firmware/Cargo.lock +++ b/artiq/firmware/Cargo.lock @@ -115,6 +115,7 @@ dependencies = [ "build_artiq 0.0.0", "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "compiler_builtins 0.1.0 (git+https://github.com/rust-lang-nursery/compiler-builtins)", + "cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "fringe 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "logger_artiq 0.0.0", diff --git a/artiq/firmware/ksupport/api.rs b/artiq/firmware/ksupport/api.rs index 43e44e032..ea1093caf 100644 --- a/artiq/firmware/ksupport/api.rs +++ b/artiq/firmware/ksupport/api.rs @@ -1,8 +1,6 @@ -use libc::{c_void, c_char, size_t}; - macro_rules! api { ($i:ident) => ({ - extern { static $i: c_void; } + extern { static $i: u8; } api!($i = &$i as *const _) }); ($i:ident, $d:item) => ({ @@ -68,9 +66,8 @@ static mut API: &'static [(&'static str, *const ())] = &[ api!(__powidf2), /* libc */ - api!(strcmp), - api!(strlen, extern { fn strlen(s: *const c_char) -> size_t; }), api!(abort = ::abort), + api!(memcmp, extern { fn memcmp(a: *const u8, b: *mut u8, size: usize); }), /* libm */ api!(sqrt), diff --git a/artiq/firmware/ksupport/lib.rs b/artiq/firmware/ksupport/lib.rs index 3c2d62333..92959d26b 100644 --- a/artiq/firmware/ksupport/lib.rs +++ b/artiq/firmware/ksupport/lib.rs @@ -30,15 +30,14 @@ extern { macro_rules! artiq_raise { ($name:expr, $message:expr, $param0:expr, $param1:expr, $param2:expr) => ({ let exn = $crate::kernel_proto::Exception { - name: concat!("0:artiq.coredevice.exceptions.", $name, "\0").as_bytes().as_ptr(), - file: concat!(file!(), "\0").as_bytes().as_ptr(), + name: concat!("0:artiq.coredevice.exceptions.", $name), + file: file!(), line: line!(), column: column!(), // https://github.com/rust-lang/rfcs/pull/1719 - function: "(Rust function)\0".as_bytes().as_ptr(), - message: concat!($message, "\0").as_bytes().as_ptr(), - param: [$param0, $param1, $param2], - phantom: ::core::marker::PhantomData + function: "(Rust function)", + message: $message, + param: [$param0, $param1, $param2] }; #[allow(unused_unsafe)] unsafe { $crate::__artiq_raise(&exn as *const _) } @@ -48,9 +47,8 @@ macro_rules! artiq_raise { }); } -use core::{mem, ptr, slice, str}; +use core::{mem, ptr, str}; use std::io::Cursor; -use libc::{c_char, size_t}; use cslice::{CSlice, CMutSlice, AsCSlice}; use kernel_proto::*; use dyld::Library; @@ -125,28 +123,22 @@ extern fn abort() -> ! { loop {} } -extern fn send_rpc(service: u32, tag: *const u8, data: *const *const ()) { - extern { fn strlen(s: *const c_char) -> size_t; } - let tag = unsafe { slice::from_raw_parts(tag, strlen(tag as *const c_char)) }; - +extern fn send_rpc(service: u32, tag: CSlice, data: *const *const ()) { while !rpc_queue::empty() {} send(&RpcSend { async: false, service: service, - tag: tag, + tag: tag.as_ref(), data: data }) } -extern fn send_async_rpc(service: u32, tag: *const u8, data: *const *const ()) { - extern { fn strlen(s: *const c_char) -> size_t; } - let tag = unsafe { slice::from_raw_parts(tag, strlen(tag as *const c_char)) }; - +extern fn send_async_rpc(service: u32, tag: CSlice, data: *const *const ()) { while rpc_queue::full() {} rpc_queue::enqueue(|mut slice| { let length = { let mut writer = Cursor::new(&mut slice[4..]); - rpc_proto::send_args(&mut writer, service, tag, data)?; + rpc_proto::send_args(&mut writer, service, tag.as_ref(), data)?; writer.position() }; proto::write_u32(&mut slice, length as u32) @@ -157,7 +149,7 @@ extern fn send_async_rpc(service: u32, tag: *const u8, data: *const *const ()) { send(&RpcSend { async: true, service: service, - tag: tag, + tag: tag.as_ref(), data: data }) }) @@ -206,21 +198,18 @@ extern fn watchdog_clear(id: i32) { send(&WatchdogClear { id: id as usize }) } -extern fn cache_get(key: *const u8) -> CSlice<'static, i32> { - extern { fn strlen(s: *const c_char) -> size_t; } - let key = unsafe { slice::from_raw_parts(key, strlen(key as *const c_char)) }; - let key = unsafe { str::from_utf8_unchecked(key) }; - - send(&CacheGetRequest { key: key }); +extern fn cache_get(key: CSlice) -> CSlice<'static, i32> { + send(&CacheGetRequest { + key: str::from_utf8(key.as_ref()).unwrap() + }); recv!(&CacheGetReply { value } => value.as_c_slice()) } -extern fn cache_put(key: *const u8, list: CSlice) { - extern { fn strlen(s: *const c_char) -> size_t; } - let key = unsafe { slice::from_raw_parts(key, strlen(key as *const c_char)) }; - let key = unsafe { str::from_utf8_unchecked(key) }; - - send(&CachePutRequest { key: key, value: list.as_ref() }); +extern fn cache_put(key: CSlice, list: CSlice) { + send(&CachePutRequest { + key: str::from_utf8(key.as_ref()).unwrap(), + value: list.as_ref() + }); recv!(&CachePutReply { succeeded } => { if !succeeded { artiq_raise!("CacheError", "cannot put into a busy cache row") @@ -249,8 +238,8 @@ extern fn i2c_read(busno: i32, ack: bool) -> i32 { unsafe fn attribute_writeback(typeinfo: *const ()) { struct Attr { offset: usize, - tag: *const u8, - name: *const u8 + tag: CSlice<'static, u8>, + name: CSlice<'static, u8> } struct Type { @@ -276,7 +265,7 @@ unsafe fn attribute_writeback(typeinfo: *const ()) { let attribute = *attributes; attributes = attributes.offset(1); - if !(*attribute).tag.is_null() { + if (*attribute).tag.len() > 0 { send_async_rpc(0, (*attribute).tag, [ &object as *const _ as *const (), &(*attribute).name as *const _ as *const (), diff --git a/artiq/firmware/liballoc_artiq/lib.rs b/artiq/firmware/liballoc_artiq/lib.rs index 6e4278da4..073062973 100644 --- a/artiq/firmware/liballoc_artiq/lib.rs +++ b/artiq/firmware/liballoc_artiq/lib.rs @@ -1,5 +1,3 @@ -#![allow(dead_code)] // FIXME - #![feature(allocator)] #![no_std] #![allocator] diff --git a/artiq/firmware/libstd_artiq/lib.rs b/artiq/firmware/libstd_artiq/lib.rs index 3a7f66404..9d6d7471d 100644 --- a/artiq/firmware/libstd_artiq/lib.rs +++ b/artiq/firmware/libstd_artiq/lib.rs @@ -1,4 +1,4 @@ -#![feature(lang_items, asm, alloc, collections, libc, needs_panic_runtime, +#![feature(lang_items, asm, alloc, collections, needs_panic_runtime, unicode, raw, int_error_internals, try_from, macro_reexport, allow_internal_unstable, stmt_expr_attributes)] #![no_std] @@ -9,7 +9,6 @@ extern crate alloc; #[macro_use] #[macro_reexport(vec, format)] extern crate collections; -extern crate libc; pub use core::{any, cell, clone, cmp, convert, default, hash, iter, marker, mem, num, ops, option, ptr, result, sync, diff --git a/artiq/firmware/runtime/Cargo.toml b/artiq/firmware/runtime/Cargo.toml index e76b20478..3fe32e4ad 100644 --- a/artiq/firmware/runtime/Cargo.toml +++ b/artiq/firmware/runtime/Cargo.toml @@ -17,6 +17,7 @@ compiler_builtins = { git = "https://github.com/rust-lang-nursery/compiler-built alloc_artiq = { path = "../liballoc_artiq" } std_artiq = { path = "../libstd_artiq", features = ["alloc"] } logger_artiq = { path = "../liblogger_artiq" } +cslice = { version = "0.3" } log = { version = "0.3", default-features = false, features = ["max_level_debug"] } board = { path = "../libboard", features = ["uart_console"] } fringe = { version = "= 1.1.0", default-features = false, features = ["alloc"] } diff --git a/artiq/firmware/runtime/kernel_proto.rs b/artiq/firmware/runtime/kernel_proto.rs index 5b3c11c34..f235c02ab 100644 --- a/artiq/firmware/runtime/kernel_proto.rs +++ b/artiq/firmware/runtime/kernel_proto.rs @@ -1,6 +1,5 @@ #![allow(dead_code)] -use core::marker::PhantomData; use core::fmt; pub const KERNELCPU_EXEC_ADDRESS: usize = 0x40800000; @@ -11,14 +10,13 @@ pub const KSUPPORT_HEADER_SIZE: usize = 0x80; #[repr(C)] #[derive(Debug, Clone)] pub struct Exception<'a> { - pub name: *const u8, - pub file: *const u8, + pub name: &'a str, + pub file: &'a str, pub line: u32, pub column: u32, - pub function: *const u8, - pub message: *const u8, - pub param: [i64; 3], - pub phantom: PhantomData<&'a str> + pub function: &'a str, + pub message: &'a str, + pub param: [i64; 3] } #[derive(Debug)] diff --git a/artiq/firmware/runtime/lib.rs b/artiq/firmware/runtime/lib.rs index 61ee29feb..d5292f9ad 100644 --- a/artiq/firmware/runtime/lib.rs +++ b/artiq/firmware/runtime/lib.rs @@ -6,6 +6,7 @@ extern crate alloc_artiq; #[macro_use] extern crate std_artiq as std; extern crate libc; +extern crate cslice; #[macro_use] extern crate log; extern crate logger_artiq; diff --git a/artiq/firmware/runtime/rpc_proto.rs b/artiq/firmware/runtime/rpc_proto.rs index 493bcb3bc..83c716432 100644 --- a/artiq/firmware/runtime/rpc_proto.rs +++ b/artiq/firmware/runtime/rpc_proto.rs @@ -1,7 +1,8 @@ #![allow(dead_code)] -use core::slice; +use core::str; use std::io::{self, Read, Write}; +use cslice::{CSlice, CMutSlice}; use proto::*; use self::tag::{Tag, TagIterator, split_tag}; @@ -30,11 +31,10 @@ unsafe fn recv_value(reader: &mut Read, tag: Tag, data: &mut *mut (), *ptr = read_u64(reader)?; Ok(()) }), Tag::String => { - consume_value!(*mut u8, |ptr| { - let length = read_u32(reader)?; - // NB: the received string includes a trailing \0 - *ptr = alloc(length as usize)? as *mut u8; - reader.read_exact(slice::from_raw_parts_mut(*ptr, length as usize))?; + consume_value!(CMutSlice, |ptr| { + let length = read_u32(reader)? as usize; + *ptr = CMutSlice::new(alloc(length)? as *mut u8, length); + reader.read_exact((*ptr).as_mut())?; Ok(()) }) } @@ -86,12 +86,6 @@ pub fn recv_return(reader: &mut Read, tag_bytes: &[u8], data: *mut (), Ok(()) } -pub unsafe fn from_c_str<'a>(ptr: *const u8) -> &'a str { - use core::{str, slice}; - extern { fn strlen(ptr: *const u8) -> usize; } - str::from_utf8_unchecked(slice::from_raw_parts(ptr as *const u8, strlen(ptr))) -} - unsafe fn send_value(writer: &mut Write, tag: Tag, data: &mut *const ()) -> io::Result<()> { macro_rules! consume_value { ($ty:ty, |$ptr:ident| $map:expr) => ({ @@ -114,8 +108,8 @@ unsafe fn send_value(writer: &mut Write, tag: Tag, data: &mut *const ()) -> io:: consume_value!(u64, |ptr| write_u64(writer, *ptr)), Tag::String => - consume_value!(*const u8, |ptr| - write_string(writer, from_c_str(*ptr))), + consume_value!(CSlice, |ptr| + write_string(writer, str::from_utf8_unchecked((*ptr).as_ref()))), Tag::Tuple(it, arity) => { let mut it = it.clone(); write_u8(writer, arity)?; @@ -145,9 +139,9 @@ unsafe fn send_value(writer: &mut Write, tag: Tag, data: &mut *const ()) -> io:: Ok(()) } Tag::Keyword(it) => { - struct Keyword { name: *const u8, contents: () }; + struct Keyword<'a> { name: CSlice<'a, u8>, contents: () }; consume_value!(Keyword, |ptr| { - write_string(writer, from_c_str((*ptr).name))?; + write_string(writer, str::from_utf8_unchecked((*ptr).name.as_ref()))?; let tag = it.clone().next().expect("truncated tag"); let mut data = &(*ptr).contents as *const (); send_value(writer, tag, &mut data) diff --git a/artiq/firmware/runtime/session.rs b/artiq/firmware/runtime/session.rs index 682dbae1c..106ed5412 100644 --- a/artiq/firmware/runtime/session.rs +++ b/artiq/firmware/runtime/session.rs @@ -62,8 +62,7 @@ struct Session<'a> { congress: &'a mut Congress, kernel_state: KernelState, watchdog_set: board::clock::WatchdogSet, - log_buffer: String, - interner: BTreeSet + log_buffer: String } impl<'a> Session<'a> { @@ -72,8 +71,7 @@ impl<'a> Session<'a> { congress: congress, kernel_state: KernelState::Absent, watchdog_set: board::clock::WatchdogSet::new(), - log_buffer: String::new(), - interner: BTreeSet::new() + log_buffer: String::new() } } @@ -183,6 +181,7 @@ unsafe fn kern_load(io: &Io, session: &mut Session, library: &[u8]) -> io::Resul Ok(()) } &kern::LoadReply(Err(error)) => { + kernel::stop(); Err(io::Error::new(io::ErrorKind::Other, format!("cannot load kernel: {}", error))) } @@ -332,22 +331,14 @@ fn process_host_message(io: &Io, } })?; - // FIXME: gross. - fn into_c_str(interner: &mut BTreeSet, s: String) -> *const u8 { - let s = s + "\0"; - interner.insert(s.clone()); - let p = interner.get(&s).unwrap().as_bytes().as_ptr(); - p - } let exn = kern::Exception { - name: into_c_str(&mut session.interner, name), - message: into_c_str(&mut session.interner, message), - param: param, - file: into_c_str(&mut session.interner, file), - line: line, - column: column, - function: into_c_str(&mut session.interner, function), - phantom: ::core::marker::PhantomData + name: name.as_ref(), + message: message.as_ref(), + param: param, + file: file.as_ref(), + line: line, + column: column, + function: function.as_ref() }; kern_send(io, &kern::RpcRecvReply(Err(exn)))?; @@ -513,40 +504,33 @@ fn process_kern_message(io: &Io, } } - &kern::RunException { exception: ref exn, backtrace } => { + &kern::RunException { + exception: kern::Exception { name, message, param, file, line, column, function }, + backtrace + } => { unsafe { kernel::stop() } session.kernel_state = KernelState::Absent; unsafe { session.congress.cache.unborrow() } - unsafe fn from_c_str<'a>(s: *const u8) -> &'a str { - use ::libc::{c_char, size_t}; - use core::slice; - extern { fn strlen(s: *const c_char) -> size_t; } - let s = slice::from_raw_parts(s, strlen(s as *const c_char)); - str::from_utf8_unchecked(s) - } - let name = unsafe { from_c_str(exn.name) }; - let message = unsafe { from_c_str(exn.message) }; - let file = unsafe { from_c_str(exn.file) }; - let function = unsafe { from_c_str(exn.function) }; match stream { None => { error!("exception in flash kernel"); - error!("{}: {} {:?}", name, message, exn.param); - error!("at {}:{}:{} in {}", file, exn.line, exn.column, function); + error!("{}: {} {:?}", name, message, param); + error!("at {}:{}:{} in {}", file, line, column, function); return Ok(true) }, - Some(ref mut stream) => + Some(ref mut stream) => { host_write(stream, host::Reply::KernelException { - name: name, - message: message, - param: exn.param, - file: file, - line: exn.line, - column: exn.column, - function: function, + name: name, + message: message, + param: param, + file: file, + line: line, + column: column, + function: function, backtrace: backtrace }) + } } } diff --git a/artiq/runtime/artiq_personality.c b/artiq/runtime/artiq_personality.c index 48dcf900d..510e3f9a1 100644 --- a/artiq/runtime/artiq_personality.c +++ b/artiq/runtime/artiq_personality.c @@ -230,18 +230,19 @@ static _Unwind_Reason_Code __artiq_uncaught_exception( void *stop_parameter); struct artiq_raised_exception { - struct _Unwind_Exception unwind; - struct artiq_exception artiq; - int handled; + struct _Unwind_Exception unwind; + struct artiq_exception artiq; + int handled; uintptr_t backtrace[1024]; - size_t backtrace_size; + size_t backtrace_size; }; static struct artiq_raised_exception inflight; void __artiq_raise(struct artiq_exception *artiq_exn) { - EH_LOG("===> raise (name=%s, msg=%s, params=[%lld,%lld,%lld])", - artiq_exn->name, artiq_exn->message, + EH_LOG("===> raise (name=%.*s, msg=%.*s, params=[%lld,%lld,%lld])", + (int)artiq_exn->name.len, (const char*)artiq_exn->name.ptr, + (int)artiq_exn->name.len, (const char*)artiq_exn->message.ptr, (long long int)artiq_exn->param[0], (long long int)artiq_exn->param[1], (long long int)artiq_exn->param[2]); @@ -269,8 +270,6 @@ void __artiq_reraise() { __artiq_raise(&inflight.artiq); } else { EH_LOG0("===> resume"); - EH_ASSERT((inflight.artiq.typeinfo != 0) && - "Need an exception to reraise"); _Unwind_Resume(&inflight.unwind); abort(); } @@ -333,8 +332,8 @@ _Unwind_Reason_Code __artiq_personality( struct artiq_raised_exception *inflight = (struct artiq_raised_exception*)exceptionObject; - EH_LOG("=> exception name=%s", - inflight->artiq.name); + EH_LOG("=> exception name=%.*s", + (int)inflight->artiq.name.len, (const char*)inflight->artiq.name.ptr); // Get a pointer to LSDA. If there's no LSDA, this function doesn't // actually handle any exceptions. @@ -418,13 +417,17 @@ _Unwind_Reason_Code __artiq_personality( if(typeInfoOffset > 0) { unsigned encodingSize = getEncodingSize(ttypeEncoding); const uint8_t *typeInfoPtrPtr = classInfo - typeInfoOffset * encodingSize; - uintptr_t typeInfoPtr = readEncodedPointer(&typeInfoPtrPtr, ttypeEncoding); + const struct slice *typeInfoPtr = + (const struct slice *)readEncodedPointer(&typeInfoPtrPtr, ttypeEncoding); EH_LOG("encodingSize=%u typeInfoPtrPtr=%p typeInfoPtr=%p", encodingSize, typeInfoPtrPtr, (void*)typeInfoPtr); - EH_LOG("typeInfo=%s", (char*)typeInfoPtr); + if(typeInfoPtr != NULL) { + EH_LOG("typeInfo=%.*s", (int)typeInfoPtr->len, (const char *)typeInfoPtr->ptr); + } - if(typeInfoPtr == 0 || !strcmp((char*)inflight->artiq.typeinfo, - (char*)typeInfoPtr)) { + if(typeInfoPtr == NULL || + (inflight->artiq.name.len == typeInfoPtr->len && + !memcmp(inflight->artiq.name.ptr, typeInfoPtr->ptr, typeInfoPtr->len))) { EH_LOG0("matching action found"); exceptionMatched = 1; break; diff --git a/artiq/runtime/artiq_personality.h b/artiq/runtime/artiq_personality.h index 6f75ae2d8..4848e47c0 100644 --- a/artiq/runtime/artiq_personality.h +++ b/artiq/runtime/artiq_personality.h @@ -10,16 +10,13 @@ struct slice { }; struct artiq_exception { - union { - uintptr_t typeinfo; - const char *name; - }; - const char *file; - int32_t line; - int32_t column; - const char *function; - const char *message; - int64_t param[3]; + struct slice name; + struct slice file; + int32_t line; + int32_t column; + struct slice function; + struct slice message; + int64_t param[3]; }; #ifdef __cplusplus @@ -32,20 +29,6 @@ void __artiq_raise(struct artiq_exception *artiq_exn) void __artiq_reraise(void) __attribute__((noreturn)); -#define artiq_raise_from_c(exnname, exnmsg, exnparam0, exnparam1, exnparam2) \ - do { \ - struct artiq_exception exn = { \ - .name = "0:artiq.coredevice.exceptions." exnname, \ - .message = exnmsg, \ - .param = { exnparam0, exnparam1, exnparam2 }, \ - .file = __FILE__, \ - .line = __LINE__, \ - .column = -1, \ - .function = __func__, \ - }; \ - __artiq_raise(&exn); \ - } while(0) - /* Called by the runtime */ void __artiq_terminate(struct artiq_exception *artiq_exn, struct slice backtrace) diff --git a/artiq/test/libartiq_support/artiq_terminate.c b/artiq/test/libartiq_support/artiq_terminate.c index 36772f535..455566a42 100644 --- a/artiq/test/libartiq_support/artiq_terminate.c +++ b/artiq/test/libartiq_support/artiq_terminate.c @@ -9,11 +9,14 @@ void __artiq_terminate(struct artiq_exception *exn, struct slice backtrace) { - printf("Uncaught %s: %s (%"PRIi64", %"PRIi64", %"PRIi64")\n" - "at %s:%"PRIi32":%"PRIi32"\n", - exn->name, exn->message, + printf("Uncaught %.*s: %.*s (%"PRIi64", %"PRIi64", %"PRIi64")\n" + "at %.*s:%"PRIi32":%"PRIi32"\n", + (int)exn->name.len, (const char *)exn->name.ptr, + (int)exn->message.len, (const char *)exn->message.ptr, exn->param[0], exn->param[1], exn->param[1], - exn->file, exn->line, exn->column + 1); + (int)exn->file.len, (const char *)exn->file.ptr, + exn->line, + exn->column + 1); for(size_t i = 0; i < backtrace.len; i++) { printf("at %"PRIxPTR"\n", ((uintptr_t*)backtrace.ptr)[i]); diff --git a/artiq/test/lit/integration/str.py b/artiq/test/lit/integration/str.py index ab4e7866d..45b430d2a 100644 --- a/artiq/test/lit/integration/str.py +++ b/artiq/test/lit/integration/str.py @@ -1,4 +1,5 @@ # RUN: %python -m artiq.compiler.testbench.jit %s # RUN: %python %s +assert "xy" == "xy" assert ("x" + "y") == "xy"