forked from M-Labs/artiq
compiler: fixed try codegen and allocate exceptions
Exceptions are now allocated in the runtime when we raise the exception, and destroyed when we exit the catch block. Nested exception and try block is now supported, and should behave the same as in CPython. Exceptions raised in except blocks will now unwind through finally blocks, matching the behavior in CPython. Reraise will now preserve backtrace. Phi block LLVM IR generation is modified to handle landingpads, which one ARTIQ IR will map to multiple LLVM IR.
This commit is contained in:
parent
4644e105b1
commit
da4ff44377
|
@ -1245,9 +1245,9 @@ class Raise(Terminator):
|
||||||
if len(self.operands) > 1:
|
if len(self.operands) > 1:
|
||||||
return self.operands[1]
|
return self.operands[1]
|
||||||
|
|
||||||
class Reraise(Terminator):
|
class Resume(Terminator):
|
||||||
"""
|
"""
|
||||||
A reraise instruction.
|
A resume instruction.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -1261,7 +1261,7 @@ class Reraise(Terminator):
|
||||||
super().__init__(operands, builtins.TNone(), name)
|
super().__init__(operands, builtins.TNone(), name)
|
||||||
|
|
||||||
def opcode(self):
|
def opcode(self):
|
||||||
return "reraise"
|
return "resume"
|
||||||
|
|
||||||
def exception_target(self):
|
def exception_target(self):
|
||||||
if len(self.operands) > 0:
|
if len(self.operands) > 0:
|
||||||
|
|
|
@ -8,6 +8,7 @@ semantics explicitly.
|
||||||
|
|
||||||
from collections import OrderedDict, defaultdict
|
from collections import OrderedDict, defaultdict
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
from itertools import chain
|
||||||
from pythonparser import algorithm, diagnostic, ast
|
from pythonparser import algorithm, diagnostic, ast
|
||||||
from .. import types, builtins, asttyped, ir, iodelay
|
from .. import types, builtins, asttyped, ir, iodelay
|
||||||
|
|
||||||
|
@ -61,6 +62,9 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
the basic block to which ``return`` will transfer control
|
the basic block to which ``return`` will transfer control
|
||||||
:ivar unwind_target: (:class:`ir.BasicBlock` or None)
|
:ivar unwind_target: (:class:`ir.BasicBlock` or None)
|
||||||
the basic block to which unwinding will transfer control
|
the basic block to which unwinding will transfer control
|
||||||
|
:ivar catch_clauses: (list of (:class:`ir.BasicBlock`, :class:`types.Type` or None))
|
||||||
|
a list of catch clauses that should be appended to inner try block
|
||||||
|
landingpad
|
||||||
:ivar final_branch: (function (target: :class:`ir.BasicBlock`, block: :class:`ir.BasicBlock)
|
:ivar final_branch: (function (target: :class:`ir.BasicBlock`, block: :class:`ir.BasicBlock)
|
||||||
or None)
|
or None)
|
||||||
the function that appends to ``block`` a jump through the ``finally`` statement
|
the function that appends to ``block`` a jump through the ``finally`` statement
|
||||||
|
@ -103,10 +107,13 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
self.current_private_env = None
|
self.current_private_env = None
|
||||||
self.current_args = None
|
self.current_args = None
|
||||||
self.current_assign = None
|
self.current_assign = None
|
||||||
|
self.current_exception = None
|
||||||
self.break_target = None
|
self.break_target = None
|
||||||
self.continue_target = None
|
self.continue_target = None
|
||||||
self.return_target = None
|
self.return_target = None
|
||||||
self.unwind_target = None
|
self.unwind_target = None
|
||||||
|
self.catch_clauses = []
|
||||||
|
self.outer_final = None
|
||||||
self.final_branch = None
|
self.final_branch = None
|
||||||
self.function_map = dict()
|
self.function_map = dict()
|
||||||
self.variable_map = dict()
|
self.variable_map = dict()
|
||||||
|
@ -650,9 +657,9 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
self.append(ir.Raise(exn))
|
self.append(ir.Raise(exn))
|
||||||
else:
|
else:
|
||||||
if self.unwind_target is not None:
|
if self.unwind_target is not None:
|
||||||
self.append(ir.Reraise(self.unwind_target))
|
self.append(ir.Resume(self.unwind_target))
|
||||||
else:
|
else:
|
||||||
self.append(ir.Reraise())
|
self.append(ir.Resume())
|
||||||
|
|
||||||
def visit_Raise(self, node):
|
def visit_Raise(self, node):
|
||||||
if node.exc is not None and types.is_exn_constructor(node.exc.type):
|
if node.exc is not None and types.is_exn_constructor(node.exc.type):
|
||||||
|
@ -662,6 +669,9 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
|
|
||||||
def visit_Try(self, node):
|
def visit_Try(self, node):
|
||||||
dispatcher = self.add_block("try.dispatch")
|
dispatcher = self.add_block("try.dispatch")
|
||||||
|
cleanup = self.add_block('handler.cleanup')
|
||||||
|
landingpad = ir.LandingPad(cleanup)
|
||||||
|
dispatcher.append(landingpad)
|
||||||
|
|
||||||
if any(node.finalbody):
|
if any(node.finalbody):
|
||||||
# k for continuation
|
# k for continuation
|
||||||
|
@ -677,15 +687,6 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
final_targets.append(target)
|
final_targets.append(target)
|
||||||
final_paths.append(block)
|
final_paths.append(block)
|
||||||
|
|
||||||
final_exn_targets = []
|
|
||||||
final_exn_paths = []
|
|
||||||
# raise has to be treated differently
|
|
||||||
# we cannot follow indirectbr for local access validation, so we
|
|
||||||
# have to construct the control flow explicitly
|
|
||||||
def exception_final_branch(target, block):
|
|
||||||
final_exn_targets.append(target)
|
|
||||||
final_exn_paths.append(block)
|
|
||||||
|
|
||||||
if self.break_target is not None:
|
if self.break_target is not None:
|
||||||
break_proxy = self.add_block("try.break")
|
break_proxy = self.add_block("try.break")
|
||||||
old_break, self.break_target = self.break_target, break_proxy
|
old_break, self.break_target = self.break_target, break_proxy
|
||||||
|
@ -706,15 +707,52 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
return_action.append(ir.Return(value))
|
return_action.append(ir.Return(value))
|
||||||
final_branch(return_action, return_proxy)
|
final_branch(return_action, return_proxy)
|
||||||
|
|
||||||
|
old_outer_final, self.outer_final = self.outer_final, final_branch
|
||||||
|
elif self.outer_final is None:
|
||||||
|
landingpad.has_cleanup = False
|
||||||
|
|
||||||
|
# we should propagate the clauses to nested try catch blocks
|
||||||
|
# so nested try catch will jump to our clause if the inner one does not
|
||||||
|
# match
|
||||||
|
# note that the phi instruction here requires some hack, see
|
||||||
|
# llvm_ir_generator process_function for details
|
||||||
|
clauses = []
|
||||||
|
found_catch_all = False
|
||||||
|
for handler_node in node.handlers:
|
||||||
|
if found_catch_all:
|
||||||
|
self.warn_unreachable(handler_node)
|
||||||
|
continue
|
||||||
|
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)
|
||||||
|
phi = ir.Phi(builtins.TException(), 'exn')
|
||||||
|
handler.append(phi)
|
||||||
|
clauses.append((handler, exn_type, phi))
|
||||||
|
else:
|
||||||
|
handler = self.add_block("handler.catchall")
|
||||||
|
phi = ir.Phi(builtins.TException(), 'exn')
|
||||||
|
handler.append(phi)
|
||||||
|
clauses.append((handler, None, phi))
|
||||||
|
found_catch_all = True
|
||||||
|
|
||||||
|
all_clauses = clauses[:]
|
||||||
|
for clause in self.catch_clauses:
|
||||||
|
# if the last clause is accept all, do not add further clauses
|
||||||
|
if len(all_clauses) == 0 or all_clauses[-1][1] is not None:
|
||||||
|
all_clauses.append(clause)
|
||||||
|
|
||||||
body = self.add_block("try.body")
|
body = self.add_block("try.body")
|
||||||
self.append(ir.Branch(body))
|
self.append(ir.Branch(body))
|
||||||
self.current_block = body
|
self.current_block = body
|
||||||
|
|
||||||
|
old_unwind, self.unwind_target = self.unwind_target, dispatcher
|
||||||
|
old_clauses, self.catch_clauses = self.catch_clauses, all_clauses
|
||||||
try:
|
try:
|
||||||
old_unwind, self.unwind_target = self.unwind_target, dispatcher
|
|
||||||
self.visit(node.body)
|
self.visit(node.body)
|
||||||
finally:
|
finally:
|
||||||
self.unwind_target = old_unwind
|
self.unwind_target = old_unwind
|
||||||
|
self.catch_clauses = old_clauses
|
||||||
|
|
||||||
if not self.current_block.is_terminated():
|
if not self.current_block.is_terminated():
|
||||||
self.visit(node.orelse)
|
self.visit(node.orelse)
|
||||||
|
@ -723,95 +761,152 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
body = self.current_block
|
body = self.current_block
|
||||||
|
|
||||||
if any(node.finalbody):
|
if any(node.finalbody):
|
||||||
|
# if we have a final block, we should not append clauses to our
|
||||||
|
# landingpad or we will skip the finally block.
|
||||||
|
# when the finally block calls resume, it will unwind to the outer
|
||||||
|
# try catch block automatically
|
||||||
|
all_clauses = clauses
|
||||||
|
# reset targets
|
||||||
if self.break_target:
|
if self.break_target:
|
||||||
self.break_target = old_break
|
self.break_target = old_break
|
||||||
if self.continue_target:
|
if self.continue_target:
|
||||||
self.continue_target = old_continue
|
self.continue_target = old_continue
|
||||||
self.return_target = old_return
|
self.return_target = old_return
|
||||||
|
|
||||||
old_final_branch, self.final_branch = self.final_branch, exception_final_branch
|
if any(node.finalbody) or self.outer_final is not None:
|
||||||
|
# create new unwind target for cleanup
|
||||||
|
final_dispatcher = self.add_block("try.final.dispatch")
|
||||||
|
final_landingpad = ir.LandingPad(cleanup)
|
||||||
|
final_dispatcher.append(final_landingpad)
|
||||||
|
|
||||||
cleanup = self.add_block('handler.cleanup')
|
# make sure that exception clauses are unwinded to the finally block
|
||||||
landingpad = dispatcher.append(ir.LandingPad(cleanup))
|
old_unwind, self.unwind_target = self.unwind_target, final_dispatcher
|
||||||
if not any(node.finalbody):
|
|
||||||
landingpad.has_cleanup = False
|
if any(node.finalbody):
|
||||||
|
redirect = final_branch
|
||||||
|
elif self.outer_final is not None:
|
||||||
|
redirect = self.outer_final
|
||||||
|
else:
|
||||||
|
redirect = lambda dest, proxy: proxy.append(ir.Branch(dest))
|
||||||
|
|
||||||
|
# we need to set break/continue/return to execute end_catch
|
||||||
|
if self.break_target is not None:
|
||||||
|
break_proxy = self.add_block("try.break")
|
||||||
|
break_proxy.append(ir.Builtin("end_catch", [], builtins.TNone()))
|
||||||
|
old_break, self.break_target = self.break_target, break_proxy
|
||||||
|
redirect(old_break, break_proxy)
|
||||||
|
|
||||||
|
if self.continue_target is not None:
|
||||||
|
continue_proxy = self.add_block("try.continue")
|
||||||
|
continue_proxy.append(ir.Builtin("end_catch", [],
|
||||||
|
builtins.TNone()))
|
||||||
|
old_continue, self.continue_target = self.continue_target, continue_proxy
|
||||||
|
redirect(old_continue, continue_proxy)
|
||||||
|
|
||||||
|
return_proxy = self.add_block("try.return")
|
||||||
|
return_proxy.append(ir.Builtin("end_catch", [], builtins.TNone()))
|
||||||
|
old_return, self.return_target = self.return_target, return_proxy
|
||||||
|
old_return_target = old_return
|
||||||
|
if old_return_target is None:
|
||||||
|
old_return_target = self.add_block("try.doreturn")
|
||||||
|
value = old_return_target.append(ir.GetLocal(self.current_private_env, "$return"))
|
||||||
|
old_return_target.append(ir.Return(value))
|
||||||
|
redirect(old_return_target, return_proxy)
|
||||||
|
|
||||||
handlers = []
|
handlers = []
|
||||||
for handler_node in node.handlers:
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
for (handler_node, (handler, exn_type, phi)) in zip(node.handlers, clauses):
|
||||||
self.current_block = handler
|
self.current_block = handler
|
||||||
if handler_node.name is not None:
|
if handler_node.name is not None:
|
||||||
exn = self.append(ir.Builtin("exncast", [landingpad], handler_node.name_type))
|
exn = self.append(ir.Builtin("exncast", [phi], handler_node.name_type))
|
||||||
self._set_local(handler_node.name, exn)
|
self._set_local(handler_node.name, exn)
|
||||||
self.visit(handler_node.body)
|
self.visit(handler_node.body)
|
||||||
|
# only need to call end_catch if the current block is not terminated
|
||||||
|
# other possible paths: break/continue/return/raise
|
||||||
|
# we will call end_catch in the first 3 cases, and we should not
|
||||||
|
# end_catch in the last case for nested exception
|
||||||
|
if not self.current_block.is_terminated():
|
||||||
|
self.append(ir.Builtin("end_catch", [], builtins.TNone()))
|
||||||
post_handler = self.current_block
|
post_handler = self.current_block
|
||||||
|
handlers.append(post_handler)
|
||||||
|
|
||||||
handlers.append((handler, post_handler))
|
# branch to all possible clauses, including those from outer try catch
|
||||||
|
# block
|
||||||
|
# if we have a finally block, all_clauses will not include those from
|
||||||
|
# the outer block
|
||||||
|
for (handler, clause, phi) in all_clauses:
|
||||||
|
phi.add_incoming(landingpad, dispatcher)
|
||||||
|
landingpad.add_clause(handler, clause)
|
||||||
|
|
||||||
|
if self.break_target:
|
||||||
|
self.break_target = old_break
|
||||||
|
if self.continue_target:
|
||||||
|
self.continue_target = old_continue
|
||||||
|
self.return_target = old_return
|
||||||
|
|
||||||
if any(node.finalbody):
|
if any(node.finalbody):
|
||||||
# Finalize and continue after try statement.
|
# Finalize and continue after try statement.
|
||||||
self.final_branch = old_final_branch
|
self.outer_final = old_outer_final
|
||||||
|
self.unwind_target = old_unwind
|
||||||
for (i, (target, block)) in enumerate(zip(final_exn_targets, final_exn_paths)):
|
# Exception path
|
||||||
finalizer = self.add_block(f"finally{i}")
|
finalizer_reraise = self.add_block("finally.resume")
|
||||||
self.current_block = block
|
|
||||||
self.terminate(ir.Branch(finalizer))
|
|
||||||
self.current_block = finalizer
|
|
||||||
self.visit(node.finalbody)
|
|
||||||
self.terminate(ir.Branch(target))
|
|
||||||
|
|
||||||
finalizer = self.add_block("finally")
|
|
||||||
self.current_block = finalizer
|
|
||||||
|
|
||||||
self.visit(node.finalbody)
|
|
||||||
post_finalizer = self.current_block
|
|
||||||
|
|
||||||
# Finalize and reraise. Separate from previous case to expose flow
|
|
||||||
# to LocalAccessValidator.
|
|
||||||
finalizer_reraise = self.add_block("finally.reraise")
|
|
||||||
self.current_block = finalizer_reraise
|
self.current_block = finalizer_reraise
|
||||||
|
|
||||||
self.visit(node.finalbody)
|
self.visit(node.finalbody)
|
||||||
self.terminate(ir.Reraise(self.unwind_target))
|
self.terminate(ir.Resume(self.unwind_target))
|
||||||
|
|
||||||
self.current_block = tail = self.add_block("try.tail")
|
|
||||||
if any(node.finalbody):
|
|
||||||
final_targets.append(tail)
|
|
||||||
|
|
||||||
for block in final_paths:
|
|
||||||
block.append(ir.Branch(finalizer))
|
|
||||||
|
|
||||||
if not body.is_terminated():
|
|
||||||
body.append(ir.SetLocal(final_state, "$cont", tail))
|
|
||||||
body.append(ir.Branch(finalizer))
|
|
||||||
|
|
||||||
cleanup.append(ir.Branch(finalizer_reraise))
|
cleanup.append(ir.Branch(finalizer_reraise))
|
||||||
|
|
||||||
for handler, post_handler in handlers:
|
# Normal path
|
||||||
if not post_handler.is_terminated():
|
finalizer = self.add_block("finally")
|
||||||
post_handler.append(ir.SetLocal(final_state, "$cont", tail))
|
self.current_block = finalizer
|
||||||
post_handler.append(ir.Branch(finalizer))
|
self.visit(node.finalbody)
|
||||||
|
post_finalizer = self.current_block
|
||||||
|
self.current_block = tail = self.add_block("try.tail")
|
||||||
|
final_targets.append(tail)
|
||||||
|
|
||||||
|
# if final block is not terminated, branch to tail
|
||||||
if not post_finalizer.is_terminated():
|
if not post_finalizer.is_terminated():
|
||||||
dest = post_finalizer.append(ir.GetLocal(final_state, "$cont"))
|
dest = post_finalizer.append(ir.GetLocal(final_state, "$cont"))
|
||||||
post_finalizer.append(ir.IndirectBranch(dest, final_targets))
|
post_finalizer.append(ir.IndirectBranch(dest, final_targets))
|
||||||
|
# make sure proxies will branch to finalizer
|
||||||
|
for block in final_paths:
|
||||||
|
if finalizer in block.predecessors():
|
||||||
|
# avoid producing irreducible graphs
|
||||||
|
# generate a new finalizer
|
||||||
|
self.current_block = tmp_finalizer = self.add_block("finally.tmp")
|
||||||
|
self.visit(node.finalbody)
|
||||||
|
if not self.current_block.is_terminated():
|
||||||
|
assert isinstance(block.instructions[-1], ir.SetLocal)
|
||||||
|
self.current_block.append(ir.Branch(block.instructions[-1].operands[-1]))
|
||||||
|
block.instructions[-1].erase()
|
||||||
|
block.append(ir.Branch(tmp_finalizer))
|
||||||
|
self.current_block = tail
|
||||||
|
else:
|
||||||
|
block.append(ir.Branch(finalizer))
|
||||||
|
# if no raise in body/handlers, branch to finalizer
|
||||||
|
for block in chain([body], handlers):
|
||||||
|
if not block.is_terminated():
|
||||||
|
if finalizer in block.predecessors():
|
||||||
|
# similar to the above case
|
||||||
|
self.current_block = tmp_finalizer = self.add_block("finally.tmp")
|
||||||
|
self.visit(node.finalbody)
|
||||||
|
self.terminate(ir.Branch(tail))
|
||||||
|
block.append(ir.Branch(tmp_finalizer))
|
||||||
|
self.current_block = tail
|
||||||
|
else:
|
||||||
|
block.append(ir.SetLocal(final_state, "$cont", tail))
|
||||||
|
block.append(ir.Branch(finalizer))
|
||||||
else:
|
else:
|
||||||
|
if self.outer_final is not None:
|
||||||
|
self.unwind_target = old_unwind
|
||||||
|
self.current_block = tail = self.add_block("try.tail")
|
||||||
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))
|
cleanup.append(ir.Resume(self.unwind_target))
|
||||||
|
|
||||||
for handler, post_handler in handlers:
|
for handler in handlers:
|
||||||
if not post_handler.is_terminated():
|
if not handler.is_terminated():
|
||||||
post_handler.append(ir.Branch(tail))
|
handler.append(ir.Branch(tail))
|
||||||
|
|
||||||
def _try_finally(self, body_gen, finally_gen, name):
|
def _try_finally(self, body_gen, finally_gen, name):
|
||||||
dispatcher = self.add_block("{}.dispatch".format(name))
|
dispatcher = self.add_block("{}.dispatch".format(name))
|
||||||
|
@ -830,7 +925,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||||
self.current_block = self.add_block("{}.cleanup".format(name))
|
self.current_block = self.add_block("{}.cleanup".format(name))
|
||||||
dispatcher.append(ir.LandingPad(self.current_block))
|
dispatcher.append(ir.LandingPad(self.current_block))
|
||||||
finally_gen()
|
finally_gen()
|
||||||
self.raise_exn()
|
self.terminate(ir.Resume(self.unwind_target))
|
||||||
|
|
||||||
self.current_block = self.post_body
|
self.current_block = self.post_body
|
||||||
|
|
||||||
|
|
|
@ -171,11 +171,17 @@ class LLVMIRGenerator:
|
||||||
self.llfunction = None
|
self.llfunction = None
|
||||||
self.llmap = {}
|
self.llmap = {}
|
||||||
self.llobject_map = {}
|
self.llobject_map = {}
|
||||||
|
self.llpred_map = {}
|
||||||
self.phis = []
|
self.phis = []
|
||||||
self.debug_info_emitter = DebugInfoEmitter(self.llmodule)
|
self.debug_info_emitter = DebugInfoEmitter(self.llmodule)
|
||||||
self.empty_metadata = self.llmodule.add_metadata([])
|
self.empty_metadata = self.llmodule.add_metadata([])
|
||||||
self.quote_fail_msg = None
|
self.quote_fail_msg = None
|
||||||
|
|
||||||
|
def add_pred(self, pred, block):
|
||||||
|
if block not in self.llpred_map:
|
||||||
|
self.llpred_map[block] = set()
|
||||||
|
self.llpred_map[block].add(pred)
|
||||||
|
|
||||||
def needs_sret(self, lltyp, may_be_large=True):
|
def needs_sret(self, lltyp, may_be_large=True):
|
||||||
if isinstance(lltyp, ll.VoidType):
|
if isinstance(lltyp, ll.VoidType):
|
||||||
return False
|
return False
|
||||||
|
@ -367,7 +373,9 @@ class LLVMIRGenerator:
|
||||||
llty = ll.FunctionType(lli32, [], var_arg=True)
|
llty = ll.FunctionType(lli32, [], var_arg=True)
|
||||||
elif name == "__artiq_raise":
|
elif name == "__artiq_raise":
|
||||||
llty = ll.FunctionType(llvoid, [self.llty_of_type(builtins.TException())])
|
llty = ll.FunctionType(llvoid, [self.llty_of_type(builtins.TException())])
|
||||||
elif name == "__artiq_reraise":
|
elif name == "__artiq_resume":
|
||||||
|
llty = ll.FunctionType(llvoid, [])
|
||||||
|
elif name == "__artiq_end_catch":
|
||||||
llty = ll.FunctionType(llvoid, [])
|
llty = ll.FunctionType(llvoid, [])
|
||||||
elif name == "memcmp":
|
elif name == "memcmp":
|
||||||
llty = ll.FunctionType(lli32, [llptr, llptr, lli32])
|
llty = ll.FunctionType(lli32, [llptr, llptr, lli32])
|
||||||
|
@ -653,6 +661,28 @@ class LLVMIRGenerator:
|
||||||
self.llbuilder = ll.IRBuilder()
|
self.llbuilder = ll.IRBuilder()
|
||||||
llblock_map = {}
|
llblock_map = {}
|
||||||
|
|
||||||
|
# this is the predecessor map, from basic block to the set of its
|
||||||
|
# predecessors
|
||||||
|
# handling for branch and cbranch is here, and the handling of
|
||||||
|
# indirectbr and landingpad are in their respective process_*
|
||||||
|
# function
|
||||||
|
self.llpred_map = llpred_map = {}
|
||||||
|
branch_fn = self.llbuilder.branch
|
||||||
|
cbranch_fn = self.llbuilder.cbranch
|
||||||
|
def override_branch(block):
|
||||||
|
nonlocal self, branch_fn
|
||||||
|
self.add_pred(self.llbuilder.basic_block, block)
|
||||||
|
return branch_fn(block)
|
||||||
|
|
||||||
|
def override_cbranch(pred, bbif, bbelse):
|
||||||
|
nonlocal self, cbranch_fn
|
||||||
|
self.add_pred(self.llbuilder.basic_block, bbif)
|
||||||
|
self.add_pred(self.llbuilder.basic_block, bbelse)
|
||||||
|
return cbranch_fn(pred, bbif, bbelse)
|
||||||
|
|
||||||
|
self.llbuilder.branch = override_branch
|
||||||
|
self.llbuilder.cbranch = override_cbranch
|
||||||
|
|
||||||
if not func.is_generated:
|
if not func.is_generated:
|
||||||
lldisubprogram = self.debug_info_emitter.emit_subprogram(func, self.llfunction)
|
lldisubprogram = self.debug_info_emitter.emit_subprogram(func, self.llfunction)
|
||||||
self.llfunction.set_metadata('dbg', lldisubprogram)
|
self.llfunction.set_metadata('dbg', lldisubprogram)
|
||||||
|
@ -675,6 +705,10 @@ class LLVMIRGenerator:
|
||||||
# Third, translate all instructions.
|
# Third, translate all instructions.
|
||||||
for block in func.basic_blocks:
|
for block in func.basic_blocks:
|
||||||
self.llbuilder.position_at_end(self.llmap[block])
|
self.llbuilder.position_at_end(self.llmap[block])
|
||||||
|
old_block = None
|
||||||
|
if len(block.instructions) == 1 and \
|
||||||
|
isinstance(block.instructions[0], ir.LandingPad):
|
||||||
|
old_block = self.llbuilder.basic_block
|
||||||
for insn in block.instructions:
|
for insn in block.instructions:
|
||||||
if insn.loc is not None and not func.is_generated:
|
if insn.loc is not None and not func.is_generated:
|
||||||
self.llbuilder.debug_metadata = \
|
self.llbuilder.debug_metadata = \
|
||||||
|
@ -689,12 +723,28 @@ class LLVMIRGenerator:
|
||||||
# instruction so that the result spans several LLVM basic
|
# instruction so that the result spans several LLVM basic
|
||||||
# blocks. This only really matters for phis, which are thus
|
# blocks. This only really matters for phis, which are thus
|
||||||
# using a different map (the following one).
|
# using a different map (the following one).
|
||||||
llblock_map[block] = self.llbuilder.basic_block
|
if old_block is None:
|
||||||
|
llblock_map[block] = self.llbuilder.basic_block
|
||||||
|
else:
|
||||||
|
llblock_map[block] = old_block
|
||||||
|
|
||||||
# Fourth, add incoming values to phis.
|
# Fourth, add incoming values to phis.
|
||||||
for phi, llphi in self.phis:
|
for phi, llphi in self.phis:
|
||||||
for value, block in phi.incoming():
|
for value, block in phi.incoming():
|
||||||
llphi.add_incoming(self.map(value), llblock_map[block])
|
if isinstance(phi.type, builtins.TException):
|
||||||
|
# a hack to patch phi from landingpad
|
||||||
|
# because landingpad is a single bb in artiq IR, but
|
||||||
|
# generates multiple bb, we need to find out the
|
||||||
|
# predecessor to figure out the actual bb
|
||||||
|
landingpad = llblock_map[block]
|
||||||
|
for pred in llpred_map[llphi.parent]:
|
||||||
|
if pred in llpred_map and landingpad in llpred_map[pred]:
|
||||||
|
llphi.add_incoming(self.map(value), pred)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
llphi.add_incoming(self.map(value), landingpad)
|
||||||
|
else:
|
||||||
|
llphi.add_incoming(self.map(value), llblock_map[block])
|
||||||
finally:
|
finally:
|
||||||
self.function_flags = None
|
self.function_flags = None
|
||||||
self.llfunction = None
|
self.llfunction = None
|
||||||
|
@ -1247,6 +1297,8 @@ class LLVMIRGenerator:
|
||||||
return llstore_lo
|
return llstore_lo
|
||||||
else:
|
else:
|
||||||
return self.llbuilder.call(self.llbuiltin("delay_mu"), [llinterval])
|
return self.llbuilder.call(self.llbuiltin("delay_mu"), [llinterval])
|
||||||
|
elif insn.op == "end_catch":
|
||||||
|
return self.llbuilder.call(self.llbuiltin("__artiq_end_catch"), [])
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
|
@ -1678,7 +1730,12 @@ class LLVMIRGenerator:
|
||||||
def process_IndirectBranch(self, insn):
|
def process_IndirectBranch(self, insn):
|
||||||
llinsn = self.llbuilder.branch_indirect(self.map(insn.target()))
|
llinsn = self.llbuilder.branch_indirect(self.map(insn.target()))
|
||||||
for dest in insn.destinations():
|
for dest in insn.destinations():
|
||||||
llinsn.add_destination(self.map(dest))
|
dest = self.map(dest)
|
||||||
|
self.add_pred(self.llbuilder.basic_block, dest)
|
||||||
|
if dest not in self.llpred_map:
|
||||||
|
self.llpred_map[dest] = set()
|
||||||
|
self.llpred_map[dest].add(self.llbuilder.basic_block)
|
||||||
|
llinsn.add_destination(dest)
|
||||||
return llinsn
|
return llinsn
|
||||||
|
|
||||||
def process_Return(self, insn):
|
def process_Return(self, insn):
|
||||||
|
@ -1716,8 +1773,8 @@ class LLVMIRGenerator:
|
||||||
llexn = self.map(insn.value())
|
llexn = self.map(insn.value())
|
||||||
return self._gen_raise(insn, self.llbuiltin("__artiq_raise"), [llexn])
|
return self._gen_raise(insn, self.llbuiltin("__artiq_raise"), [llexn])
|
||||||
|
|
||||||
def process_Reraise(self, insn):
|
def process_Resume(self, insn):
|
||||||
return self._gen_raise(insn, self.llbuiltin("__artiq_reraise"), [])
|
return self._gen_raise(insn, self.llbuiltin("__artiq_resume"), [])
|
||||||
|
|
||||||
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*}
|
||||||
|
@ -1726,10 +1783,11 @@ class LLVMIRGenerator:
|
||||||
cleanup=insn.has_cleanup)
|
cleanup=insn.has_cleanup)
|
||||||
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)],
|
llexnidptr = self.llbuilder.gep(llexn, [self.llindex(0), self.llindex(0)],
|
||||||
inbounds=True)
|
inbounds=True)
|
||||||
llexnname = self.llbuilder.load(llexnnameptr)
|
llexnid = self.llbuilder.load(llexnidptr)
|
||||||
|
|
||||||
|
landingpadbb = self.llbuilder.basic_block
|
||||||
for target, typ in insn.clauses():
|
for target, typ in insn.clauses():
|
||||||
if typ is None:
|
if typ is None:
|
||||||
# we use a null pointer here, similar to how cpp does it
|
# we use a null pointer here, similar to how cpp does it
|
||||||
|
@ -1742,42 +1800,40 @@ class LLVMIRGenerator:
|
||||||
ll.Constant(lli32, 0).inttoptr(llptr)
|
ll.Constant(lli32, 0).inttoptr(llptr)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
exnname = "{}:{}".format(typ.id, typ.name)
|
|
||||||
|
|
||||||
llclauseexnname = self.llconst_of_const(
|
|
||||||
ir.Constant(exnname, builtins.TStr()))
|
|
||||||
llclauseexnnameptr = self.llmodule.globals.get("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:
|
|
||||||
# typ is None means that we match all exceptions, so no need to
|
# typ is None means that we match all exceptions, so no need to
|
||||||
# compare
|
# compare
|
||||||
self.llbuilder.branch(self.map(target))
|
target = self.map(target)
|
||||||
|
self.add_pred(landingpadbb, target)
|
||||||
|
self.add_pred(landingpadbb, self.llbuilder.basic_block)
|
||||||
|
self.llbuilder.branch(target)
|
||||||
else:
|
else:
|
||||||
llexnlen = self.llbuilder.extract_value(llexnname, 1)
|
exnname = "{}:{}".format(typ.id, typ.name)
|
||||||
llclauseexnlen = self.llbuilder.extract_value(llclauseexnname, 1)
|
llclauseexnidptr = self.llmodule.globals.get("exn.{}".format(exnname))
|
||||||
llmatchinglen = self.llbuilder.icmp_unsigned('==', llexnlen, llclauseexnlen)
|
exnid = ll.Constant(lli32, self.embedding_map.store_str(exnname))
|
||||||
with self.llbuilder.if_then(llmatchinglen):
|
if llclauseexnidptr is None:
|
||||||
llexnptr = self.llbuilder.extract_value(llexnname, 0)
|
llclauseexnidptr = ll.GlobalVariable(self.llmodule, lli32,
|
||||||
llclauseexnptr = self.llbuilder.extract_value(llclauseexnname, 0)
|
name="exn.{}".format(exnname))
|
||||||
llcomparedata = self.llbuilder.call(self.llbuiltin("memcmp"),
|
llclauseexnidptr.global_constant = True
|
||||||
[llexnptr, llclauseexnptr, llexnlen])
|
llclauseexnidptr.initializer = exnid
|
||||||
llmatchingdata = self.llbuilder.icmp_unsigned('==', llcomparedata,
|
llclauseexnidptr.linkage = "private"
|
||||||
ll.Constant(lli32, 0))
|
llclauseexnidptr.unnamed_addr = True
|
||||||
with self.llbuilder.if_then(llmatchingdata):
|
lllandingpad.add_clause(ll.CatchClause(llclauseexnidptr))
|
||||||
self.llbuilder.branch(self.map(target))
|
llmatchingdata = self.llbuilder.icmp_unsigned("==", llexnid,
|
||||||
|
exnid)
|
||||||
|
with self.llbuilder.if_then(llmatchingdata):
|
||||||
|
target = self.map(target)
|
||||||
|
self.add_pred(landingpadbb, target)
|
||||||
|
self.add_pred(landingpadbb, self.llbuilder.basic_block)
|
||||||
|
self.llbuilder.branch(target)
|
||||||
|
self.add_pred(landingpadbb, self.llbuilder.basic_block)
|
||||||
|
|
||||||
if self.llbuilder.basic_block.terminator is None:
|
if self.llbuilder.basic_block.terminator is None:
|
||||||
if insn.has_cleanup:
|
if insn.has_cleanup:
|
||||||
self.llbuilder.branch(self.map(insn.cleanup()))
|
target = self.map(insn.cleanup())
|
||||||
|
self.add_pred(landingpadbb, target)
|
||||||
|
self.add_pred(landingpadbb, self.llbuilder.basic_block)
|
||||||
|
self.llbuilder.branch(target)
|
||||||
else:
|
else:
|
||||||
self.llbuilder.resume(lllandingpad)
|
self.llbuilder.resume(lllandingpad)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue