From 6f11fa6bb1a9eb0dcd51d065532429f90b5a6e11 Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 21 Jul 2015 04:54:34 +0300 Subject: [PATCH] Add conversion to LLVM IR (except handling of exception handling). --- artiq/compiler/ir.py | 10 +- artiq/compiler/module.py | 11 +- artiq/compiler/testbench/irgen.py | 2 +- artiq/compiler/testbench/llvmgen.py | 18 + artiq/compiler/transforms/__init__.py | 3 +- ...{ir_generator.py => artiq_ir_generator.py} | 14 +- artiq/compiler/transforms/inferencer.py | 5 +- .../compiler/transforms/llvm_ir_generator.py | 408 ++++++++++++++++++ 8 files changed, 456 insertions(+), 15 deletions(-) create mode 100644 artiq/compiler/testbench/llvmgen.py rename artiq/compiler/transforms/{ir_generator.py => artiq_ir_generator.py} (99%) create mode 100644 artiq/compiler/transforms/llvm_ir_generator.py diff --git a/artiq/compiler/ir.py b/artiq/compiler/ir.py index baa753647..9d55cb069 100644 --- a/artiq/compiler/ir.py +++ b/artiq/compiler/ir.py @@ -19,10 +19,16 @@ class TBasicBlock(types.TMono): def __init__(self): super().__init__("label") +def is_basic_block(typ): + return isinstance(typ, TBasicBlock) + class TOption(types.TMono): def __init__(self, inner): super().__init__("option", {"inner": inner}) +def is_option(typ): + return isinstance(typ, TOption) + class Value: """ An SSA value that keeps track of its uses. @@ -571,7 +577,7 @@ class GetAttr(Instruction): def opcode(self): return "getattr({})".format(repr(self.attr)) - def env(self): + def object(self): return self.operands[0] class SetAttr(Instruction): @@ -600,7 +606,7 @@ class SetAttr(Instruction): def opcode(self): return "setattr({})".format(repr(self.attr)) - def env(self): + def object(self): return self.operands[0] def value(self): diff --git a/artiq/compiler/module.py b/artiq/compiler/module.py index 22ae58577..e1eb53be6 100644 --- a/artiq/compiler/module.py +++ b/artiq/compiler/module.py @@ -18,9 +18,10 @@ class Module: int_monomorphizer = transforms.IntMonomorphizer(engine=engine) monomorphism_validator = validators.MonomorphismValidator(engine=engine) escape_validator = validators.EscapeValidator(engine=engine) - ir_generator = transforms.IRGenerator(engine=engine, module_name=self.name) + artiq_ir_generator = transforms.ARTIQIRGenerator(engine=engine, module_name=self.name) dead_code_eliminator = transforms.DeadCodeEliminator(engine=engine) local_access_validator = validators.LocalAccessValidator(engine=engine) + llvm_ir_generator = transforms.LLVMIRGenerator(engine=engine, module_name=self.name) self.parsetree, self.comments = parse_buffer(source_buffer, engine=engine) self.typedtree = asttyped_rewriter.visit(self.parsetree) @@ -30,9 +31,11 @@ class Module: inferencer.visit(self.typedtree) monomorphism_validator.visit(self.typedtree) escape_validator.visit(self.typedtree) - self.ir = ir_generator.visit(self.typedtree) - dead_code_eliminator.process(self.ir) - local_access_validator.process(self.ir) + self.artiq_ir = artiq_ir_generator.visit(self.typedtree) + dead_code_eliminator.process(self.artiq_ir) + local_access_validator.process(self.artiq_ir) + llvm_ir_generator.process(self.artiq_ir) + self.llvm_ir = llvm_ir_generator.llmodule @classmethod def from_string(cls, source_string, name="input.py", first_line=1, engine=None): diff --git a/artiq/compiler/testbench/irgen.py b/artiq/compiler/testbench/irgen.py index f2701a301..20e9334ca 100644 --- a/artiq/compiler/testbench/irgen.py +++ b/artiq/compiler/testbench/irgen.py @@ -12,7 +12,7 @@ def main(): engine.process = process_diagnostic mod = Module.from_string("".join(fileinput.input()).expandtabs(), engine=engine) - for fn in mod.ir: + for fn in mod.artiq_ir: print(fn) if __name__ == "__main__": diff --git a/artiq/compiler/testbench/llvmgen.py b/artiq/compiler/testbench/llvmgen.py new file mode 100644 index 000000000..bb3d6ec3d --- /dev/null +++ b/artiq/compiler/testbench/llvmgen.py @@ -0,0 +1,18 @@ +import sys, fileinput +from pythonparser import diagnostic +from .. import Module + +def main(): + def process_diagnostic(diag): + print("\n".join(diag.render())) + if diag.level in ("fatal", "error"): + exit(1) + + engine = diagnostic.Engine() + engine.process = process_diagnostic + + mod = Module.from_string("".join(fileinput.input()).expandtabs(), engine=engine) + print(mod.llvm_ir) + +if __name__ == "__main__": + main() diff --git a/artiq/compiler/transforms/__init__.py b/artiq/compiler/transforms/__init__.py index 64dca0906..576ec7e10 100644 --- a/artiq/compiler/transforms/__init__.py +++ b/artiq/compiler/transforms/__init__.py @@ -1,5 +1,6 @@ from .asttyped_rewriter import ASTTypedRewriter from .inferencer import Inferencer from .int_monomorphizer import IntMonomorphizer -from .ir_generator import IRGenerator +from .artiq_ir_generator import ARTIQIRGenerator from .dead_code_eliminator import DeadCodeEliminator +from .llvm_ir_generator import LLVMIRGenerator diff --git a/artiq/compiler/transforms/ir_generator.py b/artiq/compiler/transforms/artiq_ir_generator.py similarity index 99% rename from artiq/compiler/transforms/ir_generator.py rename to artiq/compiler/transforms/artiq_ir_generator.py index 776a226d0..77f90b336 100644 --- a/artiq/compiler/transforms/ir_generator.py +++ b/artiq/compiler/transforms/artiq_ir_generator.py @@ -1,5 +1,5 @@ """ -:class:`IRGenerator` transforms typed AST into ARTIQ intermediate +:class:`ARTIQIRGenerator` transforms typed AST into ARTIQ intermediate representation. ARTIQ IR is designed to be low-level enough that its operations are elementary--contain no internal branching-- but without too much detail, such as exposing the reference/value @@ -25,9 +25,9 @@ def _extract_loc(node): # We put some effort in keeping generated IR readable, # i.e. with a more or less linear correspondence to the source. # This is why basic blocks sometimes seem to be produced in an odd order. -class IRGenerator(algorithm.Visitor): +class ARTIQIRGenerator(algorithm.Visitor): """ - :class:`IRGenerator` contains a lot of internal state, + :class:`ARTIQIRGenerator` contains a lot of internal state, which is effectively maintained in a stack--with push/pop pairs around any state updates. It is comprised of following: @@ -255,8 +255,7 @@ class IRGenerator(algorithm.Visitor): def visit_Pass(self, node): # Insert a dummy instruction so that analyses which extract # locations from CFG have something to use. - self.append(ir.Arith(ast.Add(loc=None), - ir.Constant(0, self._size_type), ir.Constant(0, self._size_type))) + self.append(ir.Builtin("nop", [], builtins.TNone())) def visit_Assign(self, node): try: @@ -367,12 +366,13 @@ class IRGenerator(algorithm.Visitor): try: iterable = self.visit(node.iter) length = self._iterable_len(iterable) + prehead = self.current_block head = self.add_block("for.head") self.append(ir.Branch(head)) self.current_block = head phi = self.append(ir.Phi(length.type)) - phi.add_incoming(ir.Constant(0, phi.type), head) + phi.add_incoming(ir.Constant(0, phi.type), prehead) cond = self.append(ir.Compare(ast.Lt(loc=None), phi, length)) break_block = self.add_block("for.break") @@ -842,6 +842,8 @@ class IRGenerator(algorithm.Visitor): def visit_BinOpT(self, node): if builtins.is_numeric(node.type): + # TODO: check for division by zero + # TODO: check for shift by too many bits return self.append(ir.Arith(node.op, self.visit(node.left), self.visit(node.right))) elif isinstance(node.op, ast.Add): # list + list, tuple + tuple lhs, rhs = self.visit(node.left), self.visit(node.right) diff --git a/artiq/compiler/transforms/inferencer.py b/artiq/compiler/transforms/inferencer.py index 2e34c4e57..3dd95aee0 100644 --- a/artiq/compiler/transforms/inferencer.py +++ b/artiq/compiler/transforms/inferencer.py @@ -335,9 +335,12 @@ class Inferencer(algorithm.Visitor): return list_.type, left.type, right.type else: return self._coerce_numeric((left, right), lambda typ: (typ, typ, typ)) - elif isinstance(op, (ast.Div, ast.FloorDiv, ast.Mod, ast.Pow, ast.Sub)): + elif isinstance(op, (ast.FloorDiv, ast.Mod, ast.Pow, ast.Sub)): # numeric operators work on any kind of number return self._coerce_numeric((left, right), lambda typ: (typ, typ, typ)) + elif isinstance(op, ast.Div): + # division always returns a float + return self._coerce_numeric((left, right), lambda typ: (builtins.TFloat(), typ, typ)) else: # MatMult diag = diagnostic.Diagnostic("error", "operator '{op}' is not supported", {"op": op.loc.source()}, diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py new file mode 100644 index 000000000..416c87a1f --- /dev/null +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -0,0 +1,408 @@ +""" +:class:`LLVMIRGenerator` transforms ARTIQ intermediate representation +into LLVM intermediate representation. +""" + +import llvmlite.ir as ll +from pythonparser import ast +from .. import types, builtins, ir + +class LLVMIRGenerator: + def __init__(self, engine, module_name, context=ll.Context()): + self.engine = engine + self.llcontext = context + self.llmodule = ll.Module(context=self.llcontext, name=module_name) + self.llfunction = None + self.llmap = {} + self.fixups = [] + + def llty_of_type(self, typ, for_alloc=False, for_return=False): + if types.is_tuple(typ): + return ll.LiteralStructType([self.llty_of_type(eltty) for eltty in typ.elts]) + elif types.is_function(typ): + envarg = ll.IntType(8).as_pointer + llty = ll.FunctionType(args=[envarg] + + [self.llty_of_type(typ.args[arg]) + for arg in typ.args] + + [self.llty_of_type(ir.TOption(typ.optargs[arg])) + for arg in typ.optargs], + return_type=self.llty_of_type(typ.ret, for_return=True)) + return llty.as_pointer() + elif builtins.is_none(typ): + if for_return: + return ll.VoidType() + else: + return ll.LiteralStructType([]) + elif builtins.is_bool(typ): + return ll.IntType(1) + elif builtins.is_int(typ): + return ll.IntType(builtins.get_int_width(typ)) + elif builtins.is_float(typ): + return ll.DoubleType() + elif builtins.is_list(typ): + lleltty = self.llty_of_type(builtins.get_iterable_elt(typ)) + return ll.LiteralStructType([ll.IntType(32), lleltty.as_pointer()]) + 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() + elif ir.is_option(typ): + return ll.LiteralStructType([ll.IntType(1), self.llty_of_type(typ.params["inner"])]) + elif ir.is_environment(typ): + llty = ll.LiteralStructType([self.llty_of_type(typ.params[name]) + for name in typ.params]) + if for_alloc: + return llty + else: + return llty.as_pointer() + else: + assert False + + def llconst_of_const(self, const): + llty = self.llty_of_type(const.type) + if const.value is None: + return ll.Constant(llty, []) + elif const.value is True: + return ll.Constant(llty, True) + elif const.value is False: + return ll.Constant(llty, False) + elif isinstance(const.value, (int, float)): + return ll.Constant(llty, const.value) + else: + assert False + + def map(self, value): + if isinstance(value, (ir.Instruction, ir.BasicBlock)): + return self.llmap[value] + elif isinstance(value, ir.Constant): + return self.llconst_of_const(value) + else: + assert False + + def process(self, functions): + for func in functions: + self.process_function(func) + + def process_function(self, func): + llargtys = [] + for arg in func.arguments: + llargtys.append(self.llty_of_type(arg.type)) + llfunty = ll.FunctionType(args=llargtys, + return_type=self.llty_of_type(func.type.ret, for_return=True)) + + try: + self.llfunction = ll.Function(self.llmodule, llfunty, func.name) + self.llmap = {} + self.llbuilder = ll.IRBuilder() + self.fixups = [] + + # First, create all basic blocks. + for block in func.basic_blocks: + llblock = self.llfunction.append_basic_block(block.name) + self.llmap[block] = llblock + + # Second, translate all instructions. + for block in func.basic_blocks: + self.llbuilder.position_at_end(self.llmap[block]) + for insn in block.instructions: + llinsn = getattr(self, "process_" + type(insn).__name__)(insn) + assert llinsn is not None + self.llmap[insn] = llinsn + + # Third, fixup phis. + for fixup in self.fixups: + fixup() + finally: + self.llfunction = None + self.llmap = None + self.fixups = [] + + def process_Phi(self, insn): + llinsn = self.llbuilder.phi(self.llty_of_type(insn.type), name=insn.name) + def fixup(): + for value, block in insn.incoming(): + llinsn.add_incoming(self.map(value), self.map(block)) + self.fixups.append(fixup) + return llinsn + + def process_Alloc(self, insn): + if ir.is_environment(insn.type): + return self.llbuilder.alloca(self.llty_of_type(insn.type, for_alloc=True), + name=insn.name) + elif builtins.is_list(insn.type): + llsize = self.map(insn.operands[0]) + llvalue = ll.Constant(self.llty_of_type(insn.type), ll.Undefined) + llvalue = self.llbuilder.insert_value(llvalue, llsize, 0) + llalloc = self.llbuilder.alloca(self.llty_of_type(builtins.get_iterable_elt(insn.type)), + size=llsize) + llvalue = self.llbuilder.insert_value(llvalue, llalloc, 1, name=insn.name) + return llvalue + elif builtins.is_mutable(insn.type): + assert False + else: # immutable + llvalue = ll.Constant(self.llty_of_type(insn.type), ll.Undefined) + for index, elt in enumerate(insn.operands): + llvalue = self.llbuilder.insert_value(llvalue, self.map(elt), index) + 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) + return self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(var_index)]) + else: + outer_index = list(env_ty.params.keys()).index(".outer") + llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)]) + llouterenv = self.llbuilder.load(llptr) + return self.llptr_to_var(llouterenv, env_ty.params[".outer"], var_name) + + def process_GetLocal(self, insn): + env = insn.environment() + llptr = self.llptr_to_var(self.map(env), env.type, insn.var_name) + return self.llbuilder.load(llptr) + + def process_SetLocal(self, insn): + env = insn.environment() + llptr = self.llptr_to_var(self.map(env), env.type, insn.var_name) + return self.llbuilder.store(self.map(insn.value()), llptr) + + def attr_index(self, insn): + return list(insn.object().type.attributes.keys()).index(insn.attr) + + def process_GetAttr(self, insn): + if types.is_tuple(insn.object().type): + return self.llbuilder.extract_value(self.map(insn.object()), self.attr_index(insn), + name=insn.name) + elif not builtins.is_mutable(insn.object().type): + return self.llbuilder.extract_value(self.map(insn.object()), self.attr_index(insn), + name=insn.name) + else: + llptr = self.llbuilder.gep(self.map(insn.object()), + [self.llindex(0), self.llindex(self.attr_index(insn))], + name=insn.name) + return self.llbuilder.load(llptr) + + def process_SetAttr(self, insn): + assert builtins.is_mutable(insns.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())) + + def process_GetElem(self, insn): + llelts = self.llbuilder.extract_value(self.map(insn.list()), 1) + llelt = self.llbuilder.gep(llelts, [self.map(insn.index())], + inbounds=True) + return self.llbuilder.load(llelt) + + def process_SetElem(self, insn): + llelts = self.llbuilder.extract_value(self.map(insn.list()), 1) + llelt = self.llbuilder.gep(llelts, [self.map(insn.index())], + inbounds=True) + return self.llbuilder.store(self.map(insn.value()), llelt) + + def process_Coerce(self, insn): + typ, value_typ = insn.type, insn.value().type + if builtins.is_int(typ) and builtins.is_float(value_typ): + return self.llbuilder.fptosi(self.map(insn.value()), self.llty_of_type(typ), + name=insn.name) + elif builtins.is_float(typ) and builtins.is_int(value_typ): + return self.llbuilder.sitofp(self.map(insn.value()), self.llty_of_type(typ), + name=insn.name) + elif builtins.is_int(typ) and builtins.is_int(value_typ): + if builtins.get_int_width(typ) > builtins.get_int_width(value_typ): + return self.llbuilder.sext(self.map(insn.value()), self.llty_of_type(typ), + name=insn.name) + else: # builtins.get_int_width(typ) < builtins.get_int_width(value_typ): + return self.llbuilder.trunc(self.map(insn.value()), self.llty_of_type(typ), + name=insn.name) + else: + assert False + + def process_Arith(self, insn): + if isinstance(insn.op, ast.Add): + if builtins.is_float(insn.type): + return self.llbuilder.fadd(self.map(insn.lhs()), self.map(insn.rhs()), + name=insn.name) + else: + return self.llbuilder.add(self.map(insn.lhs()), self.map(insn.rhs()), + name=insn.name) + elif isinstance(insn.op, ast.Sub): + if builtins.is_float(insn.type): + return self.llbuilder.fsub(self.map(insn.lhs()), self.map(insn.rhs()), + name=insn.name) + else: + return self.llbuilder.sub(self.map(insn.lhs()), self.map(insn.rhs()), + name=insn.name) + elif isinstance(insn.op, ast.Mult): + if builtins.is_float(insn.type): + return self.llbuilder.fmul(self.map(insn.lhs()), self.map(insn.rhs()), + name=insn.name) + else: + return self.llbuilder.mul(self.map(insn.lhs()), self.map(insn.rhs()), + name=insn.name) + elif isinstance(insn.op, ast.Div): + if builtins.is_float(insn.lhs().type): + return self.llbuilder.fdiv(self.map(insn.lhs()), self.map(insn.rhs()), + name=insn.name) + else: + lllhs = self.llbuilder.sitofp(self.map(insn.lhs()), self.llty_of_type(insn.type)) + llrhs = self.llbuilder.sitofp(self.map(insn.rhs()), self.llty_of_type(insn.type)) + return self.llbuilder.fdiv(lllhs, llrhs, + name=insn.name) + elif isinstance(insn.op, ast.FloorDiv): + if builtins.is_float(insn.type): + llvalue = self.llbuilder.fdiv(self.map(insn.lhs()), self.map(insn.rhs())) + llfnty = ll.FunctionType(ll.DoubleType(), [ll.DoubleType()]) + llfn = ll.Function(self.llmodule, llfnty, "llvm.round.f64") + return self.llbuilder.call(llfn, [llvalue], + name=insn.name) + else: + return self.llbuilder.sdiv(self.map(insn.lhs()), self.map(insn.rhs()), + name=insn.name) + elif isinstance(insn.op, ast.Mod): + if builtins.is_float(insn.type): + return self.llbuilder.frem(self.map(insn.lhs()), self.map(insn.rhs()), + name=insn.name) + else: + return self.llbuilder.srem(self.map(insn.lhs()), self.map(insn.rhs()), + name=insn.name) + elif isinstance(insn.op, ast.Pow): + if builtins.is_float(insn.type): + llfnty = ll.FunctionType(ll.DoubleType(), [ll.DoubleType(), ll.DoubleType()]) + llfn = ll.Function(self.llmodule, llfnty, "llvm.pow.f64") + return self.llbuilder.call(llfn, [self.map(insn.lhs()), self.map(insn.rhs())], + name=insn.name) + else: + llrhs = self.llbuilder.trunc(self.map(insn.rhs()), ll.IntType(32)) + llfnty = ll.FunctionType(ll.DoubleType(), [ll.DoubleType(), ll.IntType(32)]) + llfn = ll.Function(self.llmodule, llfnty, "llvm.powi.f64") + llvalue = self.llbuilder.call(llfn, [self.map(insn.lhs()), llrhs]) + return self.llbuilder.fptosi(llvalue, self.llty_of_type(insn.type), + name=insn.name) + elif isinstance(insn.op, ast.LShift): + return self.llbuilder.shl(self.map(insn.lhs()), self.map(insn.rhs()), + name=insn.name) + elif isinstance(insn.op, ast.RShift): + return self.llbuilder.ashr(self.map(insn.lhs()), self.map(insn.rhs()), + name=insn.name) + elif isinstance(insn.op, ast.BitAnd): + return self.llbuilder.and_(self.map(insn.lhs()), self.map(insn.rhs()), + name=insn.name) + elif isinstance(insn.op, ast.BitOr): + return self.llbuilder.or_(self.map(insn.lhs()), self.map(insn.rhs()), + name=insn.name) + elif isinstance(insn.op, ast.BitXor): + return self.llbuilder.xor(self.map(insn.lhs()), self.map(insn.rhs()), + name=insn.name) + else: + assert False + + def process_Compare(self, insn): + if isinstance(insn.op, ast.Eq): + op = '==' + elif isinstance(insn.op, ast.NotEq): + op = '!=' + elif isinstance(insn.op, ast.Gt): + op = '>' + elif isinstance(insn.op, ast.GtE): + op = '>=' + elif isinstance(insn.op, ast.Lt): + op = '<' + elif isinstance(insn.op, ast.LtE): + op = '<=' + else: + assert False + + if builtins.is_float(insn.lhs().type): + return self.llbuilder.fcmp_ordered(op, self.map(insn.lhs()), self.map(insn.rhs()), + name=insn.name) + else: + return self.llbuilder.icmp_signed(op, self.map(insn.lhs()), self.map(insn.rhs()), + name=insn.name) + + def process_Builtin(self, insn): + if insn.op == "nop": + fn = ll.Function(self.llmodule, ll.FunctionType(ll.VoidType(), []), "llvm.donothing") + return self.llbuilder.call(fn, []) + elif insn.op == "unwrap": + optarg, default = map(self.map, insn.operands) + has_arg = self.llbuilder.extract_value(optarg, 0) + arg = self.llbuilder.extract_value(optarg, 1) + return self.llbuilder.select(has_arg, arg, default, + name=insn.name) + elif insn.op == "round": + llfnty = ll.FunctionType(ll.DoubleType(), [ll.DoubleType()]) + llfn = ll.Function(self.llmodule, llfnty, "llvm.round.f64") + return self.llbuilder.call(llfn, [llvalue], + name=insn.name) + elif insn.op == "globalenv": + def get_outer(llenv, env_ty): + if ".outer" in env_ty.params: + outer_index = list(env_ty.params.keys()).index(".outer") + llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)]) + llouterenv = self.llbuilder.load(llptr) + return self.llptr_to_var(llouterenv, env_ty.params[".outer"], var_name) + else: + return llenv + + env, = insn.operands + return get_outer(self.map(env), env.type) + elif insn.op == "len": + lst, = insn.operands + return self.llbuilder.extract_value(self.map(lst), 0) + # elif insn.op == "exncast": + else: + assert False + + # def process_Closure(self, insn): + # pass + + # def process_Call(self, insn): + # pass + + def process_Select(self, insn): + return self.llbuilder.select(self.map(insn.cond()), + self.map(insn.lhs()), self.map(insn.rhs())) + + def process_Branch(self, insn): + return self.llbuilder.branch(self.map(insn.target())) + + def process_BranchIf(self, insn): + 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_Return(self, insn): + if builtins.is_none(insn.type): + return self.llbuilder.ret_void() + else: + return self.llbuilder.ret(self.llmap[insn.value()]) + + def process_Unreachable(self, insn): + return self.llbuilder.unreachable() + + def process_Raise(self, insn): + # TODO: hack before EH is working + llfnty = ll.FunctionType(ll.VoidType(), []) + llfn = ll.Function(self.llmodule, llfnty, "llvm.abort") + llinsn = self.llbuilder.call(llfn, [], + name=insn.name) + self.llbuilder.unreachable() + return llinsn + + # def process_Invoke(self, insn): + # pass + + # def process_LandingPad(self, insn): + # pass +