Beispiel #1
0
def insert_allocations(func, env):
    b = Builder(func)

    # IR positions and list of ops
    positions = dict((op, idx) for idx, op in enumerate(func.ops))
    oplist = list(func.ops)

    for op in func.ops:
        if op.opcode == 'ckernel':
            ckernel, args = op.args
            alloc = Op('alloc', op.type, args=[])

            # TODO: Insert alloc in args list of ckernel

            # Replace uses of ckernel with temporary allocation
            op.replace_uses(alloc)
            op.set_args([ckernel, [alloc] + args])

            # Emit allocation before first use
            b.position_before(op)
            b.emit(alloc)

            # Emit deallocation after last use, unless we are returning
            # the result
            idx = max(positions[u] for u in func.uses[alloc])
            last_op = oplist[idx]
            if not last_op.opcode == 'ret':
                b.position_after(last_op)
                dealloc = Op('dealloc', types.Void, [alloc])
                b.emit(dealloc)

    return func, env
Beispiel #2
0
def explicit_coercions(func, env=None):
    """
    Turn implicit coercions into explicit conversion operations.
    """
    conversions = {}
    b = Builder(func)

    for op in func.ops:
        if op.opcode != 'kernel':
            continue

        overload = op.metadata['overload']
        signature = overload.resolved_sig
        parameters = signature.parameters[:-1]
        assert len(op.args) - 1 == len(parameters)

        # -------------------------------------------------
        # Identify conversion points

        replacements = {} # { arg : replacement_conversion }
        for arg, param_type in zip(op.args[1:], parameters):
            if arg.type != param_type:
                conversion = conversions.get((arg, param_type))
                if not conversion:
                    conversion = Op('convert', param_type, [arg])
                    b.position_after(arg)
                    b.emit(conversion)
                    conversions[arg, param_type] = conversion

                replacements[arg] = conversion

        # -------------------------------------------------

        op.replace_args(replacements)
Beispiel #3
0
def split_critical_edges(func, cfg, phis):
    """
    Split critical edges to correctly handle cycles in phis. See 2) above.
    """
    b = Builder(func)
    for block in cfg.node:
        successors = cfg.neighbors(block)
        if len(successors) > 1:
            # More than one successor, we need to split
            # (Alternatively, we could move our copies into the successor block
            #  if we were the only predecessor, but this seems simpler)

            # Split successors with phis
            new_succs = {} # old_successor -> new_successor
            for succ in successors:
                if phis[succ]:
                    label = func.temp("split_critical")
                    new_succ = func.new_block(label, after=block)
                    new_succs[succ] = new_succ
                    b.position_at_end(new_succ)
                    b.jump(succ)

            # Patch our basic-block terminator to point to new blocks
            if new_succs:
                terminator = block.terminator
                assert terminator.opcode == 'cbranch', terminator
                test, truebb, falsebb = terminator.args
                terminator.set_args([test,
                                     new_succs.get(truebb, truebb),
                                     new_succs.get(falsebb, falsebb)])
Beispiel #4
0
    def initialize(self):
        """Initialize pykit untypes structures"""

        # Setup Function
        sig = types.Function(types.Opaque, [types.Opaque] * len(self.argnames),
                             False)
        self.dst = Function(func_name(self.func), self.argnames, sig)

        # Setup Builder
        self.builder = Builder(self.dst)

        # Setup Blocks
        for offset in self.bytecode.labels:
            name = blockname(self.func, offset)
            block = self.dst.new_block(name)
            self.blocks[offset] = block
            self.stacks[block] = []

        # Setup Variables
        self.builder.position_at_beginning(self.dst.startblock)
        for varname in self.varnames:
            stackvar = self.builder.alloca(types.Pointer(types.Opaque),
                                           result=self.dst.temp(varname))
            self.allocas[varname] = stackvar

            # Initialize function arguments
            if varname in self.argnames:
                self.builder.store(self.dst.get_arg(varname), stackvar)
Beispiel #5
0
def rewrite_setattr(func, env):
    """
    Resolve missing attributes through __setattr__
    """
    context = env['flypy.typing.context']

    b = Builder(func)

    for op in func.ops:
        if op.opcode == 'setfield':
            obj, attr, value = op.args
            obj_type = context[obj]
            attr_type = types.String[()]

            if attr not in obj_type.fields and attr not in obj_type.layout:
                assert SETATTR in obj_type.fields, attr

                b.position_after(op)

                # Construct attribute string
                attr_string = OConst(attr)

                # call(getfield(obj, '__setattr__'), ['attr', value])
                method_type = make_method(obj_type, SETATTR)
                method = b.getfield(ptypes.Opaque, obj, SETATTR)
                call = b.call(ptypes.Opaque, method, [attr_string, value])
                op.delete()

                # Update context
                del context[op]
                context[method] = method_type
                context[call] = types.Void[()]
                context[attr_string] = attr_type
Beispiel #6
0
def insert_phis(func, cfg, allocas):
    """Insert φs in the function given the set of promotable stack variables"""
    builder = Builder(func)
    phis = {}  # phi -> alloca
    for block in func.blocks:
        if len(cfg.predecessors(block)) > 1:
            with builder.at_front(block):
                for alloca in allocas:
                    phi = builder.phi(alloca.type.base, [], [])
                    phis[phi] = alloca

    return phis
Beispiel #7
0
def rewrite_lowlevel_constants(func, env):
    """
    Rewrite constant pointers.
    """
    b = Builder(func)
    b.position_at_beginning(func.startblock)

    for op in func.ops:
        constants = collect_constants(op)
        new_constants = []
        for c in constants:
            new_constants.append(allocate_pointer_const(b, c))
        substitute_args(op, constants, new_constants)
    def test_exc_rewrite(self):
        func = Function("foo", [], types.Function(types.Void, (), False))
        entry = func.new_block("entry")
        catch_block = func.new_block("catch")
        b = Builder(func)

        with b.at_front(entry):
            b.exc_setup([catch_block])
            b.exc_throw(Const(StopIteration, types.Exception))
        with b.at_front(catch_block):
            b.exc_catch([Const(Exception, types.Exception)])

        local_exceptions.run(func, {})
        self.assertNotIn('exc_throw', opcodes(func))
Beispiel #9
0
    def visit_FuncDef(self, node):
        assert not node.param_decls
        self.enter_func()

        name = node.decl.name
        type = self.visit(node.decl.type)
        argnames = [p.name for p in node.decl.type.args.params]
        self.func = Function(name, argnames, type)
        self.func.add_block('entry')
        self.builder = Builder(self.func)
        self.builder.position_at_end(self.func.blocks[0])
        self.generic_visit(node.body)

        self.leave_func()
Beispiel #10
0
def move_generator(func, consumer, empty_body):
    gen = consumer.generator
    gen.unlink()

    b = Builder(func)
    b.position_at_end(empty_body)
    b.emit(gen)

    with b.at_end(empty_body):
        loop_exit = determine_loop_exit(consumer.loop)
        b.jump(loop_exit)
Beispiel #11
0
def inline(func, call):
    """
    Inline the call instruction into func.

    :return: { old_op : new_op }
    """
    callee = call.args[0]
    callblock = call.block
    # assert_inlinable(func, call, callee, uses)

    builder = Builder(func)
    builder.position_before(call)
    inline_header, inline_exit = builder.splitblock()
    new_callee, valuemap = copy_function(callee, temper=func.temp)
    result = rewrite_return(new_callee)

    # Fix up arguments
    for funcarg, arg in zip(new_callee.args, call.args[1]):
        funcarg.replace_uses(arg)

    # Copy blocks
    new_blocks = list(new_callee.blocks)
    after = inline_header
    for block in new_blocks:
        block.parent = None
        func.add_block(block, after=after)
        after = block

    # Fix up wiring
    builder.jump(new_callee.startblock)
    with builder.at_end(new_callee.exitblock):
        builder.jump(inline_exit)

    stretch_exception_block(builder, callblock, new_blocks)

    # Fix up final result of call
    if result is not None:
        # non-void return
        result.unlink()
        result.result = call.result
        call.replace(result)
    else:
        call.delete()

    func.reset_uses()
    #verify(func)

    return valuemap
Beispiel #12
0
def detach_loop(func, consumer):
    loop, iter = consumer.loop, consumer.iter

    for block in loop.blocks:
        func.del_block(block)

    func.reset_uses()

    b = Builder(func)
    jump = iter.block.terminator
    assert jump.opcode == 'jump' and jump.args[0] == loop.head
    jump.delete()

    b.position_at_end(iter.block)
    _, newblock = b.splitblock(terminate=True)
    return newblock
Beispiel #13
0
class RuntimeLowering(object):
    """
    Lower assembly Operations into runtime calls, e.g.
        Op("thread_start") -> Op("call", "thread_start", [])
    """
    def __init__(self, func):
        self.mod = func.module
        self.builder = Builder(func)
        self.func = func

    def lower_ops_into_runtime(self, names, insert_decl=False):
        """Lower all ops listed in `names` into runtime calls"""
        names = set(names)
        for op in self.func.ops:
            if op.opcode in names:
                self.lower_into_runtime(op, insert_decl)

    def lower_into_runtime(self, op, insert_decl=False):
        """
        Lower op into a runtime call.

        :param decl: Whether to insert an external declaration if not present
        """
        _verify_args(op)
        if insert_decl and not self.mod.get_global(op.opcode):
            signature = types.Function(op.type, [arg.type for arg in op.args],
                                       False)
            self.mod.add_global(
                GlobalValue(op.opcode, signature, external=True))

        call = self.builder.gen_call_external(op.opcode, op.args, op.result)
        op.replace(call)
Beispiel #14
0
class TestIR(unittest.TestCase):

    def setUp(self):
        self.m = from_c(source)
        self.f = self.m.get_function('testfunc')
        self.b = Builder(self.f)

    def test_replace(self):
        entry = self.f.get_block('entry')
        for op in entry:
            if op.opcode == ops.convert:
                r, = op.args
                t = self.b.add(types.Int32, [r, r])
                c = self.b.convert(types.Float32, [t], result=op.result)
                op.replace([t, c])
                break

        cfa.run(self.f)
        self.assertEqual(opcodes(self.f), ['mul', 'add', 'convert', 'ret'])
Beispiel #15
0
class TestInterp(unittest.TestCase):

    def setUp(self):
        self.f = from_assembly(testfunc)
        self.b = Builder(self.f)

    def test_replace(self):
        entry = self.f.get_block('entry')
        for op in entry:
            if op.opcode == ops.convert:
                r, = op.args
                t = self.b.add(types.Int32, [r, r], result="temp")
                c = self.b.convert(types.Float32, [t], result=op.result)
                op.replace([t, c])
                break

        result = [(op.opcode, op.result) for op in entry]
        assert result == [('alloca', '0'), ('mul', 'r'), ('add', 'temp'),
                          ('convert', '1'), ('store', '2'), ('load', '3'),
                          ('ret', '4')]
Beispiel #16
0
def rewrite_raise_exc_type(func, env):
    """
    Rewrite 'raise Exception' to 'raise Exception()'
    """
    context = env['flypy.typing.context']
    b = Builder(func)

    for op in func.ops:
        if op.opcode == 'exc_throw':
            [exc_type] = op.args
            if isinstance(exc_type, Const):
                ty = context[exc_type]
                if ty.impl == Type:  # Type[Exception[]]
                    # Generate constructor application
                    b.position_before(op)
                    exc_obj = b.call(ptypes.Opaque, exc_type, [])
                    op.set_args([exc_obj])

                    type = ty.parameters[0]
                    context[exc_obj] = type
Beispiel #17
0
    def visit_FuncDef(self, node):
        assert not node.param_decls
        self.enter_func()

        name = node.decl.name
        type = self.visit(node.decl.type)
        if node.decl.type.args:
            argnames = [p.name or "" for p in node.decl.type.args.params]
        else:
            argnames = []
        self.func = Function(name, argnames, type)
        self.func.new_block('entry')
        self.builder = Builder(self.func)
        self.builder.position_at_end(self.func.startblock)

        # Store arguments in stack variables
        for argname in argnames:
            self.assignvar(argname, self.func.get_arg(argname))

        self.generic_visit(node.body)
        self.leave_func()
Beispiel #18
0
def explicit_coercions(func, env=None):
    """
    Turn implicit coercions into explicit conversion operations.
    """
    conversions = {}
    b = Builder(func)

    for op in func.ops:
        if op.opcode != 'kernel':
            continue

        overload = op.metadata['overload']
        signature = overload.resolved_sig
        parameters = signature.parameters[:-1]
        assert len(op.args) - 1 == len(parameters)

        # -------------------------------------------------
        # Identify conversion points

        replacements = {} # { arg : replacement_conversion }
        for arg, param_type in zip(op.args[1:], parameters):
            if arg.type != param_type:
                conversion = conversions.get((arg, param_type))
                if not conversion:
                    conversion = Op('convert', param_type, [arg])
                    b.position_after(arg)
                    b.emit(conversion)
                    conversions[arg, param_type] = conversion

                replacements[arg] = conversion

        # -------------------------------------------------

        op.replace_args(replacements)
Beispiel #19
0
def split_critical_edges(func, cfg, phis):
    """
    Split critical edges to correctly handle cycles in phis. See 2) above.
    """
    b = Builder(func)
    for block in cfg.node:
        successors = cfg.neighbors(block)
        if len(successors) > 1:
            # More than one successor, we need to split
            # (Alternatively, we could move our copies into the successor block
            #  if we were the only predecessor, but this seems simpler)

            # Split successors with phis
            new_succs = {}  # old_successor -> new_successor
            for succ in successors:
                if phis[succ]:
                    label = func.temp("split_critical")
                    new_succ = func.new_block(label, after=block)
                    new_succs[succ] = new_succ
                    b.position_at_end(new_succ)
                    b.jump(succ)

            # Patch our basic-block terminator to point to new blocks
            if new_succs:
                terminator = block.terminator
                assert terminator.opcode == 'cbranch', terminator
                test, truebb, falsebb = terminator.args
                terminator.set_args([
                    test,
                    new_succs.get(truebb, truebb),
                    new_succs.get(falsebb, falsebb)
                ])
Beispiel #20
0
def rewrite_setattr(func, env):
    """
    Resolve missing attributes through __setattr__
    """
    context = env['flypy.typing.context']

    b = Builder(func)

    for op in func.ops:
        if op.opcode == 'setfield':
            obj, attr, value = op.args
            obj_type = context[obj]
            attr_type = types.String[()]

            if attr not in obj_type.fields and attr not in obj_type.layout:
                assert SETATTR in obj_type.fields, attr

                b.position_after(op)

                # Construct attribute string
                attr_string = OConst(attr)

                # call(getfield(obj, '__setattr__'), ['attr', value])
                method_type = make_method(obj_type, SETATTR)
                method = b.getfield(ptypes.Opaque, obj, SETATTR)
                call = b.call(ptypes.Opaque, method, [attr_string, value])
                op.delete()

                # Update context
                del context[op]
                context[method] = method_type
                context[call] = types.Void[()]
                context[attr_string] = attr_type
Beispiel #21
0
def move_allocas(func, allocas):
    """Move all allocas to the start block"""
    builder = Builder(func)
    builder.position_at_beginning(func.startblock)
    for alloca in allocas:
        if alloca.block != func.startblock:
            alloca.unlink()
            builder.emit(alloca)
Beispiel #22
0
def simplify_exceptions(func, env=None):
    """
    Rewrite exceptions emitted by the front-end:

        exc_end -> split block
    """
    b = Builder(func)
    for op in func.ops:
        if op.opcode == 'exc_end':
            b.position_after(op)
            b.splitblock(terminate=True, preserve_exc=False)
            op.delete()
Beispiel #23
0
def lower_fields(func, env):
    b = Builder(func)
    opbuilder = OpBuilder()

    for op in func.ops:
        if op.opcode not in ("getfield", "setfield"):
            continue

        if op.args[0].type.is_pointer:
            b.position_before(op)

            # Load the pointer and update the argument
            p = op.args[0]
            load = b.load(p)
            args = [load] + op.args[1:]
            op.set_args(args)

            if op.opcode == "setfield":
                # Write back result
                b.position_after(op)
                op.type = load.type
                b.store(op, p)

        if op.opcode == "getfield":
            struct, attr = op.args
            newop = opbuilder.extractfield(op.type,
                                           struct,
                                           attr,
                                           result=op.result)
        else:
            struct, attr, value = op.args
            newop = opbuilder.insertfield(op.type,
                                          struct,
                                          attr,
                                          value,
                                          result=op.result)

        op.replace(newop)
def preserve_exceptions(oldblock, newblock):
    """
    Preserve exc_setup instructions for block splits.
    """
    from pykit.ir import Builder

    func = oldblock.parent
    b = Builder(func)
    b.position_at_beginning(newblock)

    for op in oldblock.leaders:
        if op.opcode == 'exc_setup':
            b.exc_setup(op.args[0], **op.metadata)
Beispiel #25
0
def from_expr(graph, expr_context, env):
    """
    Map a Blaze expression graph to blaze AIR

    Parameters
    ----------
    graph: blaze.expr.Op
        Expression graph

    expr_context: ExprContext
        Context of the expression

    ctx: ExecutionContext
    """
    inputs = expr_context.params

    # -------------------------------------------------
    # Types

    argtypes = [operand.dshape for operand in inputs]
    signature = types.Function(graph.dshape, argtypes, varargs=False)

    # -------------------------------------------------
    # Setup function

    name = "expr"
    argnames = ["e%d" % i for i in range(len(inputs))]
    f = Function(name, argnames, signature)
    builder = Builder(f)
    builder.position_at_beginning(f.new_block('entry'))

    # -------------------------------------------------
    # Generate function

    valuemap = dict(
        (expr, f.get_arg("e%d" % i)) for i, expr in enumerate(inputs))
    _from_expr(graph, f, builder, valuemap)

    retval = valuemap[graph]
    builder.ret(retval)

    # Update environment with runtime arguments
    runtime_args = [expr_context.terms[input] for input in inputs]
    env['runtime.args'] = dict(zip(f.args, runtime_args))

    return f
Beispiel #26
0
def rewrite_varargs(func, env):
    """
    Rewrite function application with arguments that go in the varargs:

        def f(x, *args):
            ...

        call(f, [x, y, z]) -> call(f, [x, (y, z)])
    """
    b = Builder(func)
    caller = Caller(b, env['flypy.typing.context'], env)
    caller = Caller(b, env['flypy.typing.context'], env)
    call_flags = env['flypy.state.call_flags']

    def f(context, py_func, f_env, op):
        f, args = op.args
        flags = call_flags.get(op, {})

        if flags.get('varargs'):
            return

        # Retrieve any remaining arguments meant for *args
        flattened = flatargs(py_func, args, {})
        if flattened.have_varargs:
            b.position_before(op)
            #print(py_func, flattened.varargs)

            # -- Build the tuple -- #
            result = caller.apply_constructor(EmptyTuple)
            for item in flattened.varargs:
                result = caller.apply_constructor(StaticTuple,
                                                  args=[item, result])

            # -- Patch callsite -- #
            args = list(flattened.positional) + [result]
            op.set_args([f, args])

    jitcallmap(f, func, env)
def splitblock(block, trailing, name=None, terminate=False, preserve_exc=True):
    """Split the current block, returning (old_block, new_block)"""

    func = block.parent

    if block.is_terminated():
        successors = deduce_successors(block)
    else:
        successors = []

    # -------------------------------------------------
    # Sanity check

    # Allow splitting only after leaders and before terminator
    # TODO: error check

    # -------------------------------------------------
    # Split

    blockname = name or func.temp('Block')
    newblock = func.new_block(blockname, after=block)

    # -------------------------------------------------
    # Move ops after the split to new block

    for op in trailing:
        op.unlink()
    newblock.extend(trailing)

    if terminate and not block.is_terminated():
        # Terminate
        b = Builder(func)
        b.position_at_end(block)
        b.jump(newblock)

    # Update phis and preserve exception blocks
    patch_phis(block, newblock, successors)
    if preserve_exc:
        preserve_exceptions(block, newblock)

    return block, newblock
Beispiel #28
0
def rewrite_getattr(func, env):
    """
    Resolve missing attributes through __getattr__
    """
    context = env['flypy.typing.context']

    b = OpBuilder()
    builder = Builder(func)

    for op in func.ops:
        if op.opcode == 'getfield':
            value, attr = op.args
            obj_type = context[value]
            attr_type = types.String[()]

            if attr not in obj_type.fields and attr not in obj_type.layout:
                assert '__getattr__' in obj_type.fields

                op.set_args([value, '__getattr__'])

                # Construct attribute string
                attr_string = OConst(attr)

                # Retrieve __getattr__ function and type
                getattr_func, func_type, restype = infer_getattr(
                    obj_type, op, env)

                # call(getfield(obj, '__getattr__'), ['attr'])
                call = b.call(op.type, op, [attr_string])
                op.replace_uses(call)
                builder.position_after(op)
                builder.emit(call)

                # Update context
                context[op] = func_type
                context[attr_string] = attr_type
                context[call] = restype
Beispiel #29
0
 def __init__(self, func):
     self.mod = func.module
     self.builder = Builder(func)
     self.func = func
Beispiel #30
0
def run(func, env=None, return_block=None):
    """
    Rewrite 'ret' operations into jumps to a return block and assignments
    to a return variable.
    """
    b = Builder(func)
    return_block = return_block or func.new_block("pykit.return")

    # Allocate return variable
    if not func.type.restype.is_void:
        with b.at_front(func.startblock):
            return_var = b.alloca(types.Pointer(func.type.restype), [])
            b.store(Undef(func.type.restype), return_var)
    else:
        return_var = None

    # Repace 'ret' instructions with jumps and assignments
    for op in func.ops:
        if op.opcode == "ret":
            b.position_after(op)
            if return_var:
                b.store(op.args[0], return_var)
            b.jump(return_block)
            op.delete()

    with b.at_end(return_block):
        if return_var:
            result = b.load(return_var.type.base, [return_var])
        else:
            result = None

        b.ret(result)
Beispiel #31
0
def consume_yields(func, consumer, generator_func, valuemap):
    b = Builder(func)
    copier = lambda x: x

    loop = consumer.loop
    inlined_values = set(valuemap.values())

    for block in func.blocks:
        if block in inlined_values:
            for op in block.ops:
                if op.opcode == 'yield':
                    # -- Replace 'yield' by the loop body -- #
                    b.position_after(op)
                    _, resume = b.splitblock()

                    # Copy blocks
                    blocks = [copier(block) for block in loop.blocks]

                    # Insert blocks
                    prev = op.block
                    for block in blocks:
                        func.add_block(block, after=prev)
                        prev = block

                    # Fix wiring
                    b.jump(blocks[0])
                    b.position_at_end(blocks[-1])
                    b.jump(resume)

                    # We just introduced a bunch of copied blocks
                    func.reset_uses()

                    # Update phis with new predecessor
                    b.replace_predecessor(loop.tail, op.block, loop.head)
                    b.replace_predecessor(loop.tail, op.block, loop.head)

                    # Replace next() by value produced by yield
                    value = op.args[0]
                    consumer.next.replace_uses(value)
                    op.delete()

    # We don't need these anymore
    consumer.next.delete()
Beispiel #32
0
 def setUp(self):
     self.m = from_c(source)
     self.f = self.m.get_function('testfunc')
     self.b = Builder(self.f)
Beispiel #33
0
class PykitIRVisitor(c_ast.NodeVisitor):
    """
    Map pykit IR in the form of polymorphic C to in-memory pykit IR.

        int function(float x) {
            int i = 0;        /* I am a comment */
            while (i < 10) {  /*: { "unroll": true } :*/
                x = call_external("sqrt", x * x);
            }
            return (int) x;
        }

    Attributes:
    """

    in_function = False

    def __init__(self, type_env=None):
        self.mod = Module()
        self.type_env = type_env or {}

        self.func = None
        self.builder = None
        self.local_vars = None
        self.allocas = None

        self.global_vars = {}
        self.functions = {}

    # ______________________________________________________________________

    @property
    def vars(self):
        if self.in_function:
            return self.local_vars
        else:
            return self.global_vars

    def enter_func(self):
        self.in_function = True
        self.local_vars = {}
        self.allocas = {}

    def leave_func(self):
        self.in_function = False
        self.mod.add_function(self.func)
        self.local_vars = None
        self.allocas = None
        self.func = None

    def visit(self, node, type=None):
        """
        Visit a node.

        :type: Whether we have a type for this opcode, which is an LHS type
               or a cast. E.g.:

              (Int) call(...)    // cast
              result = call(...) // assmnt, assuming 'result' is declared
              result = call(..., call(...)) // second 'call' isn't typed

        """
        self.type = type
        method = 'visit_' + node.__class__.__name__
        visitor = getattr(self, method, self.generic_visit)
        # if visitor is None:
        #     raise SyntaxError(
        #         "Node %s not supported in %s:%s" % (node, node.coord.file,
        #                                             node.coord.line))
        return visitor(node)

    def visitif(self, node):
        if node:
            return self.visit(node)

    def visits(self, node):
        return list(map(self.visit, node))

    # ______________________________________________________________________

    def alloca(self, varname):
        if varname not in self.allocas:
            # Allocate variable with alloca
            with self.builder.at_front(self.func.startblock):
                type = types.Pointer(self.local_vars[varname])
                result = self.func.temp(varname)
                self.allocas[varname] = self.builder.alloca(type, [], result)

        return self.allocas[varname]

    def assignvar(self, varname, rhs):
        self.builder.store(rhs, self.alloca(varname))

    def assign(self, varname, rhs):
        if self.in_function:
            # Local variable
            type = self.local_vars[varname]
            self.assignvar(varname, self.visit(rhs, type=type))
        else:
            # Global variable
            type = self.global_vars[varname]
            self.mod.add_global(
                GlobalValue(varname,
                            type=self.type,
                            value=self.visit(rhs, type=type)))

    # ______________________________________________________________________

    def visit_Decl(self, decl):
        if decl.name in self.vars:
            error(decl, "Var '%s' already declared!" % (decl.name, ))

        type = self.visit(decl.type)
        self.vars[decl.name] = type
        if decl.init:
            self.assign(decl.name, decl.init)
        elif not self.in_function:
            extern = decl.storage == 'external'
            self.mod.add_global(GlobalValue(decl.name, type, external=extern))

        return type

    def visit_TypeDecl(self, decl):
        return self.visit(decl.type)

    visit_Typename = visit_TypeDecl

    def visit_PtrDecl(self, decl):
        return types.Pointer(self.visit(decl.type.type))

    def visit_FuncDecl(self, decl):
        if decl.args:
            params = self.visits(decl.args.params)
        else:
            params = []
        return types.Function(self.visit(decl.type), params)

    def visit_IdentifierType(self, node):
        name, = node.names
        return self.type_env[name]

    def visit_Typedef(self, node):
        if node.name in ("Type", "_list"):
            type = self.type_env[node.name]
        else:
            type = self.visit(node.type)
            if type == types.Type:
                type = getattr(types, node.name)

            self.type_env[node.name] = type

        return type

    def visit_Template(self, node):
        left = self.visit(node.left)
        subtypes = self.visits(node.right)
        if left is list:
            return list(subtypes)
        else:
            assert issubclass(left, types.Type)
            subtypes = self.visits(node.right)
            return left(*subtypes)

    # ______________________________________________________________________

    def visit_FuncDef(self, node):
        assert not node.param_decls
        self.enter_func()

        name = node.decl.name
        type = self.visit(node.decl.type)
        if node.decl.type.args:
            argnames = [p.name or "" for p in node.decl.type.args.params]
        else:
            argnames = []
        self.func = Function(name, argnames, type)
        self.func.new_block('entry')
        self.builder = Builder(self.func)
        self.builder.position_at_end(self.func.startblock)

        # Store arguments in stack variables
        for argname in argnames:
            self.assignvar(argname, self.func.get_arg(argname))

        self.generic_visit(node.body)
        self.leave_func()

    # ______________________________________________________________________

    def visit_FuncCall(self, node):
        type = self.type
        opcode = node.name.name
        args = self.visits(node.args.exprs) if node.args else []

        if opcode == "list":
            return args
        elif not type and not ops.is_void(opcode):
            error(
                node, "Expected a type for sub-expression "
                "(add a cast or assignment)")
        elif not hasattr(self.builder, opcode):
            if opcode in self.mod.functions:
                return self.builder.call(type,
                                         [self.mod.get_function(opcode), args])
            error(node, "No opcode %s" % (opcode, ))

        buildop = getattr(self.builder, opcode)
        if ops.is_void(opcode):
            return buildop(*args)
        else:
            return buildop(type or "Unset", args)

    def visit_ID(self, node):
        if self.in_function:
            if node.name in self.local_vars:
                result = self.alloca(node.name)
                return self.builder.load(result.type.base, [result])

            global_val = (self.mod.get_function(node.name)
                          or self.mod.get_global(node.name))

            if not global_val:
                error(node, "Not a local or global: %r" % node.name)

            return global_val

    def visit_Cast(self, node):
        type = self.visit(node.to_type)
        if isinstance(node.expr, c_ast.FuncCall):
            op = self.visit(node.expr, type=type)
            op.type = type
            return op
        else:
            result = self.visit(node.expr)
            if result.type == type:
                return result
            return self.builder.convert(type, [result])

    def visit_Assignment(self, node):
        if node.op != '=':
            error(node, "Only assignment with '=' is supported")
        if not isinstance(node.lvalue, c_ast.ID):
            error(node, "Canot only assign to a name")
        self.assign(node.lvalue.name, node.rvalue)

    def visit_Constant(self, node):
        type = self.type_env[node.type]
        const = types.convert(node.value, types.resolve_typedef(type))
        if isinstance(const, basestring):
            const = const[1:-1]  # slice away quotes
        return Const(const)

    def visit_UnaryOp(self, node):
        op = defs.unary_defs[node.op]
        buildop = getattr(self.builder, op)
        arg = self.visit(node.expr)
        type = self.type or arg.type
        return buildop(type, [arg])

    def visit_BinaryOp(self, node):
        op = binary_defs[node.op]
        buildop = getattr(self.builder, op)
        left, right = self.visits([node.left, node.right])
        type = self.type
        if not type:
            l, r = map(types.resolve_typedef, [left.type, right.type])
            assert l == r, (l, r)
        if node.op in defs.compare_defs:
            type = types.Bool
        return buildop(type or left.type, [left, right])

    def visit_If(self, node):
        cond = self.visit(node.cond)
        ifpos, elsepos, exit_block = self.builder.ifelse(cond)

        with ifpos:
            self.visit(node.iftrue)
            self.builder.jump(exit_block)

        with elsepos:
            if node.iffalse:
                self.visit(node.iffalse)
            self.builder.jump(exit_block)

        self.builder.position_at_end(exit_block)

    def _loop(self, init, cond, next, body):
        _, exit_block = self.builder.splitblock(self.func.temp("exit"))
        _, body_block = self.builder.splitblock(self.func.temp("body"))
        _, cond_block = self.builder.splitblock(self.func.temp("cond"))

        self.visitif(init)
        self.builder.jump(cond_block)

        with self.builder.at_front(cond_block):
            cond = self.visit(cond, type=types.Bool)
            self.builder.cbranch(cond, body_block, exit_block)

        with self.builder.at_front(body_block):
            self.visit(body)
            self.visitif(next)
            bb = self.builder.basic_block
            if not bb.tail or not ops.is_terminator(bb.tail.opcode):
                self.builder.jump(cond_block)

        self.builder.position_at_end(exit_block)

    def visit_While(self, node):
        self._loop(None, node.cond, None, node.stmt)

    def visit_For(self, node):
        # avoid silly 2to3 rewrite to 'node.__next__'
        next = getattr(node, 'next')
        self._loop(node.init, node.cond, next, node.stmt)

    def visit_Return(self, node):
        b = self.builder
        value = self.visit(node.expr)
        t = self.func.temp
        b.ret(b.convert(self.func.type.restype, [value]))