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.
This commit is contained in:
pca006132 2022-01-23 21:10:46 +08:00 committed by Sébastien Bourdeauducq
parent 715bff3ebf
commit 4644e105b1
5 changed files with 72 additions and 36 deletions

View File

@ -123,18 +123,23 @@ class TException(types.TMono):
# * File, line and column where it was raised (str, int, int). # * File, line and column where it was raised (str, int, int).
# * Message, which can contain substitutions {0}, {1} and {2} (str). # * Message, which can contain substitutions {0}, {1} and {2} (str).
# * Three 64-bit integers, parameterizing the message (numpy.int64). # * 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. # Keep this in sync with the function ARTIQIRGenerator.alloc_exn.
attributes = OrderedDict([ attributes = OrderedDict([
("__name__", TStr()), ("#__name__", TInt32()),
("__file__", TStr()), ("#__file__", TStr()),
("__line__", TInt32()), ("#__line__", TInt32()),
("__col__", TInt32()), ("#__col__", TInt32()),
("__func__", TStr()), ("#__func__", TStr()),
("__message__", TStr()), ("#__message__", TStr()),
("__param0__", TInt64()), ("#__param0__", TInt64()),
("__param1__", TInt64()), ("#__param1__", TInt64()),
("__param2__", TInt64()), ("#__param2__", TInt64()),
]) ])
def __init__(self, name="Exception", id=0): def __init__(self, name="Exception", id=0):

View File

@ -47,6 +47,24 @@ class EmbeddingMap:
self.module_map = {} self.module_map = {}
self.type_map = {} self.type_map = {}
self.function_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 # Modules
def store_module(self, module, module_type): def store_module(self, module, module_type):
@ -736,6 +754,12 @@ class Stitcher:
self.functions = {} self.functions = {}
self.embedding_map = EmbeddingMap() self.embedding_map = EmbeddingMap()
self.embedding_map.preallocate_runtime_exception_names(["runtimeerror",
"RTIOUnderflow",
"RTIOOverflow",
"RTIODestinationUnreachable",
"DMAError",
"I2CError"])
self.value_map = defaultdict(lambda: []) self.value_map = defaultdict(lambda: [])
self.definitely_changed = False self.definitely_changed = False

View File

@ -57,7 +57,8 @@ class Module:
constness_validator = validators.ConstnessValidator(engine=self.engine) constness_validator = validators.ConstnessValidator(engine=self.engine)
artiq_ir_generator = transforms.ARTIQIRGenerator(engine=self.engine, artiq_ir_generator = transforms.ARTIQIRGenerator(engine=self.engine,
module_name=src.name, 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) dead_code_eliminator = transforms.DeadCodeEliminator(engine=self.engine)
local_access_validator = validators.LocalAccessValidator(engine=self.engine) local_access_validator = validators.LocalAccessValidator(engine=self.engine)
local_demoter = transforms.LocalDemoter() local_demoter = transforms.LocalDemoter()

View File

@ -88,8 +88,9 @@ class ARTIQIRGenerator(algorithm.Visitor):
_size_type = builtins.TInt32() _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.engine = engine
self.embedding_map = embedding_map
self.functions = [] self.functions = []
self.name = [module_name] if module_name != "" else [] self.name = [module_name] if module_name != "" else []
self.ref_period = ir.Constant(ref_period, builtins.TFloat()) 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_column = ir.Constant(loc.column(), builtins.TInt32())
loc_function = ir.Constant(".".join(self.name), builtins.TStr()) loc_function = ir.Constant(".".join(self.name), builtins.TStr())
self.append(ir.SetAttr(exn, "__file__", loc_file)) self.append(ir.SetAttr(exn, "#__file__", loc_file))
self.append(ir.SetAttr(exn, "__line__", loc_line)) self.append(ir.SetAttr(exn, "#__line__", loc_line))
self.append(ir.SetAttr(exn, "__col__", loc_column)) self.append(ir.SetAttr(exn, "#__col__", loc_column))
self.append(ir.SetAttr(exn, "__func__", loc_function)) self.append(ir.SetAttr(exn, "#__func__", loc_function))
if self.unwind_target is not None: if self.unwind_target is not None:
self.append(ir.Raise(exn, self.unwind_target)) 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): def alloc_exn(self, typ, message=None, param0=None, param1=None, param2=None):
typ = typ.find() typ = typ.find()
name = "{}:{}".format(typ.id, typ.name) name = "{}:{}".format(typ.id, typ.name)
name_id = self.embedding_map.store_str(name)
attributes = [ attributes = [
ir.Constant(name, builtins.TStr()), # typeinfo ir.Constant(name_id, builtins.TInt32()), # typeinfo
ir.Constant("<not thrown>", builtins.TStr()), # file ir.Constant("<not thrown>", builtins.TStr()), # file
ir.Constant(0, builtins.TInt32()), # line ir.Constant(0, builtins.TInt32()), # line
ir.Constant(0, builtins.TInt32()), # column ir.Constant(0, builtins.TInt32()), # column
@ -2578,10 +2580,10 @@ class ARTIQIRGenerator(algorithm.Visitor):
old_unwind, self.unwind_target = self.unwind_target, None old_unwind, self.unwind_target = self.unwind_target, None
exn = self.alloc_exn(builtins.TException("AssertionError"), message=msg) exn = self.alloc_exn(builtins.TException("AssertionError"), message=msg)
self.append(ir.SetAttr(exn, "__file__", file)) self.append(ir.SetAttr(exn, "#__file__", file))
self.append(ir.SetAttr(exn, "__line__", line)) self.append(ir.SetAttr(exn, "#__line__", line))
self.append(ir.SetAttr(exn, "__col__", col)) self.append(ir.SetAttr(exn, "#__col__", col))
self.append(ir.SetAttr(exn, "__func__", function)) self.append(ir.SetAttr(exn, "#__func__", function))
self.append(ir.Raise(exn)) self.append(ir.Raise(exn))
finally: finally:
self.current_function = old_func self.current_function = old_func
@ -2717,11 +2719,11 @@ class ARTIQIRGenerator(algorithm.Visitor):
format_string += ")" format_string += ")"
elif builtins.is_exception(value.type): elif builtins.is_exception(value.type):
name = self.append(ir.GetAttr(value, "__name__")) name = self.append(ir.GetAttr(value, "#__name__"))
message = self.append(ir.GetAttr(value, "__message__")) message = self.append(ir.GetAttr(value, "#__message__"))
param1 = self.append(ir.GetAttr(value, "__param0__")) param1 = self.append(ir.GetAttr(value, "#__param0__"))
param2 = self.append(ir.GetAttr(value, "__param1__")) param2 = self.append(ir.GetAttr(value, "#__param1__"))
param3 = self.append(ir.GetAttr(value, "__param2__")) 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] args += [name, message, param1, param2, param3]

View File

@ -571,29 +571,33 @@ class CommKernel:
self._write_header(Request.RPCException) 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"): if hasattr(exn, "artiq_core_exception"):
exn = exn.artiq_core_exception exn = exn.artiq_core_exception
self._write_string(exn.name) self._write_int32(embedding_map.store_str(exn.name))
self._write_string(self._truncate_message(exn.message)) self._write_int32(embedding_map.store_str(self._truncate_message(exn.message)))
for index in range(3): for index in range(3):
self._write_int64(exn.param[index]) self._write_int64(exn.param[index])
filename, line, column, function = exn.traceback[-1] 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(line)
self._write_int32(column) self._write_int32(column)
self._write_string(function) self._write_int32(embedding_map.store_str(function))
else: else:
exn_type = type(exn) exn_type = type(exn)
if exn_type in (ZeroDivisionError, ValueError, IndexError, RuntimeError) or \ if exn_type in (ZeroDivisionError, ValueError, IndexError, RuntimeError) or \
hasattr(exn, "artiq_builtin"): hasattr(exn, "artiq_builtin"):
self._write_string("0:{}".format(exn_type.__name__)) name = "0:{}".format(exn_type.__name__)
else: else:
exn_id = embedding_map.store_object(exn_type) exn_id = embedding_map.store_object(exn_type)
self._write_string("{}:{}.{}".format(exn_id, name = "{}:{}.{}".format(exn_id,
exn_type.__module__, exn_type.__module__,
exn_type.__qualname__)) exn_type.__qualname__)
self._write_string(self._truncate_message(str(exn))) 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): for index in range(3):
self._write_int64(0) self._write_int64(0)
@ -604,10 +608,10 @@ class CommKernel:
((filename, line, function, _), ) = tb ((filename, line, function, _), ) = tb
else: else:
assert False assert False
self._write_string(filename) self._write_int32(embedding_map.store_str(filename))
self._write_int32(line) self._write_int32(line)
self._write_int32(-1) # column not known self._write_int32(-1) # column not known
self._write_string(function) self._write_int32(embedding_map.store_str(function))
self._flush() self._flush()
else: else:
logger.debug("rpc service: %d %r %r = %r", logger.debug("rpc service: %d %r %r = %r",