mirror of https://github.com/m-labs/artiq.git
Add tests for finally clause and reraising.
This commit is contained in:
parent
a83e7e2248
commit
2939d4f0f3
|
@ -979,15 +979,14 @@ class Raise(Terminator):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
:param value: (:class:`Value`) exception value
|
:param value: (:class:`Value`) exception value
|
||||||
:param exn: (:class:`BasicBlock`) exceptional target
|
:param exn: (:class:`BasicBlock` or None) exceptional target
|
||||||
"""
|
"""
|
||||||
def __init__(self, value, exn=None, name=""):
|
def __init__(self, value=None, exn=None, name=""):
|
||||||
assert isinstance(value, Value)
|
assert isinstance(value, Value)
|
||||||
|
operands = [value]
|
||||||
if exn is not None:
|
if exn is not None:
|
||||||
assert isinstance(exn, BasicBlock)
|
assert isinstance(exn, BasicBlock)
|
||||||
operands = [value, exn]
|
operands.append(exn)
|
||||||
else:
|
|
||||||
operands = [value]
|
|
||||||
super().__init__(operands, builtins.TNone(), name)
|
super().__init__(operands, builtins.TNone(), name)
|
||||||
|
|
||||||
def opcode(self):
|
def opcode(self):
|
||||||
|
@ -1000,6 +999,28 @@ class Raise(Terminator):
|
||||||
if len(self.operands) > 1:
|
if len(self.operands) > 1:
|
||||||
return self.operands[1]
|
return self.operands[1]
|
||||||
|
|
||||||
|
class Reraise(Terminator):
|
||||||
|
"""
|
||||||
|
A reraise instruction.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
:param exn: (:class:`BasicBlock` or None) exceptional target
|
||||||
|
"""
|
||||||
|
def __init__(self, exn=None, name=""):
|
||||||
|
operands = []
|
||||||
|
if exn is not None:
|
||||||
|
assert isinstance(exn, BasicBlock)
|
||||||
|
operands.append(exn)
|
||||||
|
super().__init__(operands, builtins.TNone(), name)
|
||||||
|
|
||||||
|
def opcode(self):
|
||||||
|
return "reraise"
|
||||||
|
|
||||||
|
def exception_target(self):
|
||||||
|
if len(self.operands) > 0:
|
||||||
|
return self.operands[0]
|
||||||
|
|
||||||
class Invoke(Terminator):
|
class Invoke(Terminator):
|
||||||
"""
|
"""
|
||||||
A function call operation that supports exception handling.
|
A function call operation that supports exception handling.
|
||||||
|
@ -1051,12 +1072,18 @@ class LandingPad(Terminator):
|
||||||
exception types corresponding to the basic block operands
|
exception types corresponding to the basic block operands
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name=""):
|
def __init__(self, cleanup, name=""):
|
||||||
super().__init__([], builtins.TException(), name)
|
super().__init__([cleanup], builtins.TException(), name)
|
||||||
self.types = []
|
self.types = []
|
||||||
|
|
||||||
|
def opcode(self):
|
||||||
|
return "landingpad"
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
return self.operands[0]
|
||||||
|
|
||||||
def clauses(self):
|
def clauses(self):
|
||||||
return zip(self.operands, self.types)
|
return zip(self.operands[1:], self.types)
|
||||||
|
|
||||||
def add_clause(self, target, typ):
|
def add_clause(self, target, typ):
|
||||||
assert isinstance(target, BasicBlock)
|
assert isinstance(target, BasicBlock)
|
||||||
|
@ -1065,14 +1092,11 @@ class LandingPad(Terminator):
|
||||||
self.types.append(typ.find() if typ is not None else None)
|
self.types.append(typ.find() if typ is not None else None)
|
||||||
target.uses.add(self)
|
target.uses.add(self)
|
||||||
|
|
||||||
def opcode(self):
|
|
||||||
return "landingpad"
|
|
||||||
|
|
||||||
def _operands_as_string(self):
|
def _operands_as_string(self):
|
||||||
table = []
|
table = []
|
||||||
for typ, target in zip(self.types, self.operands):
|
for target, typ in self.clauses():
|
||||||
if typ is None:
|
if typ is None:
|
||||||
table.append("... => {}".format(target.as_operand()))
|
table.append("... => {}".format(target.as_operand()))
|
||||||
else:
|
else:
|
||||||
table.append("{} => {}".format(types.TypePrinter().name(typ), target.as_operand()))
|
table.append("{} => {}".format(types.TypePrinter().name(typ), target.as_operand()))
|
||||||
return "[{}]".format(", ".join(table))
|
return "cleanup {}, [{}]".format(self.cleanup().as_operand(), ", ".join(table))
|
||||||
|
|
|
@ -466,23 +466,29 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
self.append(ir.Branch(self.continue_target))
|
self.append(ir.Branch(self.continue_target))
|
||||||
|
|
||||||
def raise_exn(self, exn):
|
def raise_exn(self, exn):
|
||||||
loc_file = ir.Constant(self.current_loc.source_buffer.name, builtins.TStr())
|
if exn is not None:
|
||||||
loc_line = ir.Constant(self.current_loc.line(), builtins.TInt(types.TValue(32)))
|
loc_file = ir.Constant(self.current_loc.source_buffer.name, builtins.TStr())
|
||||||
loc_column = ir.Constant(self.current_loc.column(), builtins.TInt(types.TValue(32)))
|
loc_line = ir.Constant(self.current_loc.line(), builtins.TInt(types.TValue(32)))
|
||||||
self.append(ir.SetAttr(exn, "__file__", loc_file))
|
loc_column = ir.Constant(self.current_loc.column(), builtins.TInt(types.TValue(32)))
|
||||||
self.append(ir.SetAttr(exn, "__line__", loc_line))
|
self.append(ir.SetAttr(exn, "__file__", loc_file))
|
||||||
self.append(ir.SetAttr(exn, "__col__", loc_column))
|
self.append(ir.SetAttr(exn, "__line__", loc_line))
|
||||||
if self.unwind_target:
|
self.append(ir.SetAttr(exn, "__col__", loc_column))
|
||||||
self.append(ir.Raise(exn, self.unwind_target))
|
|
||||||
|
if self.unwind_target is not None:
|
||||||
|
self.append(ir.Raise(exn, self.unwind_target))
|
||||||
|
else:
|
||||||
|
self.append(ir.Raise(exn))
|
||||||
else:
|
else:
|
||||||
self.append(ir.Raise(exn))
|
if self.unwind_target is not None:
|
||||||
|
self.append(ir.Reraise(self.unwind_target))
|
||||||
|
else:
|
||||||
|
self.append(ir.Reraise())
|
||||||
|
|
||||||
def visit_Raise(self, node):
|
def visit_Raise(self, node):
|
||||||
self.raise_exn(self.visit(node.exc))
|
self.raise_exn(self.visit(node.exc))
|
||||||
|
|
||||||
def visit_Try(self, node):
|
def visit_Try(self, node):
|
||||||
dispatcher = self.add_block("try.dispatch")
|
dispatcher = self.add_block("try.dispatch")
|
||||||
landingpad = dispatcher.append(ir.LandingPad())
|
|
||||||
|
|
||||||
if any(node.finalbody):
|
if any(node.finalbody):
|
||||||
# k for continuation
|
# k for continuation
|
||||||
|
@ -532,8 +538,10 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
self.continue_target = old_continue
|
self.continue_target = old_continue
|
||||||
self.return_target = old_return
|
self.return_target = old_return
|
||||||
|
|
||||||
|
cleanup = self.add_block('handler.cleanup')
|
||||||
|
landingpad = dispatcher.append(ir.LandingPad(cleanup))
|
||||||
|
|
||||||
handlers = []
|
handlers = []
|
||||||
has_catchall = False
|
|
||||||
for handler_node in node.handlers:
|
for handler_node in node.handlers:
|
||||||
exn_type = handler_node.name_type.find()
|
exn_type = handler_node.name_type.find()
|
||||||
if handler_node.filter is not None and \
|
if handler_node.filter is not None and \
|
||||||
|
@ -543,7 +551,6 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
else:
|
else:
|
||||||
handler = self.add_block("handler.catchall")
|
handler = self.add_block("handler.catchall")
|
||||||
landingpad.add_clause(handler, None)
|
landingpad.add_clause(handler, None)
|
||||||
has_catchall = True
|
|
||||||
|
|
||||||
self.current_block = handler
|
self.current_block = handler
|
||||||
if handler_node.name is not None:
|
if handler_node.name is not None:
|
||||||
|
@ -561,9 +568,13 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
self.visit(node.finalbody)
|
self.visit(node.finalbody)
|
||||||
post_finalizer = self.current_block
|
post_finalizer = self.current_block
|
||||||
|
|
||||||
|
reraise = self.add_block('try.reraise')
|
||||||
|
reraise.append(ir.Reraise(self.unwind_target))
|
||||||
|
|
||||||
self.current_block = tail = self.add_block("try.tail")
|
self.current_block = tail = self.add_block("try.tail")
|
||||||
if any(node.finalbody):
|
if any(node.finalbody):
|
||||||
final_targets.append(tail)
|
final_targets.append(tail)
|
||||||
|
final_targets.append(reraise)
|
||||||
|
|
||||||
if self.break_target:
|
if self.break_target:
|
||||||
break_proxy.append(ir.Branch(finalizer))
|
break_proxy.append(ir.Branch(finalizer))
|
||||||
|
@ -575,17 +586,13 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
body.append(ir.SetLocal(final_state, ".k", tail))
|
body.append(ir.SetLocal(final_state, ".k", tail))
|
||||||
body.append(ir.Branch(finalizer))
|
body.append(ir.Branch(finalizer))
|
||||||
|
|
||||||
if not has_catchall:
|
cleanup.append(ir.SetLocal(final_state, ".k", reraise))
|
||||||
# Add a catch-all handler so that finally would have a chance
|
cleanup.append(ir.Branch(finalizer))
|
||||||
# to execute.
|
|
||||||
handler = self.add_block("handler.catchall")
|
|
||||||
landingpad.add_clause(handler, None)
|
|
||||||
handlers.append((handler, handler))
|
|
||||||
|
|
||||||
for handler, post_handler in handlers:
|
for handler, post_handler in handlers:
|
||||||
if not post_handler.is_terminated():
|
if not post_handler.is_terminated():
|
||||||
post_handler.append(ir.SetLocal(final_state, ".k", tail))
|
post_handler.append(ir.SetLocal(final_state, ".k", tail))
|
||||||
post_handler.append(ir.Branch(tail))
|
post_handler.append(ir.Branch(finalizer))
|
||||||
|
|
||||||
if not post_finalizer.is_terminated():
|
if not post_finalizer.is_terminated():
|
||||||
dest = post_finalizer.append(ir.GetLocal(final_state, ".k"))
|
dest = post_finalizer.append(ir.GetLocal(final_state, ".k"))
|
||||||
|
@ -594,6 +601,8 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
if not body.is_terminated():
|
if not body.is_terminated():
|
||||||
body.append(ir.Branch(tail))
|
body.append(ir.Branch(tail))
|
||||||
|
|
||||||
|
cleanup.append(ir.Reraise(self.unwind_target))
|
||||||
|
|
||||||
for handler, post_handler in handlers:
|
for handler, post_handler in handlers:
|
||||||
if not post_handler.is_terminated():
|
if not post_handler.is_terminated():
|
||||||
post_handler.append(ir.Branch(tail))
|
post_handler.append(ir.Branch(tail))
|
||||||
|
|
|
@ -15,7 +15,9 @@ class DeadCodeEliminator:
|
||||||
|
|
||||||
def process_function(self, func):
|
def process_function(self, func):
|
||||||
for block in func.basic_blocks:
|
for block in func.basic_blocks:
|
||||||
if not any(block.predecessors()) and block != func.entry():
|
if not any(block.predecessors()) and \
|
||||||
|
not any([isinstance(use, ir.SetLocal) for use in block.uses]) and \
|
||||||
|
block != func.entry():
|
||||||
self.remove_block(block)
|
self.remove_block(block)
|
||||||
|
|
||||||
def remove_block(self, block):
|
def remove_block(self, block):
|
||||||
|
@ -25,10 +27,6 @@ class DeadCodeEliminator:
|
||||||
use.remove_incoming_block(block)
|
use.remove_incoming_block(block)
|
||||||
if not any(use.operands):
|
if not any(use.operands):
|
||||||
self.remove_instruction(use)
|
self.remove_instruction(use)
|
||||||
elif isinstance(use, ir.SetLocal):
|
|
||||||
# Setting the target for `finally` resumption, e.g.
|
|
||||||
# setlocal(.k) %v.4, label %try.doreturn
|
|
||||||
use.erase()
|
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
|
|
|
@ -956,13 +956,14 @@ class Inferencer(algorithm.Visitor):
|
||||||
def visit_Raise(self, node):
|
def visit_Raise(self, node):
|
||||||
self.generic_visit(node)
|
self.generic_visit(node)
|
||||||
|
|
||||||
exc_type = node.exc.type
|
if node.exc is not None:
|
||||||
if not types.is_var(exc_type) and not builtins.is_exception(exc_type):
|
exc_type = node.exc.type
|
||||||
diag = diagnostic.Diagnostic("error",
|
if not types.is_var(exc_type) and not builtins.is_exception(exc_type):
|
||||||
"cannot raise a value of type {type}, which is not an exception",
|
diag = diagnostic.Diagnostic("error",
|
||||||
{"type": types.TypePrinter().name(exc_type)},
|
"cannot raise a value of type {type}, which is not an exception",
|
||||||
node.exc.loc)
|
{"type": types.TypePrinter().name(exc_type)},
|
||||||
self.engine.process(diag)
|
node.exc.loc)
|
||||||
|
self.engine.process(diag)
|
||||||
|
|
||||||
def visit_Assert(self, node):
|
def visit_Assert(self, node):
|
||||||
self.generic_visit(node)
|
self.generic_visit(node)
|
||||||
|
|
|
@ -137,13 +137,19 @@ class LLVMIRGenerator:
|
||||||
llty = ll.FunctionType(ll.DoubleType(), [ll.DoubleType(), ll.DoubleType()])
|
llty = ll.FunctionType(ll.DoubleType(), [ll.DoubleType(), ll.DoubleType()])
|
||||||
elif name == "printf":
|
elif name == "printf":
|
||||||
llty = ll.FunctionType(ll.VoidType(), [ll.IntType(8).as_pointer()], var_arg=True)
|
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":
|
elif name == "__artiq_personality":
|
||||||
llty = ll.FunctionType(ll.IntType(32), [], var_arg=True)
|
llty = ll.FunctionType(ll.IntType(32), [], var_arg=True)
|
||||||
|
elif name == "__artiq_raise":
|
||||||
|
llty = ll.FunctionType(ll.VoidType(), [self.llty_of_type(builtins.TException())])
|
||||||
|
elif name == "__artiq_reraise":
|
||||||
|
llty = ll.FunctionType(ll.VoidType(), [])
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
return ll.Function(self.llmodule, llty, name)
|
|
||||||
|
llfun = ll.Function(self.llmodule, llty, name)
|
||||||
|
if name in ("__artiq_raise", "__artiq_reraise", "llvm.trap"):
|
||||||
|
llfun.attributes.add("noreturn")
|
||||||
|
return llfun
|
||||||
|
|
||||||
def map(self, value):
|
def map(self, value):
|
||||||
if isinstance(value, (ir.Argument, ir.Instruction, ir.BasicBlock)):
|
if isinstance(value, (ir.Argument, ir.Instruction, ir.BasicBlock)):
|
||||||
|
@ -599,12 +605,20 @@ class LLVMIRGenerator:
|
||||||
llinsn.attributes.add('noreturn')
|
llinsn.attributes.add('noreturn')
|
||||||
return llinsn
|
return llinsn
|
||||||
|
|
||||||
|
def process_Reraise(self, insn):
|
||||||
|
llinsn = self.llbuilder.call(self.llbuiltin("__artiq_reraise"), [],
|
||||||
|
name=insn.name)
|
||||||
|
llinsn.attributes.add('noreturn')
|
||||||
|
self.llbuilder.unreachable()
|
||||||
|
return llinsn
|
||||||
|
|
||||||
def process_LandingPad(self, insn):
|
def process_LandingPad(self, insn):
|
||||||
# Layout on return from landing pad: {%_Unwind_Exception*, %Exception*}
|
# Layout on return from landing pad: {%_Unwind_Exception*, %Exception*}
|
||||||
lllandingpadty = ll.LiteralStructType([ll.IntType(8).as_pointer(),
|
lllandingpadty = ll.LiteralStructType([ll.IntType(8).as_pointer(),
|
||||||
ll.IntType(8).as_pointer()])
|
ll.IntType(8).as_pointer()])
|
||||||
lllandingpad = self.llbuilder.landingpad(lllandingpadty,
|
lllandingpad = self.llbuilder.landingpad(lllandingpadty,
|
||||||
self.llbuiltin("__artiq_personality"))
|
self.llbuiltin("__artiq_personality"),
|
||||||
|
cleanup=True)
|
||||||
llrawexn = self.llbuilder.extract_value(lllandingpad, 1)
|
llrawexn = self.llbuilder.extract_value(lllandingpad, 1)
|
||||||
llexn = self.llbuilder.bitcast(llrawexn, self.llty_of_type(insn.type))
|
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)])
|
||||||
|
@ -627,7 +641,7 @@ class LLVMIRGenerator:
|
||||||
self.llbuilder.branch(self.map(target))
|
self.llbuilder.branch(self.map(target))
|
||||||
|
|
||||||
if self.llbuilder.basic_block.terminator is None:
|
if self.llbuilder.basic_block.terminator is None:
|
||||||
self.llbuilder.resume(lllandingpad)
|
self.llbuilder.branch(self.map(insn.cleanup()))
|
||||||
|
|
||||||
return llexn
|
return llexn
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# RUN: %python -m artiq.compiler.testbench.jit %s
|
# RUN: %python -m artiq.compiler.testbench.jit %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
# REQUIRES: exceptions
|
# REQUIRES: exceptions
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# RUN: %python -m artiq.compiler.testbench.jit %s
|
# RUN: %python -m artiq.compiler.testbench.jit %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
# REQUIRES: exceptions
|
# REQUIRES: exceptions
|
||||||
|
|
||||||
def catch(f):
|
def catch(f):
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# RUN: %python -m artiq.compiler.testbench.jit %s
|
# RUN: %python -m artiq.compiler.testbench.jit %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
# REQUIRES: exceptions
|
# REQUIRES: exceptions
|
||||||
|
|
||||||
def catch(f):
|
def catch(f):
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# RUN: %python -m artiq.compiler.testbench.jit %s
|
# RUN: %python -m artiq.compiler.testbench.jit %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
# REQUIRES: exceptions
|
# REQUIRES: exceptions
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.jit %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
# REQUIRES: exceptions
|
||||||
|
|
||||||
|
def f():
|
||||||
|
try:
|
||||||
|
1/0
|
||||||
|
finally:
|
||||||
|
print("f-fin")
|
||||||
|
print("f-out")
|
||||||
|
|
||||||
|
def g():
|
||||||
|
try:
|
||||||
|
f()
|
||||||
|
except:
|
||||||
|
print("g-except")
|
||||||
|
|
||||||
|
# CHECK-L: f-fin
|
||||||
|
# CHECK-NOT-L: f-out
|
||||||
|
# CHECK-L: g-except
|
||||||
|
g()
|
|
@ -0,0 +1,17 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.jit %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
# REQUIRES: exceptions
|
||||||
|
|
||||||
|
def f():
|
||||||
|
try:
|
||||||
|
1/0
|
||||||
|
except:
|
||||||
|
print("f-except")
|
||||||
|
finally:
|
||||||
|
print("f-fin")
|
||||||
|
print("f-out")
|
||||||
|
|
||||||
|
# CHECK-L: f-except
|
||||||
|
# CHECK-L: f-fin
|
||||||
|
# CHECK-L: f-out
|
||||||
|
f()
|
|
@ -0,0 +1,23 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.jit %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
# REQUIRES: exceptions
|
||||||
|
|
||||||
|
def f():
|
||||||
|
try:
|
||||||
|
1/0
|
||||||
|
finally:
|
||||||
|
print("f-fin")
|
||||||
|
raise ValueError()
|
||||||
|
|
||||||
|
def g():
|
||||||
|
try:
|
||||||
|
f()
|
||||||
|
except ZeroDivisionError:
|
||||||
|
print("g-except-zde")
|
||||||
|
except ValueError:
|
||||||
|
print("g-except-ve")
|
||||||
|
|
||||||
|
# CHECK-L: f-fin
|
||||||
|
# CHECK-L: g-except-ve
|
||||||
|
# CHECK-NOT-L: g-except-zde
|
||||||
|
g()
|
|
@ -0,0 +1,21 @@
|
||||||
|
# RUN: %python -m artiq.compiler.testbench.jit %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
# REQUIRES: exceptions
|
||||||
|
|
||||||
|
def f():
|
||||||
|
try:
|
||||||
|
1/0
|
||||||
|
finally:
|
||||||
|
print("f-fin")
|
||||||
|
return
|
||||||
|
|
||||||
|
def g():
|
||||||
|
try:
|
||||||
|
f()
|
||||||
|
except:
|
||||||
|
print("g-except")
|
||||||
|
|
||||||
|
# CHECK-L: f-fin
|
||||||
|
# CHECK-NOT-L: f-out
|
||||||
|
# CHECK-NOT-L: g-except
|
||||||
|
g()
|
|
@ -0,0 +1,16 @@
|
||||||
|
# RUN: %not %python -m artiq.compiler.testbench.jit %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
# REQUIRES: exceptions
|
||||||
|
|
||||||
|
def f():
|
||||||
|
# CHECK-L: Uncaught ZeroDivisionError
|
||||||
|
# CHECK-L: at input.py:${LINE:+1}:
|
||||||
|
1/0
|
||||||
|
|
||||||
|
def g():
|
||||||
|
try:
|
||||||
|
f()
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
|
||||||
|
g()
|
|
@ -0,0 +1,16 @@
|
||||||
|
# RUN: %not %python -m artiq.compiler.testbench.jit %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
# REQUIRES: exceptions
|
||||||
|
|
||||||
|
def f():
|
||||||
|
1/0
|
||||||
|
|
||||||
|
def g():
|
||||||
|
try:
|
||||||
|
f()
|
||||||
|
except Exception as e:
|
||||||
|
# CHECK-L: Uncaught ZeroDivisionError
|
||||||
|
# CHECK-L: at input.py:${LINE:+1}:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
g()
|
|
@ -1,6 +1,7 @@
|
||||||
# RUN: %not %python -m artiq.compiler.testbench.jit %s
|
# RUN: %not %python -m artiq.compiler.testbench.jit %s >%t
|
||||||
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
# REQUIRES: exceptions
|
# REQUIRES: exceptions
|
||||||
|
|
||||||
# CHECK-L: Uncaught ZeroDivisionError: cannot divide by zero (0, 0, 0)
|
# CHECK-L: Uncaught ZeroDivisionError: cannot divide by zero (0, 0, 0)
|
||||||
# CHECK-L: at input.py:${LINE:+1}:0
|
# CHECK-L: at input.py:${LINE:+1}:
|
||||||
1/0
|
1/0
|
||||||
|
|
|
@ -62,3 +62,15 @@ def i():
|
||||||
# CHECK-NOT-L: i-out
|
# CHECK-NOT-L: i-out
|
||||||
# CHECK-L: i 30
|
# CHECK-L: i 30
|
||||||
print("i", i())
|
print("i", i())
|
||||||
|
|
||||||
|
def j():
|
||||||
|
try:
|
||||||
|
print("j-try")
|
||||||
|
finally:
|
||||||
|
print("j-finally")
|
||||||
|
print("j-out")
|
||||||
|
|
||||||
|
# CHECK-L: j-try
|
||||||
|
# CHECK-L: j-finally
|
||||||
|
# CHECK-L: j-out
|
||||||
|
print("j", j())
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
/* Logging */
|
/* Logging */
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
#define EH_LOG0(fmt) fprintf(stderr, "__artiq_personality: " fmt "\n")
|
#define EH_LOG0(fmt) fprintf(stderr, "%s: " fmt "\n", __func__)
|
||||||
#define EH_LOG(fmt, ...) fprintf(stderr, "__artiq_personality: " fmt "\n", __VA_ARGS__)
|
#define EH_LOG(fmt, ...) fprintf(stderr, "%s: " fmt "\n", __func__, __VA_ARGS__)
|
||||||
#else
|
#else
|
||||||
#define EH_LOG0(fmt)
|
#define EH_LOG0(fmt)
|
||||||
#define EH_LOG(fmt, ...)
|
#define EH_LOG(fmt, ...)
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
#define EH_FAIL(err) \
|
#define EH_FAIL(err) \
|
||||||
do { \
|
do { \
|
||||||
fprintf(stderr, "__artiq_personality fatal: %s\n", err); \
|
fprintf(stderr, "%s fatal: %s\n", __func__, err); \
|
||||||
abort(); \
|
abort(); \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
|
@ -195,37 +195,61 @@ static uintptr_t readEncodedPointer(const uint8_t **data, uint8_t encoding) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Raising and catching */
|
/* Raising */
|
||||||
|
|
||||||
#define ARTIQ_EXCEPTION_CLASS 0x4152545141525451LL // 'ARTQARTQ'
|
#define ARTIQ_EXCEPTION_CLASS 0x4152545141525451LL // 'ARTQARTQ'
|
||||||
|
|
||||||
|
static void __artiq_cleanup(_Unwind_Reason_Code reason, struct _Unwind_Exception *exc);
|
||||||
|
|
||||||
struct artiq_raised_exception {
|
struct artiq_raised_exception {
|
||||||
struct _Unwind_Exception unwind;
|
struct _Unwind_Exception unwind;
|
||||||
struct artiq_exception artiq;
|
struct artiq_exception artiq;
|
||||||
|
int handled;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct artiq_raised_exception inflight;
|
||||||
|
|
||||||
|
void __artiq_raise(struct artiq_exception *artiq_exn) {
|
||||||
|
EH_LOG("===> raise (name=%s)", artiq_exn->name);
|
||||||
|
|
||||||
|
memmove(&inflight.artiq, artiq_exn, sizeof(struct artiq_exception));
|
||||||
|
inflight.unwind.exception_class = ARTIQ_EXCEPTION_CLASS;
|
||||||
|
inflight.unwind.exception_cleanup = &__artiq_cleanup;
|
||||||
|
inflight.handled = 0;
|
||||||
|
|
||||||
|
_Unwind_Reason_Code result = _Unwind_RaiseException(&inflight.unwind);
|
||||||
|
EH_ASSERT((result == _URC_END_OF_STACK) &&
|
||||||
|
"Unexpected error during unwinding");
|
||||||
|
|
||||||
|
// If we're here, there are no handlers, only cleanups.
|
||||||
|
__artiq_terminate(&inflight.artiq);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __artiq_reraise() {
|
||||||
|
if(inflight.handled) {
|
||||||
|
EH_LOG0("===> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Catching */
|
||||||
|
|
||||||
|
// The code below does not refer to the `inflight` global.
|
||||||
|
|
||||||
static void __artiq_cleanup(_Unwind_Reason_Code reason, struct _Unwind_Exception *exc) {
|
static void __artiq_cleanup(_Unwind_Reason_Code reason, struct _Unwind_Exception *exc) {
|
||||||
|
EH_LOG0("===> cleanup");
|
||||||
struct artiq_raised_exception *inflight = (struct artiq_raised_exception*) exc;
|
struct artiq_raised_exception *inflight = (struct artiq_raised_exception*) exc;
|
||||||
// The in-flight exception is statically allocated, so we don't need to free it.
|
// The in-flight exception is statically allocated, so we don't need to free it.
|
||||||
// But, we clear it to mark it as processed.
|
// But, we clear it to mark it as processed.
|
||||||
memset(&inflight->artiq, 0, sizeof(struct artiq_exception));
|
memset(&inflight->artiq, 0, sizeof(struct artiq_exception));
|
||||||
}
|
}
|
||||||
|
|
||||||
void __artiq_raise(struct artiq_exception *artiq_exn) {
|
|
||||||
static struct artiq_raised_exception inflight;
|
|
||||||
memcpy(&inflight.artiq, artiq_exn, sizeof(struct artiq_exception));
|
|
||||||
inflight.unwind.exception_class = ARTIQ_EXCEPTION_CLASS;
|
|
||||||
inflight.unwind.exception_cleanup = &__artiq_cleanup;
|
|
||||||
|
|
||||||
_Unwind_Reason_Code result = _Unwind_RaiseException(&inflight.unwind);
|
|
||||||
if(result == _URC_END_OF_STACK) {
|
|
||||||
__artiq_terminate(&inflight.artiq);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "__artiq_raise: unexpected error (%d)\n", result);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_Unwind_Reason_Code __artiq_personality(
|
_Unwind_Reason_Code __artiq_personality(
|
||||||
int version, _Unwind_Action actions, uint64_t exceptionClass,
|
int version, _Unwind_Action actions, uint64_t exceptionClass,
|
||||||
struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context) {
|
struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context) {
|
||||||
|
@ -345,6 +369,9 @@ _Unwind_Reason_Code __artiq_personality(
|
||||||
if(!(actions & _UA_SEARCH_PHASE)) {
|
if(!(actions & _UA_SEARCH_PHASE)) {
|
||||||
EH_LOG0("=> jumping to landing pad");
|
EH_LOG0("=> jumping to landing pad");
|
||||||
|
|
||||||
|
if(actions & _UA_HANDLER_FRAME)
|
||||||
|
inflight->handled = 1;
|
||||||
|
|
||||||
_Unwind_SetGR(context, __builtin_eh_return_data_regno(0),
|
_Unwind_SetGR(context, __builtin_eh_return_data_regno(0),
|
||||||
(uintptr_t)exceptionObject);
|
(uintptr_t)exceptionObject);
|
||||||
_Unwind_SetGR(context, __builtin_eh_return_data_regno(1),
|
_Unwind_SetGR(context, __builtin_eh_return_data_regno(1),
|
||||||
|
|
|
@ -19,10 +19,15 @@ struct artiq_exception {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void __artiq_terminate(struct artiq_exception *artiq_exn)
|
/* Provided by the runtime */
|
||||||
|
void __artiq_raise(struct artiq_exception *artiq_exn)
|
||||||
|
__attribute__((noreturn));
|
||||||
|
void __artiq_reraise()
|
||||||
__attribute__((noreturn));
|
__attribute__((noreturn));
|
||||||
|
|
||||||
void __artiq_raise(struct artiq_exception *artiq_exn);
|
/* Called by the runtime */
|
||||||
|
void __artiq_terminate(struct artiq_exception *artiq_exn)
|
||||||
|
__attribute__((noreturn));
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue