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
def rewrite_obj_return(func, env): """ Handle returning stack-allocated objects. """ if should_skip(env): return context = env['flypy.typing.context'] restype = env['flypy.typing.restype'] envs = env['flypy.state.envs'] builder = Builder(func) stack_alloc = representation.byref(restype) if stack_alloc: out = func.add_arg(func.temp("out"), opaque_t) context[out] = Pointer[restype] func.type = types.Function(types.Void, func.type.argtypes, False) for arg in func.args: arg.type = opaque_t func.type = types.Function(func.type.restype, (opaque_t, ) * len(func.args), False) is_generator = env['flypy.state.generator'] for op in func.ops: if (op.opcode == 'ret' and op.args[0] is not None and stack_alloc and not is_generator): # ret val => # store (load val) out ; ret void [val] = op.args builder.position_before(op) newval = builder.load(val) builder.store(newval, out) op.set_args([None]) # Update context context[newval] = StackVar[context[val]] elif op.opcode == 'call' and op.type != types.Void: # result = call(f, ...) => # alloca result ; call(f, ..., &result) ty = context[op] if conversion.byref(ty): f, args = op.args if not is_flypy_cc(f) or should_skip(envs[f]): continue builder.position_before(op) retval = builder.alloca(opaque_t) builder.position_after(op) op.replace_uses(retval) newargs = args + [retval] op.set_args([f, newargs]) # Update context context[retval] = context[op] context[op] = void
def rewrite_obj_return(func, env): """ Handle returning stack-allocated objects. """ if should_skip(env): return context = env['flypy.typing.context'] restype = env['flypy.typing.restype'] envs = env['flypy.state.envs'] builder = Builder(func) stack_alloc = representation.byref(restype) if stack_alloc: out = func.add_arg(func.temp("out"), opaque_t) context[out] = Pointer[restype] func.type = types.Function(types.Void, func.type.argtypes, False) for arg in func.args: arg.type = opaque_t func.type = types.Function(func.type.restype, (opaque_t,) * len(func.args), False) is_generator = env['flypy.state.generator'] for op in func.ops: if (op.opcode == 'ret' and op.args[0] is not None and stack_alloc and not is_generator): # ret val => # store (load val) out ; ret void [val] = op.args builder.position_before(op) newval = builder.load(val) builder.store(newval, out) op.set_args([None]) # Update context context[newval] = StackVar[context[val]] elif op.opcode == 'call' and op.type != types.Void: # result = call(f, ...) => # alloca result ; call(f, ..., &result) ty = context[op] if conversion.byref(ty): f, args = op.args if not is_flypy_cc(f) or should_skip(envs[f]): continue builder.position_before(op) retval = builder.alloca(opaque_t) builder.position_after(op) op.replace_uses(retval) newargs = args + [retval] op.set_args([f, newargs]) # Update context context[retval] = context[op] context[op] = void
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 lower_fields(func, env=None): b = Builder(func) for op in func.ops: if op.opcode in ("getfield", "setfield") and op.args[0].type.is_pointer: b.position_before(op) p = op.args[0] load = b.load(p.type.base, [p]) args = [load] + op.args[1:] op.set_args(args) if op.opcode == "setfield": b.position_after(op) b.store(op, p)
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
def inline(func, call, uses=None): """ Inline the call instruction into func. :param uses: defuse information """ callee = call.args[0] # assert_inlinable(func, call, callee, uses) builder = Builder(func) builder.position_before(call) inline_header, inline_exit = builder.splitblock() new_callee = 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 after = inline_header for block in new_callee.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) # 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)
def rewrite_raise_exc_type(func, env): """ Rewrite 'raise Exception' to 'raise Exception()' """ context = env['numba.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
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
def generate_copies(func, phis): """ Emit stores to stack variables in predecessor blocks. """ builder = Builder(func) vars = {} loads = {} # Allocate a stack variable for each phi builder.position_at_beginning(func.startblock) for block in phis: for phi in phis[block]: vars[phi] = builder.alloca(types.Pointer(phi.type)) # Generate loads in blocks containing the phis for block in phis: leaders = list(block.leaders) last_leader = leaders[-1] if leaders else block.head builder.position_after(last_leader) for phi in phis[block]: loads[phi] = builder.load(vars[phi]) # Generate copies (store to stack variables) for block in phis: for phi in phis[block]: preds, args = phi.args var = vars[phi] phi_args = [loads.get(arg, arg) for arg in args] for pred, arg in zip(preds, phi_args): builder.position_before(pred.terminator) builder.store(arg, var) # Replace phis for block in phis: for phi in phis[block]: phi.replace_uses(loads[phi]) phi.delete() return vars, loads
def convert_retval(func, env): """ Rewrite 'return x' to 'return (restype) x' """ if env['numba.state.opaque']: return restype = func.type.restype context = env['numba.typing.context'] b = Builder(func) for op in func.ops: if op.opcode != 'ret' or op.args[0] is None: continue [retval] = op.args if retval.type != restype: b.position_before(op) converted = b.convert(restype, retval) op.set_args([converted]) # Update type context context[converted] = context[retval]