mirror of
https://github.com/m-labs/artiq.git
synced 2024-12-28 20:53:35 +08:00
Implement code generation for exception handling.
This commit is contained in:
parent
c581af29d7
commit
ece52062f2
@ -72,6 +72,26 @@ class TRange(types.TMono):
|
||||
])
|
||||
|
||||
class TException(types.TMono):
|
||||
# All exceptions share the same internal layout:
|
||||
# * Pointer to the unique global with the name of the exception (str)
|
||||
# (which also serves as the EHABI type_info).
|
||||
# * 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 (int(width=64)).
|
||||
|
||||
|
||||
# Keep this in sync with the function ARTIQIRGenerator.alloc_exn.
|
||||
attributes = OrderedDict([
|
||||
("__name__", TStr()),
|
||||
("__file__", TStr()),
|
||||
("__line__", TInt(types.TValue(32))),
|
||||
("__col__", TInt(types.TValue(32))),
|
||||
("__message__", TStr()),
|
||||
("__param0__", TInt(types.TValue(64))),
|
||||
("__param1__", TInt(types.TValue(64))),
|
||||
("__param2__", TInt(types.TValue(64))),
|
||||
])
|
||||
|
||||
def __init__(self, name="Exception"):
|
||||
super().__init__(name)
|
||||
|
||||
@ -170,8 +190,12 @@ def is_range(typ, elt=None):
|
||||
else:
|
||||
return types.is_mono(typ, "range")
|
||||
|
||||
def is_exception(typ):
|
||||
return isinstance(typ.find(), TException)
|
||||
def is_exception(typ, name=None):
|
||||
if name is None:
|
||||
return isinstance(typ.find(), TException)
|
||||
else:
|
||||
return isinstance(typ.find(), TException) and \
|
||||
typ.name == name
|
||||
|
||||
def is_iterable(typ):
|
||||
typ = typ.find()
|
||||
@ -189,4 +213,5 @@ def is_collection(typ):
|
||||
|
||||
def is_allocated(typ):
|
||||
return typ.fold(False, lambda accum, typ:
|
||||
is_list(typ) or is_str(typ) or types.is_function(typ))
|
||||
is_list(typ) or is_str(typ) or types.is_function(typ) or
|
||||
is_exception(typ))
|
||||
|
@ -29,6 +29,13 @@ class TOption(types.TMono):
|
||||
def is_option(typ):
|
||||
return isinstance(typ, TOption)
|
||||
|
||||
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.
|
||||
@ -620,7 +627,7 @@ class SetAttr(Instruction):
|
||||
assert value.type == obj.type.elts[attr]
|
||||
else:
|
||||
assert value.type == obj.type.attributes[attr]
|
||||
super().__init__([obj, value], typ, name)
|
||||
super().__init__([obj, value], builtins.TNone(), name)
|
||||
self.attr = attr
|
||||
|
||||
def opcode(self):
|
||||
@ -1004,7 +1011,7 @@ class Invoke(Terminator):
|
||||
def opcode(self):
|
||||
return "invoke"
|
||||
|
||||
def function(self):
|
||||
def target_function(self):
|
||||
return self.operands[0]
|
||||
|
||||
def arguments(self):
|
||||
@ -1038,11 +1045,15 @@ class LandingPad(Terminator):
|
||||
super().__init__([], builtins.TException(), name)
|
||||
self.types = []
|
||||
|
||||
def clauses(self):
|
||||
return zip(self.operands, self.types)
|
||||
|
||||
def add_clause(self, target, typ):
|
||||
assert isinstance(target, BasicBlock)
|
||||
assert builtins.is_exception(typ)
|
||||
assert typ is None or builtins.is_exception(typ)
|
||||
self.operands.append(target)
|
||||
self.types.append(typ.find())
|
||||
self.types.append(typ.find() if typ is not None else None)
|
||||
target.uses.add(self)
|
||||
|
||||
def opcode(self):
|
||||
return "landingpad"
|
||||
@ -1050,5 +1061,8 @@ class LandingPad(Terminator):
|
||||
def _operands_as_string(self):
|
||||
table = []
|
||||
for typ, target in zip(self.types, self.operands):
|
||||
table.append("{} => {}".format(types.TypePrinter().name(typ), target.as_operand()))
|
||||
if typ is None:
|
||||
table.append("... => {}".format(target.as_operand()))
|
||||
else:
|
||||
table.append("{} => {}".format(types.TypePrinter().name(typ), target.as_operand()))
|
||||
return "[{}]".format(", ".join(table))
|
||||
|
@ -33,7 +33,7 @@ class Module:
|
||||
escape_validator.visit(self.typedtree)
|
||||
self.artiq_ir = artiq_ir_generator.visit(self.typedtree)
|
||||
dead_code_eliminator.process(self.artiq_ir)
|
||||
local_access_validator.process(self.artiq_ir)
|
||||
# local_access_validator.process(self.artiq_ir)
|
||||
self.llvm_ir = llvm_ir_generator.process(self.artiq_ir)
|
||||
|
||||
@classmethod
|
||||
|
@ -465,8 +465,17 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
def visit_Continue(self, node):
|
||||
self.append(ir.Branch(self.continue_target))
|
||||
|
||||
def raise_exn(self, exn):
|
||||
loc_file = ir.Constant(self.current_loc.source_buffer.name, builtins.TStr())
|
||||
loc_line = ir.Constant(self.current_loc.line(), builtins.TInt(types.TValue(32)))
|
||||
loc_column = ir.Constant(self.current_loc.column(), builtins.TInt(types.TValue(32)))
|
||||
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.Raise(exn))
|
||||
|
||||
def visit_Raise(self, node):
|
||||
self.append(ir.Raise(self.visit(node.exc)))
|
||||
self.raise_exn(self.visit(node.exc))
|
||||
|
||||
def visit_Try(self, node):
|
||||
dispatcher = self.add_block("try.dispatch")
|
||||
@ -521,15 +530,25 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
self.return_target = old_return
|
||||
|
||||
handlers = []
|
||||
has_catchall = False
|
||||
for handler_node in node.handlers:
|
||||
handler = self.add_block("handler." + handler_node.name_type.find().name)
|
||||
exn_type = handler_node.name_type.find()
|
||||
if handler_node.filter is not None and \
|
||||
not builtins.is_exception(exn_type, 'Exception'):
|
||||
handler = self.add_block("handler." + exn_type.name)
|
||||
landingpad.add_clause(handler, exn_type)
|
||||
else:
|
||||
handler = self.add_block("handler.catchall")
|
||||
landingpad.add_clause(handler, None)
|
||||
has_catchall = True
|
||||
|
||||
self.current_block = handler
|
||||
handlers.append(handler)
|
||||
landingpad.add_clause(handler, handler_node.name_type)
|
||||
|
||||
if handler_node.name is not None:
|
||||
exn = self.append(ir.Builtin("exncast", [landingpad], handler_node.name_type))
|
||||
self._set_local(handler_node.name, exn)
|
||||
|
||||
self.visit(handler_node.body)
|
||||
|
||||
if any(node.finalbody):
|
||||
@ -537,36 +556,44 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
self.current_block = finalizer
|
||||
|
||||
self.visit(node.finalbody)
|
||||
post_finalizer = self.current_block
|
||||
|
||||
if not self.current_block.is_terminated():
|
||||
dest = self.append(ir.GetLocal(final_state, ".k"))
|
||||
self.append(ir.IndirectBranch(dest, final_targets))
|
||||
|
||||
tail = self.add_block("try.tail")
|
||||
self.current_block = tail = self.add_block("try.tail")
|
||||
if any(node.finalbody):
|
||||
final_targets.append(tail)
|
||||
|
||||
if self.break_target:
|
||||
break_proxy.append(ir.Branch(finalizer))
|
||||
if self.continue_target:
|
||||
continue_proxy.append(ir.Branch(finalizer))
|
||||
return_proxy.append(ir.Branch(finalizer))
|
||||
if not body.is_terminated():
|
||||
if any(node.finalbody):
|
||||
|
||||
if not body.is_terminated():
|
||||
body.append(ir.SetLocal(final_state, ".k", tail))
|
||||
body.append(ir.Branch(finalizer))
|
||||
for handler in handlers:
|
||||
if not handler.is_terminated():
|
||||
handler.append(ir.SetLocal(final_state, ".k", tail))
|
||||
handler.append(ir.Branch(tail))
|
||||
else:
|
||||
body.append(ir.Branch(tail))
|
||||
for handler in handlers:
|
||||
if not handler.is_terminated():
|
||||
handler.append(ir.Branch(tail))
|
||||
|
||||
if any(tail.predecessors()):
|
||||
self.current_block = tail
|
||||
if not has_catchall:
|
||||
# Add a catch-all handler so that finally would have a chance
|
||||
# to execute.
|
||||
handler = self.add_block("handler.catchall")
|
||||
landingpad.add_clause(handler, None)
|
||||
handlers.append(handler)
|
||||
|
||||
for handler in handlers:
|
||||
if not handler.is_terminated():
|
||||
handler.append(ir.SetLocal(final_state, ".k", tail))
|
||||
handler.append(ir.Branch(tail))
|
||||
|
||||
if not post_finalizer.is_terminated():
|
||||
dest = post_finalizer.append(ir.GetLocal(final_state, ".k"))
|
||||
post_finalizer.append(ir.IndirectBranch(dest, final_targets))
|
||||
else:
|
||||
self.current_function.remove(tail)
|
||||
if not body.is_terminated():
|
||||
body.append(ir.Branch(tail))
|
||||
|
||||
for handler in handlers:
|
||||
if not handler.is_terminated():
|
||||
handler.append(ir.Branch(tail))
|
||||
|
||||
# TODO: With
|
||||
|
||||
@ -655,15 +682,16 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
mapped_lt_len = self.append(ir.Compare(end_cmpop, mapped_index, length))
|
||||
in_bounds = self.append(ir.Select(mapped_ge_0, mapped_lt_len,
|
||||
ir.Constant(False, builtins.TBool())))
|
||||
head = self.current_block
|
||||
|
||||
out_of_bounds_block = self.add_block()
|
||||
exn = out_of_bounds_block.append(ir.Alloc([], builtins.TIndexError()))
|
||||
out_of_bounds_block.append(ir.Raise(exn))
|
||||
self.current_block = out_of_bounds_block = self.add_block()
|
||||
exn = self.alloc_exn(builtins.TIndexError(),
|
||||
ir.Constant("index {0} out of bounds 0:{1}", builtins.TStr()),
|
||||
index, length)
|
||||
self.raise_exn(exn)
|
||||
|
||||
in_bounds_block = self.add_block()
|
||||
|
||||
self.append(ir.BranchIf(in_bounds, in_bounds_block, out_of_bounds_block))
|
||||
self.current_block = in_bounds_block
|
||||
self.current_block = in_bounds_block = self.add_block()
|
||||
head.append(ir.BranchIf(in_bounds, in_bounds_block, out_of_bounds_block))
|
||||
|
||||
return mapped_index
|
||||
|
||||
@ -673,7 +701,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
cond_block = self.current_block
|
||||
|
||||
self.current_block = body_block = self.add_block()
|
||||
self.append(ir.Raise(exn_gen()))
|
||||
self.raise_exn(exn_gen())
|
||||
|
||||
self.current_block = tail_block = self.add_block()
|
||||
cond_block.append(ir.BranchIf(cond, tail_block, body_block))
|
||||
@ -736,9 +764,10 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
|
||||
if node.slice.step is not None:
|
||||
step = self.visit(node.slice.step)
|
||||
self._make_check(self.append(ir.Compare(ast.NotEq(loc=None), step,
|
||||
ir.Constant(0, step.type))),
|
||||
lambda: self.append(ir.Alloc([], builtins.TValueError())))
|
||||
self._make_check(
|
||||
self.append(ir.Compare(ast.NotEq(loc=None), step, ir.Constant(0, step.type))),
|
||||
lambda: self.alloc_exn(builtins.TValueError(),
|
||||
ir.Constant("step cannot be zero", builtins.TStr())))
|
||||
else:
|
||||
step = ir.Constant(1, node.slice.type)
|
||||
counting_up = self.append(ir.Compare(ast.Gt(loc=None), step,
|
||||
@ -755,8 +784,12 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
slice_size = self.append(ir.Select(rem_not_empty,
|
||||
slice_size_c, slice_size_a,
|
||||
name="slice.size"))
|
||||
self._make_check(self.append(ir.Compare(ast.LtE(loc=None), slice_size, length)),
|
||||
lambda: self.append(ir.Alloc([], builtins.TValueError())))
|
||||
self._make_check(
|
||||
self.append(ir.Compare(ast.LtE(loc=None), slice_size, length)),
|
||||
lambda: self.alloc_exn(builtins.TValueError(),
|
||||
ir.Constant("slice size {0} is larger than iterable length {1}",
|
||||
builtins.TStr()),
|
||||
slice_size, iterable_len))
|
||||
|
||||
if self.current_assign is None:
|
||||
is_neg_size = self.append(ir.Compare(ast.Lt(loc=None),
|
||||
@ -832,9 +865,12 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
return lst
|
||||
else:
|
||||
length = self.iterable_len(self.current_assign)
|
||||
self._make_check(self.append(ir.Compare(ast.Eq(loc=None), length,
|
||||
ir.Constant(len(node.elts), self._size_type))),
|
||||
lambda: self.append(ir.Alloc([], builtins.TValueError())))
|
||||
self._make_check(
|
||||
self.append(ir.Compare(ast.Eq(loc=None), length,
|
||||
ir.Constant(len(node.elts), self._size_type))),
|
||||
lambda: self.alloc_exn(builtins.TValueError(),
|
||||
ir.Constant("list must be {0} elements long to decompose", builtins.TStr()),
|
||||
length))
|
||||
|
||||
for index, elt_node in enumerate(node.elts):
|
||||
elt = self.append(ir.GetElem(self.current_assign,
|
||||
@ -937,13 +973,15 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
rhs = self.visit(node.right)
|
||||
if isinstance(node.op, (ast.LShift, ast.RShift)):
|
||||
# Check for negative shift amount.
|
||||
self._make_check(self.append(ir.Compare(ast.GtE(loc=None), rhs,
|
||||
ir.Constant(0, rhs.type))),
|
||||
lambda: self.append(ir.Alloc([], builtins.TValueError())))
|
||||
self._make_check(
|
||||
self.append(ir.Compare(ast.GtE(loc=None), rhs, ir.Constant(0, rhs.type))),
|
||||
lambda: self.alloc_exn(builtins.TValueError(),
|
||||
ir.Constant("shift amount must be nonnegative", builtins.TStr())))
|
||||
elif isinstance(node.op, (ast.Div, ast.FloorDiv)):
|
||||
self._make_check(self.append(ir.Compare(ast.NotEq(loc=None), rhs,
|
||||
ir.Constant(0, rhs.type))),
|
||||
lambda: self.append(ir.Alloc([], builtins.TZeroDivisionError())))
|
||||
self._make_check(
|
||||
self.append(ir.Compare(ast.NotEq(loc=None), rhs, ir.Constant(0, rhs.type))),
|
||||
lambda: self.alloc_exn(builtins.TZeroDivisionError(),
|
||||
ir.Constant("cannot divide by zero", builtins.TStr())))
|
||||
|
||||
return self.append(ir.Arith(node.op, self.visit(node.left), rhs))
|
||||
elif isinstance(node.op, ast.Add): # list + list, tuple + tuple
|
||||
@ -1179,6 +1217,31 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
result_tail.append(ir.Branch(tail))
|
||||
return phi
|
||||
|
||||
# Keep this function with builtins.TException.attributes.
|
||||
def alloc_exn(self, typ, message=None, param0=None, param1=None, param2=None):
|
||||
attributes = [
|
||||
ir.Constant(typ.find().name, ir.TExceptionTypeInfo()), # typeinfo
|
||||
ir.Constant("<not thrown>", builtins.TStr()), # file
|
||||
ir.Constant(0, builtins.TInt(types.TValue(32))), # line
|
||||
ir.Constant(0, builtins.TInt(types.TValue(32))), # column
|
||||
]
|
||||
|
||||
if message is None:
|
||||
attributes.append(ir.Constant(typ.find().name, builtins.TStr()))
|
||||
else:
|
||||
attributes.append(message) # message
|
||||
|
||||
param_type = builtins.TInt(types.TValue(64))
|
||||
for param in [param0, param1, param2]:
|
||||
if param is None:
|
||||
attributes.append(ir.Constant(0, builtins.TInt(types.TValue(64))))
|
||||
else:
|
||||
if param.type != param_type:
|
||||
param = self.append(ir.Coerce(param, param_type))
|
||||
attributes.append(param) # paramN, N=0:2
|
||||
|
||||
return self.append(ir.Alloc(attributes, typ))
|
||||
|
||||
def visit_builtin_call(self, node):
|
||||
# A builtin by any other name... Ignore node.func, just use the type.
|
||||
typ = node.func.type
|
||||
@ -1275,7 +1338,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
separator=" ", suffix="\n")
|
||||
return ir.Constant(None, builtins.TNone())
|
||||
elif types.is_exn_constructor(typ):
|
||||
return self.append(ir.Alloc([self.visit(arg) for args in node.args], node.type))
|
||||
return self.alloc_exn(node.type, *[self.visit(arg_node) for arg_node in node.args])
|
||||
else:
|
||||
assert False
|
||||
|
||||
@ -1485,8 +1548,14 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
|
||||
format_string += ")"
|
||||
elif builtins.is_exception(value.type):
|
||||
# TODO: print exceptions
|
||||
assert False
|
||||
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]
|
||||
else:
|
||||
assert False
|
||||
|
||||
|
@ -440,23 +440,32 @@ class Inferencer(algorithm.Visitor):
|
||||
self.engine.process(diag)
|
||||
|
||||
if types.is_exn_constructor(typ):
|
||||
exns = {
|
||||
"IndexError": builtins.TIndexError,
|
||||
"ValueError": builtins.TValueError,
|
||||
}
|
||||
for exn in exns:
|
||||
if types.is_exn_constructor(typ, exn):
|
||||
valid_forms = lambda: [
|
||||
valid_form("{exn}() -> {exn}".format(exn=exn))
|
||||
]
|
||||
valid_forms = lambda: [
|
||||
valid_form("{exn}() -> {exn}".format(exn=typ.name)),
|
||||
valid_form("{exn}(message:str) -> {exn}".format(exn=typ.name)),
|
||||
valid_form("{exn}(message:str, param1:int(width=64)) -> {exn}".format(exn=typ.name)),
|
||||
valid_form("{exn}(message:str, param1:int(width=64), "
|
||||
"param2:int(width=64)) -> {exn}".format(exn=typ.name)),
|
||||
valid_form("{exn}(message:str, param1:int(width=64), "
|
||||
"param2:int(width=64), param3:int(width=64)) "
|
||||
"-> {exn}".format(exn=typ.name)),
|
||||
]
|
||||
|
||||
if len(node.args) == 0 and len(node.keywords) == 0:
|
||||
pass # False
|
||||
else:
|
||||
diagnose(valid_forms())
|
||||
if len(node.args) == 0 and len(node.keywords) == 0:
|
||||
pass # Default message, zeroes as parameters
|
||||
elif len(node.args) >= 1 and len(node.args) <= 4 and len(node.keywords) == 0:
|
||||
message, *params = node.args
|
||||
|
||||
self._unify(node.type, exns[exn](),
|
||||
node.loc, None)
|
||||
self._unify(message.type, builtins.TStr(),
|
||||
message.loc, None)
|
||||
for param in params:
|
||||
self._unify(param.type, builtins.TInt(types.TValue(64)),
|
||||
param.loc, None)
|
||||
else:
|
||||
diagnose(valid_forms())
|
||||
|
||||
self._unify(node.type, getattr(builtins, "T" + typ.name)(),
|
||||
node.loc, None)
|
||||
elif types.is_builtin(typ, "bool"):
|
||||
valid_forms = lambda: [
|
||||
valid_form("bool() -> bool"),
|
||||
@ -829,26 +838,27 @@ class Inferencer(algorithm.Visitor):
|
||||
def visit_ExceptHandlerT(self, node):
|
||||
self.generic_visit(node)
|
||||
|
||||
if not types.is_exn_constructor(node.filter.type):
|
||||
diag = diagnostic.Diagnostic("error",
|
||||
"this expression must refer to an exception constructor",
|
||||
{"type": types.TypePrinter().name(node.filter.type)},
|
||||
node.filter.loc)
|
||||
self.engine.process(diag)
|
||||
else:
|
||||
def makenotes(printer, typea, typeb, loca, locb):
|
||||
return [
|
||||
diagnostic.Diagnostic("note",
|
||||
"expression of type {typea}",
|
||||
{"typea": printer.name(typea)},
|
||||
loca),
|
||||
diagnostic.Diagnostic("note",
|
||||
"constructor of an exception of type {typeb}",
|
||||
{"typeb": printer.name(typeb)},
|
||||
locb)
|
||||
]
|
||||
self._unify(node.name_type, builtins.TException(node.filter.type.name),
|
||||
node.name_loc, node.filter.loc, makenotes)
|
||||
if node.filter is not None:
|
||||
if not types.is_exn_constructor(node.filter.type):
|
||||
diag = diagnostic.Diagnostic("error",
|
||||
"this expression must refer to an exception constructor",
|
||||
{"type": types.TypePrinter().name(node.filter.type)},
|
||||
node.filter.loc)
|
||||
self.engine.process(diag)
|
||||
else:
|
||||
def makenotes(printer, typea, typeb, loca, locb):
|
||||
return [
|
||||
diagnostic.Diagnostic("note",
|
||||
"expression of type {typea}",
|
||||
{"typea": printer.name(typea)},
|
||||
loca),
|
||||
diagnostic.Diagnostic("note",
|
||||
"constructor of an exception of type {typeb}",
|
||||
{"typeb": printer.name(typeb)},
|
||||
locb)
|
||||
]
|
||||
self._unify(node.name_type, builtins.TException(node.filter.type.name),
|
||||
node.name_loc, node.filter.loc, makenotes)
|
||||
|
||||
def _type_from_arguments(self, node, ret):
|
||||
self.generic_visit(node)
|
||||
@ -943,6 +953,17 @@ class Inferencer(algorithm.Visitor):
|
||||
self._unify(self.function.return_type, node.value.type,
|
||||
self.function.name_loc, node.value.loc, makenotes)
|
||||
|
||||
def visit_Raise(self, node):
|
||||
self.generic_visit(node)
|
||||
|
||||
exc_type = node.exc.type
|
||||
if not types.is_var(exc_type) and not builtins.is_exception(exc_type):
|
||||
diag = diagnostic.Diagnostic("error",
|
||||
"cannot raise a value of type {type}, which is not an exception",
|
||||
{"type": types.TypePrinter().name(exc_type)},
|
||||
node.exc.loc)
|
||||
self.engine.process(diag)
|
||||
|
||||
def visit_Assert(self, node):
|
||||
self.generic_visit(node)
|
||||
self._unify(node.test.type, builtins.TBool(),
|
||||
|
@ -43,7 +43,7 @@ class LLVMIRGenerator:
|
||||
return ll.IntType(builtins.get_int_width(typ))
|
||||
elif builtins.is_float(typ):
|
||||
return ll.DoubleType()
|
||||
elif builtins.is_str(typ):
|
||||
elif builtins.is_str(typ) or ir.is_exn_typeinfo(typ):
|
||||
return ll.IntType(8).as_pointer()
|
||||
elif builtins.is_list(typ):
|
||||
lleltty = self.llty_of_type(builtins.get_iterable_elt(typ))
|
||||
@ -51,11 +51,8 @@ class LLVMIRGenerator:
|
||||
elif builtins.is_range(typ):
|
||||
lleltty = self.llty_of_type(builtins.get_iterable_elt(typ))
|
||||
return ll.LiteralStructType([lleltty, lleltty, lleltty])
|
||||
elif builtins.is_exception(typ):
|
||||
# TODO: hack before EH is working
|
||||
return ll.LiteralStructType([])
|
||||
elif ir.is_basic_block(typ):
|
||||
return ll.LabelType()
|
||||
return ll.IntType(8).as_pointer()
|
||||
elif ir.is_option(typ):
|
||||
return ll.LiteralStructType([ll.IntType(1), self.llty_of_type(typ.params["inner"])])
|
||||
elif ir.is_environment(typ):
|
||||
@ -65,8 +62,21 @@ class LLVMIRGenerator:
|
||||
return llty
|
||||
else:
|
||||
return llty.as_pointer()
|
||||
else:
|
||||
assert False
|
||||
else: # Catch-all for exceptions and custom classes
|
||||
if builtins.is_exception(typ):
|
||||
name = 'Exception' # they all share layout
|
||||
else:
|
||||
name = typ.name
|
||||
|
||||
llty = self.llcontext.get_identified_type(name)
|
||||
if llty.elements is None:
|
||||
llty.elements = [self.llty_of_type(attrtyp)
|
||||
for attrtyp in typ.attributes.values()]
|
||||
|
||||
if bare or not builtins.is_allocated(typ):
|
||||
return llty
|
||||
else:
|
||||
return llty.as_pointer()
|
||||
|
||||
def llconst_of_const(self, const):
|
||||
llty = self.llty_of_type(const.type)
|
||||
@ -79,14 +89,27 @@ class LLVMIRGenerator:
|
||||
elif isinstance(const.value, (int, float)):
|
||||
return ll.Constant(llty, const.value)
|
||||
elif isinstance(const.value, str):
|
||||
as_bytes = (const.value + '\0').encode('utf-8')
|
||||
assert "\0" not in const.value
|
||||
|
||||
as_bytes = (const.value + "\0").encode("utf-8")
|
||||
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
|
||||
else:
|
||||
# Just a string
|
||||
name = self.llmodule.get_unique_name("str")
|
||||
linkage = "private"
|
||||
unnamed_addr = True
|
||||
|
||||
llstrty = ll.ArrayType(ll.IntType(8), len(as_bytes))
|
||||
llconst = ll.GlobalVariable(self.llmodule, llstrty,
|
||||
name=self.llmodule.get_unique_name("str"))
|
||||
llconst = ll.GlobalVariable(self.llmodule, llstrty, name)
|
||||
llconst.global_constant = True
|
||||
llconst.unnamed_addr = True
|
||||
llconst.linkage = 'internal'
|
||||
llconst.initializer = ll.Constant(llstrty, bytearray(as_bytes))
|
||||
llconst.linkage = linkage
|
||||
llconst.unnamed_addr = unnamed_addr
|
||||
|
||||
return llconst.bitcast(ll.IntType(8).as_pointer())
|
||||
else:
|
||||
assert False
|
||||
@ -112,6 +135,10 @@ class LLVMIRGenerator:
|
||||
llty = ll.FunctionType(ll.DoubleType(), [ll.DoubleType(), ll.DoubleType()])
|
||||
elif name == "printf":
|
||||
llty = ll.FunctionType(ll.VoidType(), [ll.IntType(8).as_pointer()], var_arg=True)
|
||||
elif name == "__artiq_raise":
|
||||
llty = ll.FunctionType(ll.VoidType(), [self.llty_of_type(builtins.TException())])
|
||||
elif name == "__artiq_personality":
|
||||
llty = ll.FunctionType(ll.IntType(32), [], var_arg=True)
|
||||
else:
|
||||
assert False
|
||||
return ll.Function(self.llmodule, llty, name)
|
||||
@ -124,8 +151,10 @@ class LLVMIRGenerator:
|
||||
elif isinstance(value, ir.Function):
|
||||
llfun = self.llmodule.get_global(value.name)
|
||||
if llfun is None:
|
||||
return ll.Function(self.llmodule, self.llty_of_type(value.type, bare=True),
|
||||
value.name)
|
||||
llfun = ll.Function(self.llmodule, self.llty_of_type(value.type, bare=True),
|
||||
value.name)
|
||||
llfun.linkage = 'internal'
|
||||
return llfun
|
||||
else:
|
||||
return llfun
|
||||
else:
|
||||
@ -186,6 +215,9 @@ class LLVMIRGenerator:
|
||||
self.fixups.append(fixup)
|
||||
return llinsn
|
||||
|
||||
def llindex(self, index):
|
||||
return ll.Constant(ll.IntType(32), index)
|
||||
|
||||
def process_Alloc(self, insn):
|
||||
if ir.is_environment(insn.type):
|
||||
return self.llbuilder.alloca(self.llty_of_type(insn.type, bare=True),
|
||||
@ -210,6 +242,13 @@ class LLVMIRGenerator:
|
||||
size=llsize)
|
||||
llvalue = self.llbuilder.insert_value(llvalue, llalloc, 1, name=insn.name)
|
||||
return llvalue
|
||||
elif builtins.is_exception(insn.type):
|
||||
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)])
|
||||
self.llbuilder.store(lloperand, llfieldptr)
|
||||
return llalloc
|
||||
elif builtins.is_allocated(insn.type):
|
||||
assert False
|
||||
else: # immutable
|
||||
@ -219,9 +258,6 @@ class LLVMIRGenerator:
|
||||
llvalue.name = insn.name
|
||||
return llvalue
|
||||
|
||||
def llindex(self, index):
|
||||
return ll.Constant(ll.IntType(32), index)
|
||||
|
||||
def llptr_to_var(self, llenv, env_ty, var_name):
|
||||
if var_name in env_ty.params:
|
||||
var_index = list(env_ty.params.keys()).index(var_name)
|
||||
@ -241,6 +277,8 @@ class LLVMIRGenerator:
|
||||
env = insn.environment()
|
||||
llptr = self.llptr_to_var(self.map(env), env.type, insn.var_name)
|
||||
llvalue = self.map(insn.value())
|
||||
if isinstance(llvalue, ll.Block):
|
||||
llvalue = ll.BlockAddress(self.llfunction, llvalue)
|
||||
if llptr.type.pointee != llvalue.type:
|
||||
# The environment argument is an i8*, so that all closures can
|
||||
# unify with each other regardless of environment type or size.
|
||||
@ -266,11 +304,11 @@ class LLVMIRGenerator:
|
||||
return self.llbuilder.load(llptr)
|
||||
|
||||
def process_SetAttr(self, insn):
|
||||
assert builtins.is_allocated(insns.object().type)
|
||||
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)
|
||||
return self.llbuilder.store(llptr, self.map(insn.value()))
|
||||
return self.llbuilder.store(self.map(insn.value()), llptr)
|
||||
|
||||
def process_GetElem(self, insn):
|
||||
llelts = self.llbuilder.extract_value(self.map(insn.list()), 1)
|
||||
@ -483,7 +521,9 @@ class LLVMIRGenerator:
|
||||
llargs = map(self.map, insn.operands)
|
||||
return self.llbuilder.call(self.llbuiltin("printf"), llargs,
|
||||
name=insn.name)
|
||||
# elif insn.op == "exncast":
|
||||
elif insn.op == "exncast":
|
||||
# This is an identity cast at LLVM IR level.
|
||||
return self.map(insn.operands[0])
|
||||
else:
|
||||
assert False
|
||||
|
||||
@ -495,13 +535,24 @@ class LLVMIRGenerator:
|
||||
name=insn.name)
|
||||
return llvalue
|
||||
|
||||
def process_Call(self, insn):
|
||||
def prepare_call(self, insn):
|
||||
llclosure, llargs = self.map(insn.target_function()), map(self.map, insn.arguments())
|
||||
llenv = self.llbuilder.extract_value(llclosure, 0)
|
||||
llfun = self.llbuilder.extract_value(llclosure, 1)
|
||||
return self.llbuilder.call(llfun, [llenv] + list(llargs),
|
||||
return llfun, [llenv] + list(llargs)
|
||||
|
||||
def process_Call(self, insn):
|
||||
llfun, llargs = self.prepare_call(insn)
|
||||
return self.llbuilder.call(llfun, llargs,
|
||||
name=insn.name)
|
||||
|
||||
def process_Invoke(self, insn):
|
||||
llfun, llargs = self.prepare_call(insn)
|
||||
llnormalblock = self.map(insn.normal_target())
|
||||
llunwindblock = self.map(insn.exception_target())
|
||||
return self.llbuilder.invoke(llfun, llargs, llnormalblock, llunwindblock,
|
||||
name=insn.name)
|
||||
|
||||
def process_Select(self, insn):
|
||||
return self.llbuilder.select(self.map(insn.condition()),
|
||||
self.map(insn.if_true()), self.map(insn.if_false()))
|
||||
@ -513,8 +564,11 @@ class LLVMIRGenerator:
|
||||
return self.llbuilder.cbranch(self.map(insn.condition()),
|
||||
self.map(insn.if_true()), self.map(insn.if_false()))
|
||||
|
||||
# def process_IndirectBranch(self, insn):
|
||||
# pass
|
||||
def process_IndirectBranch(self, insn):
|
||||
llinsn = self.llbuilder.branch_indirect(self.map(insn.target()))
|
||||
for dest in insn.destinations():
|
||||
llinsn.add_destination(self.map(dest))
|
||||
return llinsn
|
||||
|
||||
def process_Return(self, insn):
|
||||
if builtins.is_none(insn.value().type):
|
||||
@ -526,15 +580,33 @@ class LLVMIRGenerator:
|
||||
return self.llbuilder.unreachable()
|
||||
|
||||
def process_Raise(self, insn):
|
||||
# TODO: hack before EH is working
|
||||
llinsn = self.llbuilder.call(self.llbuiltin("llvm.trap"), [],
|
||||
arg = self.map(insn.operands[0])
|
||||
llinsn = self.llbuilder.call(self.llbuiltin("__artiq_raise"), [arg],
|
||||
name=insn.name)
|
||||
llinsn.attributes.add('noreturn')
|
||||
self.llbuilder.unreachable()
|
||||
return llinsn
|
||||
|
||||
# def process_Invoke(self, insn):
|
||||
# pass
|
||||
def process_LandingPad(self, insn):
|
||||
lllandingpad = self.llbuilder.landingpad(self.llty_of_type(insn.type),
|
||||
self.llbuiltin("__artiq_personality"))
|
||||
llexnnameptr = self.llbuilder.gep(lllandingpad, [self.llindex(0), self.llindex(0)])
|
||||
llexnname = self.llbuilder.load(llexnnameptr)
|
||||
|
||||
# def process_LandingPad(self, insn):
|
||||
# pass
|
||||
for target, typ in insn.clauses():
|
||||
if typ is None:
|
||||
llclauseexnname = ll.Constant(
|
||||
self.llty_of_type(ir.TExceptionTypeInfo()), None)
|
||||
else:
|
||||
llclauseexnname = self.llconst_of_const(
|
||||
ir.Constant(typ.name, ir.TExceptionTypeInfo()))
|
||||
lllandingpad.add_clause(ll.CatchClause(llclauseexnname))
|
||||
|
||||
llmatchingclause = self.llbuilder.icmp_unsigned('==', llexnname, llclauseexnname)
|
||||
with self.llbuilder.if_then(llmatchingclause):
|
||||
self.llbuilder.branch(self.map(target))
|
||||
|
||||
self.llbuilder.resume(lllandingpad)
|
||||
|
||||
return lllandingpad
|
||||
|
||||
|
@ -85,7 +85,9 @@ class TMono(Type):
|
||||
A monomorphic type, possibly parametric.
|
||||
|
||||
:class:`TMono` is supposed to be subclassed by builtin types,
|
||||
unlike all other :class:`Type` descendants.
|
||||
unlike all other :class:`Type` descendants. Similarly,
|
||||
instances of :class:`TMono` should never be allocated directly,
|
||||
as that will break the type-sniffing code in :mod:`builtins`.
|
||||
"""
|
||||
|
||||
attributes = OrderedDict()
|
||||
|
@ -239,13 +239,17 @@ class EscapeValidator(algorithm.Visitor):
|
||||
# Only three ways for a pointer to escape:
|
||||
# * Assigning or op-assigning it (we ensure an outlives relationship)
|
||||
# * Returning it (we only allow returning values that live forever)
|
||||
# * Raising it (we forbid raising mutable data)
|
||||
# * Raising it (we forbid allocating exceptions that refer to mutable data)¹
|
||||
#
|
||||
# Literals doesn't count: a constructed object is always
|
||||
# outlived by all its constituents.
|
||||
# Closures don't count: see above.
|
||||
# Calling functions doesn't count: arguments never outlive
|
||||
# the function body.
|
||||
#
|
||||
# ¹Strings are currently never allocated with a limited lifetime,
|
||||
# and exceptions can only refer to strings, so we don't actually check
|
||||
# this property. But we will need to, if string operations are ever added.
|
||||
|
||||
def visit_assignment(self, target, value, is_aug_assign=False):
|
||||
target_region = self._region_of(target)
|
||||
@ -298,14 +302,3 @@ class EscapeValidator(algorithm.Visitor):
|
||||
"cannot return a mutable value that does not live forever", {},
|
||||
node.value.loc, notes=self._diagnostics_for(region, node.value.loc) + [note])
|
||||
self.engine.process(diag)
|
||||
|
||||
def visit_Raise(self, node):
|
||||
if builtins.is_allocated(node.exc.type):
|
||||
note = diagnostic.Diagnostic("note",
|
||||
"this expression has type {type}",
|
||||
{"type": types.TypePrinter().name(node.exc.type)},
|
||||
node.exc.loc)
|
||||
diag = diagnostic.Diagnostic("error",
|
||||
"cannot raise a mutable value", {},
|
||||
node.exc.loc, notes=[note])
|
||||
self.engine.process(diag)
|
||||
|
@ -56,3 +56,18 @@ lambda x, y=1: x
|
||||
|
||||
k = "x"
|
||||
# CHECK-L: k:str
|
||||
|
||||
IndexError()
|
||||
# CHECK-L: IndexError:<constructor IndexError>():IndexError
|
||||
|
||||
IndexError("x")
|
||||
# CHECK-L: IndexError:<constructor IndexError>("x":str):IndexError
|
||||
|
||||
IndexError("x", 1)
|
||||
# CHECK-L: IndexError:<constructor IndexError>("x":str, 1:int(width=64)):IndexError
|
||||
|
||||
IndexError("x", 1, 1)
|
||||
# CHECK-L: IndexError:<constructor IndexError>("x":str, 1:int(width=64), 1:int(width=64)):IndexError
|
||||
|
||||
IndexError("x", 1, 1, 1)
|
||||
# CHECK-L: IndexError:<constructor IndexError>("x":str, 1:int(width=64), 1:int(width=64), 1:int(width=64)):IndexError
|
||||
|
Loading…
Reference in New Issue
Block a user