From 4644e105b169aea58e37b9eaa7ebd252dfc9b3f0 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Sun, 23 Jan 2022 21:10:46 +0800 Subject: [PATCH] compiler: modified exception representation Exception name is replaced by exception ID, which requires no allocation. Other strings in the exception can now be 'host-only' strings, which is represented by a CSlice with len = usize::MAX and ptr = key, to avoid the need for allocation when raising exceptions through RPC. --- artiq/compiler/builtins.py | 23 +++++++------ artiq/compiler/embedding.py | 24 ++++++++++++++ artiq/compiler/module.py | 3 +- .../compiler/transforms/artiq_ir_generator.py | 32 ++++++++++--------- artiq/coredevice/comm_kernel.py | 26 ++++++++------- 5 files changed, 72 insertions(+), 36 deletions(-) diff --git a/artiq/compiler/builtins.py b/artiq/compiler/builtins.py index 54b25e719..4524d87dd 100644 --- a/artiq/compiler/builtins.py +++ b/artiq/compiler/builtins.py @@ -123,18 +123,23 @@ class TException(types.TMono): # * File, line and column where it was raised (str, int, int). # * Message, which can contain substitutions {0}, {1} and {2} (str). # * Three 64-bit integers, parameterizing the message (numpy.int64). + # These attributes are prefixed with `#` so that users cannot access them, + # and we don't have to do string allocation in the runtime. + # #__name__ is now a string key in the host. TStr may not be an actual + # CSlice in the runtime, they might be a CSlice with length = i32::MAX and + # ptr = string key in the host. # Keep this in sync with the function ARTIQIRGenerator.alloc_exn. attributes = OrderedDict([ - ("__name__", TStr()), - ("__file__", TStr()), - ("__line__", TInt32()), - ("__col__", TInt32()), - ("__func__", TStr()), - ("__message__", TStr()), - ("__param0__", TInt64()), - ("__param1__", TInt64()), - ("__param2__", TInt64()), + ("#__name__", TInt32()), + ("#__file__", TStr()), + ("#__line__", TInt32()), + ("#__col__", TInt32()), + ("#__func__", TStr()), + ("#__message__", TStr()), + ("#__param0__", TInt64()), + ("#__param1__", TInt64()), + ("#__param2__", TInt64()), ]) def __init__(self, name="Exception", id=0): diff --git a/artiq/compiler/embedding.py b/artiq/compiler/embedding.py index 89866c7c8..2a6ce62c5 100644 --- a/artiq/compiler/embedding.py +++ b/artiq/compiler/embedding.py @@ -47,6 +47,24 @@ class EmbeddingMap: self.module_map = {} self.type_map = {} self.function_map = {} + self.str_forward_map = {} + self.str_reverse_map = {} + + def preallocate_runtime_exception_names(self, names): + for i, name in enumerate(names): + exn_id = self.store_str("0:artiq.coredevice.exceptions." + name) + assert exn_id == i + + def store_str(self, s): + if s in self.str_forward_map: + return self.str_forward_map[s] + str_id = len(self.str_forward_map) + self.str_forward_map[s] = str_id + self.str_reverse_map[str_id] = s + return str_id + + def retrieve_str(self, str_id): + return self.str_reverse_map[str_id] # Modules def store_module(self, module, module_type): @@ -736,6 +754,12 @@ class Stitcher: self.functions = {} self.embedding_map = EmbeddingMap() + self.embedding_map.preallocate_runtime_exception_names(["runtimeerror", + "RTIOUnderflow", + "RTIOOverflow", + "RTIODestinationUnreachable", + "DMAError", + "I2CError"]) self.value_map = defaultdict(lambda: []) self.definitely_changed = False diff --git a/artiq/compiler/module.py b/artiq/compiler/module.py index d43404b20..7f027ba2f 100644 --- a/artiq/compiler/module.py +++ b/artiq/compiler/module.py @@ -57,7 +57,8 @@ class Module: constness_validator = validators.ConstnessValidator(engine=self.engine) artiq_ir_generator = transforms.ARTIQIRGenerator(engine=self.engine, module_name=src.name, - ref_period=ref_period) + ref_period=ref_period, + embedding_map=self.embedding_map) dead_code_eliminator = transforms.DeadCodeEliminator(engine=self.engine) local_access_validator = validators.LocalAccessValidator(engine=self.engine) local_demoter = transforms.LocalDemoter() diff --git a/artiq/compiler/transforms/artiq_ir_generator.py b/artiq/compiler/transforms/artiq_ir_generator.py index 069422d1b..7b6a1a985 100644 --- a/artiq/compiler/transforms/artiq_ir_generator.py +++ b/artiq/compiler/transforms/artiq_ir_generator.py @@ -88,8 +88,9 @@ class ARTIQIRGenerator(algorithm.Visitor): _size_type = builtins.TInt32() - def __init__(self, module_name, engine, ref_period): + def __init__(self, module_name, engine, ref_period, embedding_map): self.engine = engine + self.embedding_map = embedding_map self.functions = [] self.name = [module_name] if module_name != "" else [] self.ref_period = ir.Constant(ref_period, builtins.TFloat()) @@ -638,10 +639,10 @@ class ARTIQIRGenerator(algorithm.Visitor): loc_column = ir.Constant(loc.column(), builtins.TInt32()) loc_function = ir.Constant(".".join(self.name), builtins.TStr()) - self.append(ir.SetAttr(exn, "__file__", loc_file)) - self.append(ir.SetAttr(exn, "__line__", loc_line)) - self.append(ir.SetAttr(exn, "__col__", loc_column)) - self.append(ir.SetAttr(exn, "__func__", loc_function)) + self.append(ir.SetAttr(exn, "#__file__", loc_file)) + self.append(ir.SetAttr(exn, "#__line__", loc_line)) + self.append(ir.SetAttr(exn, "#__col__", loc_column)) + self.append(ir.SetAttr(exn, "#__func__", loc_function)) if self.unwind_target is not None: self.append(ir.Raise(exn, self.unwind_target)) @@ -2105,8 +2106,9 @@ class ARTIQIRGenerator(algorithm.Visitor): def alloc_exn(self, typ, message=None, param0=None, param1=None, param2=None): typ = typ.find() name = "{}:{}".format(typ.id, typ.name) + name_id = self.embedding_map.store_str(name) attributes = [ - ir.Constant(name, builtins.TStr()), # typeinfo + ir.Constant(name_id, builtins.TInt32()), # typeinfo ir.Constant("", builtins.TStr()), # file ir.Constant(0, builtins.TInt32()), # line ir.Constant(0, builtins.TInt32()), # column @@ -2578,10 +2580,10 @@ class ARTIQIRGenerator(algorithm.Visitor): old_unwind, self.unwind_target = self.unwind_target, None exn = self.alloc_exn(builtins.TException("AssertionError"), message=msg) - self.append(ir.SetAttr(exn, "__file__", file)) - self.append(ir.SetAttr(exn, "__line__", line)) - self.append(ir.SetAttr(exn, "__col__", col)) - self.append(ir.SetAttr(exn, "__func__", function)) + self.append(ir.SetAttr(exn, "#__file__", file)) + self.append(ir.SetAttr(exn, "#__line__", line)) + self.append(ir.SetAttr(exn, "#__col__", col)) + self.append(ir.SetAttr(exn, "#__func__", function)) self.append(ir.Raise(exn)) finally: self.current_function = old_func @@ -2717,11 +2719,11 @@ class ARTIQIRGenerator(algorithm.Visitor): format_string += ")" elif builtins.is_exception(value.type): - name = self.append(ir.GetAttr(value, "__name__")) - message = self.append(ir.GetAttr(value, "__message__")) - param1 = self.append(ir.GetAttr(value, "__param0__")) - param2 = self.append(ir.GetAttr(value, "__param1__")) - param3 = self.append(ir.GetAttr(value, "__param2__")) + name = self.append(ir.GetAttr(value, "#__name__")) + message = self.append(ir.GetAttr(value, "#__message__")) + param1 = self.append(ir.GetAttr(value, "#__param0__")) + param2 = self.append(ir.GetAttr(value, "#__param1__")) + param3 = self.append(ir.GetAttr(value, "#__param2__")) format_string += "%.*s(%.*s, %lld, %lld, %lld)" args += [name, message, param1, param2, param3] diff --git a/artiq/coredevice/comm_kernel.py b/artiq/coredevice/comm_kernel.py index 37ffd2aeb..cbe1ac6db 100644 --- a/artiq/coredevice/comm_kernel.py +++ b/artiq/coredevice/comm_kernel.py @@ -571,29 +571,33 @@ class CommKernel: self._write_header(Request.RPCException) + # Note: instead of sending strings, we send object ID + # This is to avoid the need of allocatio on the device side + # This is a special case: this only applies to exceptions if hasattr(exn, "artiq_core_exception"): exn = exn.artiq_core_exception - self._write_string(exn.name) - self._write_string(self._truncate_message(exn.message)) + self._write_int32(embedding_map.store_str(exn.name)) + self._write_int32(embedding_map.store_str(self._truncate_message(exn.message))) for index in range(3): self._write_int64(exn.param[index]) filename, line, column, function = exn.traceback[-1] - self._write_string(filename) + self._write_int32(embedding_map.store_str(filename)) self._write_int32(line) self._write_int32(column) - self._write_string(function) + self._write_int32(embedding_map.store_str(function)) else: exn_type = type(exn) if exn_type in (ZeroDivisionError, ValueError, IndexError, RuntimeError) or \ hasattr(exn, "artiq_builtin"): - self._write_string("0:{}".format(exn_type.__name__)) + name = "0:{}".format(exn_type.__name__) else: exn_id = embedding_map.store_object(exn_type) - self._write_string("{}:{}.{}".format(exn_id, - exn_type.__module__, - exn_type.__qualname__)) - self._write_string(self._truncate_message(str(exn))) + name = "{}:{}.{}".format(exn_id, + exn_type.__module__, + exn_type.__qualname__) + self._write_int32(embedding_map.store_str(name)) + self._write_int32(embedding_map.store_str(self._truncate_message(str(exn)))) for index in range(3): self._write_int64(0) @@ -604,10 +608,10 @@ class CommKernel: ((filename, line, function, _), ) = tb else: assert False - self._write_string(filename) + self._write_int32(embedding_map.store_str(filename)) self._write_int32(line) self._write_int32(-1) # column not known - self._write_string(function) + self._write_int32(embedding_map.store_str(function)) self._flush() else: logger.debug("rpc service: %d %r %r = %r",