def test_inline2(self): harder = textwrap.dedent(""" int callee(int i) { (void) print(i); while (i < 10) { i = i + 1; return i * 2; } return i; } int caller() { int x = 4; while (x < 10) { (void) print(x); x = call(callee, list(x)); } return x; } """) mod = from_c(harder) func = mod.get_function("caller") verify(func) result = interp.run(func) [callsite] = findallops(func, 'call') inline.inline(func, callsite) cfa.run(func) verify(func) # TODO: update phi when splitting blocks result2 = interp.run(func) assert result == result2
def test_inline2(self): harder = """ int callee(int i) { (void) print(i); while (i < 10) { i = i + 1; return i * 2; } return i; } int caller() { int x = 4; while (x < 10) { (void) print(x); x = call(callee, list(x)); } return x; } """ mod = from_c(harder) func = mod.get_function("caller") verify(func) result = interp.run(func) [callsite] = findallops(func, 'call') inline.inline(func, callsite) cfa.run(func) verify(func)
def test_inline2(self): harder = textwrap.dedent(""" int callee(int i) { (void) print(i); while (i < 10) { i = i + 1; return i * 2; } return i; } int caller() { int x = 4; while (x < 10) { (void) print(x); x = call(callee, list(x)); } return x; } """) mod = from_c(harder) func = mod.get_function("caller") verify(func) result = interp.run(func) [callsite] = findallops(func, 'call') inline.inline(func, callsite) cfa.run(func) verify(func)
def rewrite_return(func): """Rewrite ret ops to assign to a variable instead, which is returned""" ret_normalization.run(func) [ret] = findallops(func, 'ret') [value] = ret.args ret.delete() return value
def find_handler(setup_handlers, exc_model, exc): for exc_setup in setup_handlers: [handler_blocks] = exc_setup.args for block in handler_blocks: for op in findallops(block.leaders, 'exc_catch'): [exc_types] = op.args if any(exc_model.exc_op_match(exc_type, exc) for exc_type in exc_types): return op.block
def run(func, env, exc_model=interp.ExceptionModel()): b = OpBuilder() for op in func.ops: if op.opcode != 'exc_throw': continue [exc] = op.args if not isinstance(exc, Const): continue setup_handlers = findallops(op.block.leaders, 'exc_setup') handler = find_handler(setup_handlers, exc_model, exc.const) if handler: op.replace(b.jump(handler, result=op.result))
def rewrite_exceptions(func, env, exc_model=interp.ExceptionModel()): """ Rewrite exc_throw(exc) -> jump(handler_block) for statically determined exceptions. TODO: Can this be optimized instead after lowering to a zero-cost or costful model? """ b = OpBuilder() for op in func.ops: if op.opcode != 'exc_throw': continue [exc] = op.args setup_handlers = findallops(op.block.leaders, 'exc_setup') handler = find_handler(setup_handlers, exc_model, exc) if handler: op.replace(b.jump(handler, result=op.result))
def _verify_function(func): """Verify a pykit function""" # Verify arguments assert len(func.args) == len(func.type.argtypes) # Verify return presence and type restype = func.type.restype if not restype.is_void and not restype.is_opaque: rets = findallops(func, 'ret') for ret in rets: arg, = ret.args assert arg.type == restype, (arg.type, restype) verify_uniqueness(func) verify_block_order(func) verify_operations(func) verify_uses(func) verify_semantics(func)
def test_inline(self): simple = """ #include <pykit_ir.h> int callee(int i) { return i * i; } int caller(int i) { int x = call(callee, list(i)); return x; } """ mod = from_c(simple) func = mod.get_function("caller") [callsite] = findallops(func, 'call') inline.inline(func, callsite) cfa.run(func) verify(func) assert interp.run(func, args=[10]) == 100 assert len(list(func.blocks)) == 1 assert opcodes(func) == ['mul', 'ret']
def test_inline(self): simple = textwrap.dedent(""" #include <pykit_ir.h> int callee(int i) { return i * i; } int caller(int i) { int x = call(callee, list(i)); return x; } """) mod = from_c(simple) func = mod.get_function("caller") [callsite] = findallops(func, 'call') inline.inline(func, callsite) cfa.run(func) verify(func) assert interp.run(func, args=[10]) == 100 assert len(list(func.blocks)) == 1 assert opcodes(func) == ['mul', 'ret']
def test_inline(self): simple = textwrap.dedent(""" #include <pykit_ir.h> int callee(int i) { return i * i; } int caller(int i) { int x = call(callee, list(i)); return x; } """) mod = from_c(simple) func = mod.get_function("caller") [callsite] = findallops(func, 'call') inline.inline(func, callsite) cfa.run(func) verify(func) assert interp.run(func, args=[10]) == 100 assert len(list(func.blocks)) == 1 self.assertEqual([o for o in opcodes(func) if o != 'convert'], ['mul', 'ret'])
def assert_inlinable(func, call, callee, uses): """ Verify that a function call can be inlined. We can inline generators if they are consumed in a single loop: - iter(g) must be in a loop header - next(g) must be in the loop body :return: None if inlineable, or an exception with a message """ if not isinstance(callee, Function): return CompileError("Cannot inline external function: %s" % (callee,)) yields = findallops(callee, 'yield') if yields: for use in uses[call]: if use.opcode not in ('iter', 'next'): return CompileError( "Cannot inline generator with use %s" % (use,)) if len(uses[call]) != 2: return CompileError("Can only") loops = loop_detection.find_natural_loops(func)