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:
|
||||
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)
|
||||
|
||||
def opcode(self):
|
||||
return "reraise"
|
||||
return "resume"
|
||||
|
||||
def exception_target(self):
|
||||
if len(self.operands) > 0:
|
||||
|
@ -8,6 +8,7 @@ semantics explicitly.
|
||||
|
||||
from collections import OrderedDict, defaultdict
|
||||
from functools import reduce
|
||||
from itertools import chain
|
||||
from pythonparser import algorithm, diagnostic, ast
|
||||
from .. import types, builtins, asttyped, ir, iodelay
|
||||
|
||||
@ -61,6 +62,9 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
the basic block to which ``return`` will transfer control
|
||||
:ivar unwind_target: (:class:`ir.BasicBlock` or None)
|
||||
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)
|
||||
or None)
|
||||
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_args = None
|
||||
self.current_assign = None
|
||||
self.current_exception = None
|
||||
self.break_target = None
|
||||
self.continue_target = None
|
||||
self.return_target = None
|
||||
self.unwind_target = None
|
||||
self.catch_clauses = []
|
||||
self.outer_final = None
|
||||
self.final_branch = None
|
||||
self.function_map = dict()
|
||||
self.variable_map = dict()
|
||||
@ -650,9 +657,9 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
self.append(ir.Raise(exn))
|
||||
else:
|
||||
if self.unwind_target is not None:
|
||||
self.append(ir.Reraise(self.unwind_target))
|
||||
self.append(ir.Resume(self.unwind_target))
|
||||
else:
|
||||
self.append(ir.Reraise())
|
||||
self.append(ir.Resume())
|
||||
|
||||
def visit_Raise(self, node):
|
||||
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):
|
||||
dispatcher = self.add_block("try.dispatch")
|
||||
cleanup = self.add_block('handler.cleanup')
|
||||
landingpad = ir.LandingPad(cleanup)
|
||||
dispatcher.append(landingpad)
|
||||
|
||||
if any(node.finalbody):
|
||||
# k for continuation
|
||||
@ -677,15 +687,6 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
final_targets.append(target)
|
||||
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:
|
||||
break_proxy = self.add_block("try.break")
|
||||
old_break, self.break_target = self.break_target, break_proxy
|
||||
@ -706,15 +707,52 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
return_action.append(ir.Return(value))
|
||||
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")
|
||||
self.append(ir.Branch(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:
|
||||
old_unwind, self.unwind_target = self.unwind_target, dispatcher
|
||||
self.visit(node.body)
|
||||
finally:
|
||||
self.unwind_target = old_unwind
|
||||
self.catch_clauses = old_clauses
|
||||
|
||||
if not self.current_block.is_terminated():
|
||||
self.visit(node.orelse)
|
||||
@ -723,95 +761,152 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
body = self.current_block
|
||||
|
||||
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:
|
||||
self.break_target = old_break
|
||||
if self.continue_target:
|
||||
self.continue_target = old_continue
|
||||
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')
|
||||
landingpad = dispatcher.append(ir.LandingPad(cleanup))
|
||||
if not any(node.finalbody):
|
||||
landingpad.has_cleanup = False
|
||||
# make sure that exception clauses are unwinded to the finally block
|
||||
old_unwind, self.unwind_target = self.unwind_target, final_dispatcher
|
||||
|
||||
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 = []
|
||||
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
|
||||
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.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
|
||||
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):
|
||||
# Finalize and continue after try statement.
|
||||
self.final_branch = old_final_branch
|
||||
|
||||
for (i, (target, block)) in enumerate(zip(final_exn_targets, final_exn_paths)):
|
||||
finalizer = self.add_block(f"finally{i}")
|
||||
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.outer_final = old_outer_final
|
||||
self.unwind_target = old_unwind
|
||||
# Exception path
|
||||
finalizer_reraise = self.add_block("finally.resume")
|
||||
self.current_block = finalizer_reraise
|
||||
|
||||
self.visit(node.finalbody)
|
||||
self.terminate(ir.Reraise(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))
|
||||
|
||||
self.terminate(ir.Resume(self.unwind_target))
|
||||
cleanup.append(ir.Branch(finalizer_reraise))
|
||||
|
||||
for handler, post_handler in handlers:
|
||||
if not post_handler.is_terminated():
|
||||
post_handler.append(ir.SetLocal(final_state, "$cont", tail))
|
||||
post_handler.append(ir.Branch(finalizer))
|
||||
# Normal path
|
||||
finalizer = self.add_block("finally")
|
||||
self.current_block = 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():
|
||||
dest = post_finalizer.append(ir.GetLocal(final_state, "$cont"))
|
||||
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:
|
||||
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():
|
||||
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:
|
||||
if not post_handler.is_terminated():
|
||||
post_handler.append(ir.Branch(tail))
|
||||
for handler in handlers:
|
||||
if not handler.is_terminated():
|
||||
handler.append(ir.Branch(tail))
|
||||
|
||||
def _try_finally(self, body_gen, finally_gen, 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))
|
||||
dispatcher.append(ir.LandingPad(self.current_block))
|
||||
finally_gen()
|
||||
self.raise_exn()
|
||||
self.terminate(ir.Resume(self.unwind_target))
|
||||
|
||||
self.current_block = self.post_body
|
||||
|
||||
|
@ -171,11 +171,17 @@ class LLVMIRGenerator:
|
||||
self.llfunction = None
|
||||
self.llmap = {}
|
||||
self.llobject_map = {}
|
||||
self.llpred_map = {}
|
||||
self.phis = []
|
||||
self.debug_info_emitter = DebugInfoEmitter(self.llmodule)
|
||||
self.empty_metadata = self.llmodule.add_metadata([])
|
||||
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):
|
||||
if isinstance(lltyp, ll.VoidType):
|
||||
return False
|
||||
@ -367,7 +373,9 @@ class LLVMIRGenerator:
|
||||
llty = ll.FunctionType(lli32, [], var_arg=True)
|
||||
elif name == "__artiq_raise":
|
||||
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, [])
|
||||
elif name == "memcmp":
|
||||
llty = ll.FunctionType(lli32, [llptr, llptr, lli32])
|
||||
@ -653,6 +661,28 @@ class LLVMIRGenerator:
|
||||
self.llbuilder = ll.IRBuilder()
|
||||
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:
|
||||
lldisubprogram = self.debug_info_emitter.emit_subprogram(func, self.llfunction)
|
||||
self.llfunction.set_metadata('dbg', lldisubprogram)
|
||||
@ -675,6 +705,10 @@ class LLVMIRGenerator:
|
||||
# Third, translate all instructions.
|
||||
for block in func.basic_blocks:
|
||||
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:
|
||||
if insn.loc is not None and not func.is_generated:
|
||||
self.llbuilder.debug_metadata = \
|
||||
@ -689,12 +723,28 @@ class LLVMIRGenerator:
|
||||
# instruction so that the result spans several LLVM basic
|
||||
# blocks. This only really matters for phis, which are thus
|
||||
# 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.
|
||||
for phi, llphi in self.phis:
|
||||
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:
|
||||
self.function_flags = None
|
||||
self.llfunction = None
|
||||
@ -1247,6 +1297,8 @@ class LLVMIRGenerator:
|
||||
return llstore_lo
|
||||
else:
|
||||
return self.llbuilder.call(self.llbuiltin("delay_mu"), [llinterval])
|
||||
elif insn.op == "end_catch":
|
||||
return self.llbuilder.call(self.llbuiltin("__artiq_end_catch"), [])
|
||||
else:
|
||||
assert False
|
||||
|
||||
@ -1678,7 +1730,12 @@ class LLVMIRGenerator:
|
||||
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))
|
||||
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
|
||||
|
||||
def process_Return(self, insn):
|
||||
@ -1716,8 +1773,8 @@ class LLVMIRGenerator:
|
||||
llexn = self.map(insn.value())
|
||||
return self._gen_raise(insn, self.llbuiltin("__artiq_raise"), [llexn])
|
||||
|
||||
def process_Reraise(self, insn):
|
||||
return self._gen_raise(insn, self.llbuiltin("__artiq_reraise"), [])
|
||||
def process_Resume(self, insn):
|
||||
return self._gen_raise(insn, self.llbuiltin("__artiq_resume"), [])
|
||||
|
||||
def process_LandingPad(self, insn):
|
||||
# Layout on return from landing pad: {%_Unwind_Exception*, %Exception*}
|
||||
@ -1726,10 +1783,11 @@ class LLVMIRGenerator:
|
||||
cleanup=insn.has_cleanup)
|
||||
llrawexn = self.llbuilder.extract_value(lllandingpad, 1)
|
||||
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)
|
||||
llexnname = self.llbuilder.load(llexnnameptr)
|
||||
llexnid = self.llbuilder.load(llexnidptr)
|
||||
|
||||
landingpadbb = self.llbuilder.basic_block
|
||||
for target, typ in insn.clauses():
|
||||
if typ is None:
|
||||
# we use a null pointer here, similar to how cpp does it
|
||||
@ -1742,42 +1800,40 @@ class LLVMIRGenerator:
|
||||
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
|
||||
# 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:
|
||||
llexnlen = self.llbuilder.extract_value(llexnname, 1)
|
||||
llclauseexnlen = self.llbuilder.extract_value(llclauseexnname, 1)
|
||||
llmatchinglen = self.llbuilder.icmp_unsigned('==', llexnlen, llclauseexnlen)
|
||||
with self.llbuilder.if_then(llmatchinglen):
|
||||
llexnptr = self.llbuilder.extract_value(llexnname, 0)
|
||||
llclauseexnptr = self.llbuilder.extract_value(llclauseexnname, 0)
|
||||
llcomparedata = self.llbuilder.call(self.llbuiltin("memcmp"),
|
||||
[llexnptr, llclauseexnptr, llexnlen])
|
||||
llmatchingdata = self.llbuilder.icmp_unsigned('==', llcomparedata,
|
||||
ll.Constant(lli32, 0))
|
||||
with self.llbuilder.if_then(llmatchingdata):
|
||||
self.llbuilder.branch(self.map(target))
|
||||
exnname = "{}:{}".format(typ.id, typ.name)
|
||||
llclauseexnidptr = self.llmodule.globals.get("exn.{}".format(exnname))
|
||||
exnid = ll.Constant(lli32, self.embedding_map.store_str(exnname))
|
||||
if llclauseexnidptr is None:
|
||||
llclauseexnidptr = ll.GlobalVariable(self.llmodule, lli32,
|
||||
name="exn.{}".format(exnname))
|
||||
llclauseexnidptr.global_constant = True
|
||||
llclauseexnidptr.initializer = exnid
|
||||
llclauseexnidptr.linkage = "private"
|
||||
llclauseexnidptr.unnamed_addr = True
|
||||
lllandingpad.add_clause(ll.CatchClause(llclauseexnidptr))
|
||||
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 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:
|
||||
self.llbuilder.resume(lllandingpad)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user