def test_build_tuple_unpack_seq_const(self): # x, y = (3, 4) code = Bytecode([ Instr('LOAD_CONST', 3), Instr('LOAD_CONST', 4), Instr('BUILD_TUPLE', 2), Instr('UNPACK_SEQUENCE', 2), Instr('STORE_NAME', 'x'), Instr('STORE_NAME', 'y') ]) self.check(code, Instr('LOAD_CONST', (3, 4)), Instr('UNPACK_SEQUENCE', 2), Instr('STORE_NAME', 'x'), Instr('STORE_NAME', 'y'))
def test_dont_optimize(self): # x = 3 < 5 code = Bytecode([ Instr('LOAD_CONST', 3), Instr('LOAD_CONST', 5), Instr('COMPARE_OP', Compare.LT), Instr('STORE_NAME', 'x'), Instr('LOAD_CONST', None), Instr('RETURN_VALUE') ]) self.check_dont_optimize(code) # x = (10, 20, 30)[1:] code = Bytecode([ Instr('LOAD_CONST', (10, 20, 30)), Instr('LOAD_CONST', 1), Instr('LOAD_CONST', None), Instr('BUILD_SLICE', 2), Instr('BINARY_SUBSCR'), Instr('STORE_NAME', 'x') ]) self.check_dont_optimize(code)
def test_negative_size_build(self): opnames = ( "BUILD_TUPLE", "BUILD_LIST", "BUILD_SET", ) for opname in opnames: with self.subTest(): code = Bytecode() code.first_lineno = 1 code.extend([Instr(opname, 1)]) with self.assertRaises(RuntimeError): code.compute_stacksize()
def test_dead_code_jump(self): label = Label() code = Bytecode([ Instr('LOAD_NAME', 'x'), Instr('JUMP_ABSOLUTE', label), # dead code Instr('LOAD_NAME', 'y'), Instr('STORE_NAME', 'test'), label, Instr('STORE_NAME', 'test') ]) self.check(code, Instr('LOAD_NAME', 'x'), Instr('STORE_NAME', 'test'))
def test_dont_optimize(self): # x = 3 < 5 code = Bytecode([ Instr("LOAD_CONST", 3), Instr("LOAD_CONST", 5), Instr("COMPARE_OP", Compare.LT), Instr("STORE_NAME", "x"), Instr("LOAD_CONST", None), Instr("RETURN_VALUE"), ]) self.check_dont_optimize(code) # x = (10, 20, 30)[1:] code = Bytecode([ Instr("LOAD_CONST", (10, 20, 30)), Instr("LOAD_CONST", 1), Instr("LOAD_CONST", None), Instr("BUILD_SLICE", 2), Instr("BINARY_SUBSCR"), Instr("STORE_NAME", "x"), ]) self.check_dont_optimize(code)
def test_dead_code_jump(self): label = Label() code = Bytecode([ Instr("LOAD_NAME", "x"), Instr("JUMP_ABSOLUTE", label), # dead code Instr("LOAD_NAME", "y"), Instr("STORE_NAME", "test"), label, Instr("STORE_NAME", "test"), ]) self.check(code, Instr("LOAD_NAME", "x"), Instr("STORE_NAME", "test"))
def test_build_list_unpack_seq(self): for build_list in ('BUILD_TUPLE', 'BUILD_LIST'): # x, = [a] code = Bytecode([ Instr('LOAD_NAME', 'a'), Instr(build_list, 1), Instr('UNPACK_SEQUENCE', 1), Instr('STORE_NAME', 'x') ]) self.check(code, Instr('LOAD_NAME', 'a'), Instr('STORE_NAME', 'x')) # x, y = [a, b] code = Bytecode([ Instr('LOAD_NAME', 'a'), Instr('LOAD_NAME', 'b'), Instr(build_list, 2), Instr('UNPACK_SEQUENCE', 2), Instr('STORE_NAME', 'x'), Instr('STORE_NAME', 'y') ]) self.check(code, Instr('LOAD_NAME', 'a'), Instr('LOAD_NAME', 'b'), Instr('ROT_TWO'), Instr('STORE_NAME', 'x'), Instr('STORE_NAME', 'y')) # x, y, z = [a, b, c] code = Bytecode([ Instr('LOAD_NAME', 'a'), Instr('LOAD_NAME', 'b'), Instr('LOAD_NAME', 'c'), Instr(build_list, 3), Instr('UNPACK_SEQUENCE', 3), Instr('STORE_NAME', 'x'), Instr('STORE_NAME', 'y'), Instr('STORE_NAME', 'z') ]) self.check(code, Instr('LOAD_NAME', 'a'), Instr('LOAD_NAME', 'b'), Instr('LOAD_NAME', 'c'), Instr('ROT_THREE'), Instr('ROT_TWO'), Instr('STORE_NAME', 'x'), Instr('STORE_NAME', 'y'), Instr('STORE_NAME', 'z'))
def test_handling_of_extended_arg(self): code = Bytecode() code.first_lineno = 3 code.extend( [ Instr("LOAD_CONST", 7), Instr("STORE_NAME", "x"), Instr("EXTENDED_ARG", 1), Instr("LOAD_CONST", 8), Instr("STORE_NAME", "y"), ] ) self.assertEqual(code.compute_stacksize(), 1)
def test_compute_jumps_convergence(self): # Consider the following sequence of instructions: # # JUMP_ABSOLUTE Label1 # JUMP_ABSOLUTE Label2 # ...126 instructions... # Label1: Offset 254 on first pass, 256 second pass # NOP # ... many more instructions ... # Label2: Offset > 256 on first pass # # On first pass of compute_jumps(), Label2 will be at address 254, so # that value encodes into the single byte arg of JUMP_ABSOLUTE. # # On second pass compute_jumps() the instr at Label1 will have offset # of 256 so will also be given an EXTENDED_ARG. # # Thus we need to make an additional pass. This test only verifies # case where 2 passes is insufficient but three is enough. # # On Python > 3.10 we need to double the number since the offset is now # in term of instructions and not bytes. # Create code from comment above. code = Bytecode() label1 = Label() label2 = Label() nop = "NOP" code.append(Instr("JUMP_ABSOLUTE", label1)) code.append(Instr("JUMP_ABSOLUTE", label2)) # Need 254 * 2 + 2 since the arg will change by 1 instruction rather than 2 # bytes. for x in range(4, 510 if OFFSET_AS_INSTRUCTION else 254, 2): code.append(Instr(nop)) code.append(label1) code.append(Instr(nop)) for x in range( 514 if OFFSET_AS_INSTRUCTION else 256, 600 if OFFSET_AS_INSTRUCTION else 300, 2, ): code.append(Instr(nop)) code.append(label2) code.append(Instr(nop)) # This should pass by default. code.to_code() # Try with max of two passes: it should raise with self.assertRaises(RuntimeError): code.to_code(compute_jumps_passes=2)
def test_compare_op_unary_not(self): for op, not_op in ( (Compare.IN, Compare.NOT_IN), # in => not in (Compare.NOT_IN, Compare.IN), # not in => in (Compare.IS, Compare.IS_NOT), # is => is not (Compare.IS_NOT, Compare.IS), # is not => is ): code = Bytecode([ Instr("LOAD_NAME", "a"), Instr("LOAD_NAME", "b"), Instr("COMPARE_OP", op), Instr("UNARY_NOT"), Instr("STORE_NAME", "x"), ]) self.check( code, Instr("LOAD_NAME", "a"), Instr("LOAD_NAME", "b"), Instr("COMPARE_OP", not_op), Instr("STORE_NAME", "x"), ) # don't optimize: # x = not (a and b is True) label_instr5 = Label() code = Bytecode([ Instr("LOAD_NAME", "a"), Instr("JUMP_IF_FALSE_OR_POP", label_instr5), Instr("LOAD_NAME", "b"), Instr("LOAD_CONST", True), Instr("COMPARE_OP", Compare.IS), label_instr5, Instr("UNARY_NOT"), Instr("STORE_NAME", "x"), Instr("LOAD_CONST", None), Instr("RETURN_VALUE"), ]) self.check_dont_optimize(code)
def test_uncond_jump_to_uncond_jump(self): # Replace JUMP_FORWARD t1 jumping to JUMP_FORWARD t2 # with JUMP_ABSOLUTE t2 label = Label() label2 = Label() label3 = Label() label4 = Label() code = Bytecode([ Instr("LOAD_NAME", "test"), Instr("POP_JUMP_IF_TRUE", label), # redundant jump Instr("JUMP_FORWARD", label2), label, Instr("LOAD_CONST", 1), Instr("STORE_NAME", "x"), Instr("LOAD_NAME", "test"), Instr("POP_JUMP_IF_TRUE", label3), label2, Instr("JUMP_FORWARD", label4), label3, Instr("LOAD_CONST", 1), Instr("STORE_NAME", "x"), label4, Instr("LOAD_CONST", None), Instr("RETURN_VALUE"), ]) label = Label() label3 = Label() label4 = Label() self.check( code, Instr("LOAD_NAME", "test"), Instr("POP_JUMP_IF_TRUE", label), # JUMP_FORWARD label2 was replaced with JUMP_ABSOLUTE label4 Instr("JUMP_ABSOLUTE", label4), label, Instr("LOAD_CONST", 1), Instr("STORE_NAME", "x"), Instr("LOAD_NAME", "test"), Instr("POP_JUMP_IF_TRUE", label3), Instr("JUMP_FORWARD", label4), label3, Instr("LOAD_CONST", 1), Instr("STORE_NAME", "x"), label4, Instr("LOAD_CONST", None), Instr("RETURN_VALUE"), )
def test_negative_size_unary(self): opnames = ( "UNARY_POSITIVE", "UNARY_NEGATIVE", "UNARY_NOT", "UNARY_INVERT", ) for opname in opnames: with self.subTest(): code = Bytecode() code.first_lineno = 1 code.extend([Instr(opname)]) with self.assertRaises(RuntimeError): code.compute_stacksize()
def r_compile(): jit_func = Aware.f(self, id(start_func)) bc = Bytecode() bc.append(PyInstr(InstrNames.LOAD_CONST, jit_func)) bc.extend([load_arg(each, cellvars, lineno) for each in argnames]) bc.extend([ PyInstr(InstrNames.CALL_FUNCTION, len(argnames)), PyInstr(InstrNames.RETURN_VALUE) ]) bc._copy_attr_from(code) start_func.__code__ = bc.to_code() start_func.__jit__ = jit_func return jit_func
def test_negative_size_unary_with_disable_check_of_pre_and_post(self): opnames = ( "UNARY_POSITIVE", "UNARY_NEGATIVE", "UNARY_NOT", "UNARY_INVERT", ) for opname in opnames: with self.subTest(): code = Bytecode() code.first_lineno = 1 code.extend([Instr(opname)]) co = code.to_code(check_pre_and_post=False) self.assertEqual(co.co_stacksize, 0)
def test_label(self): code = Bytecode() label = Label() code.extend([Instr('LOAD_CONST', 'hello', lineno=1), Instr('JUMP_FORWARD', label, lineno=1), label, Instr('POP_TOP', lineno=1)]) code = code.to_concrete_bytecode() expected = [ConcreteInstr('LOAD_CONST', 0, lineno=1), ConcreteInstr('JUMP_FORWARD', 0, lineno=1), ConcreteInstr('POP_TOP', lineno=1)] self.assertListEqual(list(code), expected) self.assertListEqual(code.consts, ['hello'])
def _make_bytecode(self, source, template_locator): instructions = [] symbol_table = {"write_func": io.StringIO.write} parser_obj = parser.Parser(self._template_locator) sequence = parser_obj.parse(self._get_chunks(source)) for item in sequence.elements: instructions += item.make_bytecode(symbol_table) bytecode = Bytecode(instructions + [Instr("LOAD_CONST", None), Instr("RETURN_VALUE")]) return bytecode.to_code()
def test_handling_of_set_lineno(self): code = Bytecode() code.first_lineno = 3 code.extend([ Instr("LOAD_CONST", 7), Instr("STORE_NAME", "x"), SetLineno(4), Instr("LOAD_CONST", 8), Instr("STORE_NAME", "y"), SetLineno(5), Instr("LOAD_CONST", 9), Instr("STORE_NAME", "z"), ]) self.assertEqual(code.compute_stacksize(), 1)
def test_jump_if_false_to_jump_if_false(self): # Replace JUMP_IF_FALSE_OR_POP jumping to POP_JUMP_IF_FALSE <label> # with POP_JUMP_IF_FALSE <label> # # while n > 0 and start > 3: # func() label_instr1 = Label() label_instr15 = Label() label_instr17 = Label() label_instr9 = Label() code = Bytecode([ Instr('SETUP_LOOP', label_instr17), label_instr1, Instr('LOAD_NAME', 'n'), Instr('LOAD_CONST', 0), Instr('COMPARE_OP', Compare.GT), # JUMP_IF_FALSE_OR_POP jumps to POP_JUMP_IF_FALSE # which jumps to label_instr15 Instr('JUMP_IF_FALSE_OR_POP', label_instr9), Instr('LOAD_NAME', 'start'), Instr('LOAD_CONST', 3), Instr('COMPARE_OP', Compare.GT), label_instr9, Instr('POP_JUMP_IF_FALSE', label_instr15), Instr('LOAD_NAME', 'func'), Instr('CALL_FUNCTION', 0), Instr('POP_TOP'), Instr('JUMP_ABSOLUTE', label_instr1), label_instr15, Instr('POP_BLOCK'), label_instr17, Instr('LOAD_CONST', None), Instr('RETURN_VALUE') ]) label_instr1 = Label() label_instr14 = Label() label_instr16 = Label() self.check(code, Instr('SETUP_LOOP', label_instr16), label_instr1, Instr('LOAD_NAME', 'n'), Instr('LOAD_CONST', 0), Instr('COMPARE_OP', Compare.GT), Instr('POP_JUMP_IF_FALSE', label_instr14), Instr('LOAD_NAME', 'start'), Instr('LOAD_CONST', 3), Instr('COMPARE_OP', Compare.GT), Instr('POP_JUMP_IF_FALSE', label_instr14), Instr('LOAD_NAME', 'func'), Instr('CALL_FUNCTION', 0), Instr('POP_TOP'), Instr('JUMP_ABSOLUTE', label_instr1), label_instr14, Instr('POP_BLOCK'), label_instr16, Instr('LOAD_CONST', None), Instr('RETURN_VALUE'))
def test_unconditional_jump_to_return(self): source = """ def func(): if test: if test2: x = 10 else: x = 20 else: x = 30 """ label_instr11 = Label() label_instr14 = Label() label_instr7 = Label() code = Bytecode([ Instr('LOAD_GLOBAL', 'test', lineno=2), Instr('POP_JUMP_IF_FALSE', label_instr11, lineno=2), Instr('LOAD_GLOBAL', 'test2', lineno=3), Instr('POP_JUMP_IF_FALSE', label_instr7, lineno=3), Instr('LOAD_CONST', 10, lineno=4), Instr('STORE_FAST', 'x', lineno=4), Instr('JUMP_ABSOLUTE', label_instr14, lineno=4), label_instr7, Instr('LOAD_CONST', 20, lineno=6), Instr('STORE_FAST', 'x', lineno=6), Instr('JUMP_FORWARD', label_instr14, lineno=6), label_instr11, Instr('LOAD_CONST', 30, lineno=8), Instr('STORE_FAST', 'x', lineno=8), label_instr14, Instr('LOAD_CONST', None, lineno=8), Instr('RETURN_VALUE', lineno=8) ]) label1 = Label() label3 = Label() label4 = Label() self.check(code, Instr('LOAD_GLOBAL', 'test', lineno=2), Instr('POP_JUMP_IF_FALSE', label3, lineno=2), Instr('LOAD_GLOBAL', 'test2', lineno=3), Instr('POP_JUMP_IF_FALSE', label1, lineno=3), Instr('LOAD_CONST', 10, lineno=4), Instr('STORE_FAST', 'x', lineno=4), Instr('JUMP_ABSOLUTE', label4, lineno=4), label1, Instr('LOAD_CONST', 20, lineno=6), Instr('STORE_FAST', 'x', lineno=6), Instr('JUMP_FORWARD', label4, lineno=6), label3, Instr('LOAD_CONST', 30, lineno=8), Instr('STORE_FAST', 'x', lineno=8), label4, Instr('LOAD_CONST', None, lineno=8), Instr('RETURN_VALUE', lineno=8))
def test_uncond_jump_to_uncond_jump(self): # Replace JUMP_FORWARD t1 jumping to JUMP_FORWARD t2 # with JUMP_ABSOLUTE t2 label = Label() label2 = Label() label3 = Label() label4 = Label() code = Bytecode([ Instr('LOAD_NAME', 'test'), Instr('POP_JUMP_IF_TRUE', label), # redundant jump Instr('JUMP_FORWARD', label2), label, Instr('LOAD_CONST', 1), Instr('STORE_NAME', 'x'), Instr('LOAD_NAME', 'test'), Instr('POP_JUMP_IF_TRUE', label3), label2, Instr('JUMP_FORWARD', label4), label3, Instr('LOAD_CONST', 1), Instr('STORE_NAME', 'x'), label4, Instr('LOAD_CONST', None), Instr('RETURN_VALUE') ]) label = Label() label3 = Label() label4 = Label() self.check( code, Instr('LOAD_NAME', 'test'), Instr('POP_JUMP_IF_TRUE', label), # JUMP_FORWARD label2 was replaced with JUMP_ABSOLUTE label4 Instr('JUMP_ABSOLUTE', label4), label, Instr('LOAD_CONST', 1), Instr('STORE_NAME', 'x'), Instr('LOAD_NAME', 'test'), Instr('POP_JUMP_IF_TRUE', label3), Instr('JUMP_FORWARD', label4), label3, Instr('LOAD_CONST', 1), Instr('STORE_NAME', 'x'), label4, Instr('LOAD_CONST', None), Instr('RETURN_VALUE'))
def test_cellvars(self): code = Bytecode() code.cellvars = ["x"] code.freevars = ["y"] code.extend([ Instr("LOAD_DEREF", CellVar("x"), lineno=1), Instr("LOAD_DEREF", FreeVar("y"), lineno=1), ]) concrete = code.to_concrete_bytecode() self.assertEqual(concrete.cellvars, ["x"]) self.assertEqual(concrete.freevars, ["y"]) code.extend([ ConcreteInstr("LOAD_DEREF", 0, lineno=1), ConcreteInstr("LOAD_DEREF", 1, lineno=1), ])
def test_cellvars(self): code = Bytecode() code.cellvars = ['x'] code.freevars = ['y'] code.extend([ Instr('LOAD_DEREF', CellVar('x'), lineno=1), Instr('LOAD_DEREF', FreeVar('y'), lineno=1) ]) concrete = code.to_concrete_bytecode() self.assertEqual(concrete.cellvars, ['x']) self.assertEqual(concrete.freevars, ['y']) code.extend([ ConcreteInstr("LOAD_DEREF", 0, lineno=1), ConcreteInstr("LOAD_DEREF", 1, lineno=1) ])
def test_build_list_unpack_seq_const(self): # x, y, z = [3, 4, 5] code = Bytecode([ Instr('LOAD_CONST', 3), Instr('LOAD_CONST', 4), Instr('LOAD_CONST', 5), Instr('BUILD_LIST', 3), Instr('UNPACK_SEQUENCE', 3), Instr('STORE_NAME', 'x'), Instr('STORE_NAME', 'y'), Instr('STORE_NAME', 'z') ]) self.check(code, Instr('LOAD_CONST', 5), Instr('LOAD_CONST', 4), Instr('LOAD_CONST', 3), Instr('STORE_NAME', 'x'), Instr('STORE_NAME', 'y'), Instr('STORE_NAME', 'z'))
def test_max_size(self): max_size = 3 with mock.patch.object(peephole_opt, "MAX_SIZE", max_size): # optimized binary operation: size <= maximum size # # (9,) * size size = max_size result = (9, ) * size code = Bytecode([ Instr("LOAD_CONST", 9), Instr("BUILD_TUPLE", 1), Instr("LOAD_CONST", size), Instr("BINARY_MULTIPLY"), Instr("STORE_NAME", "x"), ]) self.check(code, Instr("LOAD_CONST", result), Instr("STORE_NAME", "x")) # don't optimize binary operation: size > maximum size # # x = (9,) * size size = max_size + 1 code = Bytecode([ Instr("LOAD_CONST", 9), Instr("BUILD_TUPLE", 1), Instr("LOAD_CONST", size), Instr("BINARY_MULTIPLY"), Instr("STORE_NAME", "x"), ]) self.check( code, Instr("LOAD_CONST", (9, )), Instr("LOAD_CONST", size), Instr("BINARY_MULTIPLY"), Instr("STORE_NAME", "x"), )
def test_dont_merge_constants(self): # test two constants which are equal but have a different type code = Bytecode() code.extend([Instr('LOAD_CONST', 5, lineno=1), Instr('LOAD_CONST', 5.0, lineno=1), Instr('LOAD_CONST', -0.0, lineno=1), Instr('LOAD_CONST', +0.0, lineno=1)]) code = code.to_concrete_bytecode() expected = [ConcreteInstr('LOAD_CONST', 0, lineno=1), ConcreteInstr('LOAD_CONST', 1, lineno=1), ConcreteInstr('LOAD_CONST', 2, lineno=1), ConcreteInstr('LOAD_CONST', 3, lineno=1)] self.assertListEqual(list(code), expected) self.assertListEqual(code.consts, [5, 5.0, -0.0, +0.0])
def test_build_set(self): # test = x in {1, 2, 3} code = Bytecode([ Instr('LOAD_NAME', 'x'), Instr('LOAD_CONST', 1), Instr('LOAD_CONST', 2), Instr('LOAD_CONST', 3), Instr('BUILD_SET', 3), Instr('COMPARE_OP', Compare.IN), Instr('STORE_NAME', 'test') ]) self.check(code, Instr('LOAD_NAME', 'x'), Instr('LOAD_CONST', frozenset((1, 2, 3))), Instr('COMPARE_OP', Compare.IN), Instr('STORE_NAME', 'test'))
def test_build_list(self): # test = x in [1, 2, 3] code = Bytecode([ Instr('LOAD_NAME', 'x'), Instr('LOAD_CONST', 1), Instr('LOAD_CONST', 2), Instr('LOAD_CONST', 3), Instr('BUILD_LIST', 3), Instr('COMPARE_OP', Compare.IN), Instr('STORE_NAME', 'test') ]) self.check(code, Instr('LOAD_NAME', 'x'), Instr('LOAD_CONST', (1, 2, 3)), Instr('COMPARE_OP', Compare.IN), Instr('STORE_NAME', 'test'))
def test_compute_jumps_convergence(self): # Consider the following sequence of instructions: # # JUMP_ABSOLUTE Label1 # JUMP_ABSOLUTE Label2 # ...126 instructions... # Label1: Offset 254 on first pass, 256 second pass # NOP # ... many more instructions ... # Label2: Offset > 256 on first pass # # On first pass of compute_jumps(), Label2 will be at address 254, so # that value encodes into the single byte arg of JUMP_ABSOLUTE. # # On second pass compute_jumps() the instr at Label1 will have offset # of 256 so will also be given an EXTENDED_ARG. # # Thus we need to make an additional pass. This test only verifies # case where 2 passes is insufficient but three is enough. if not WORDCODE: # Could be done pre-WORDCODE, but that requires 2**16 bytes of # code. return # Create code from comment above. code = Bytecode() label1 = Label() label2 = Label() nop = 'UNARY_POSITIVE' # don't use NOP, dis.stack_effect will raise code.append(Instr('JUMP_ABSOLUTE', label1)) code.append(Instr('JUMP_ABSOLUTE', label2)) for x in range(4, 254, 2): code.append(Instr(nop)) code.append(label1) code.append(Instr(nop)) for x in range(256, 300, 2): code.append(Instr(nop)) code.append(label2) code.append(Instr(nop)) # This should pass by default. code.to_code() # Try with max of two passes: it should raise with self.assertRaises(RuntimeError): code.to_code(compute_jumps_passes=2)
def conditional_jump_example_bytecode() -> Bytecode: label_else = Label() label_print = Label() byte_code = Bytecode([ Instr("LOAD_NAME", "print"), Instr("LOAD_NAME", "test"), Instr("POP_JUMP_IF_FALSE", label_else), Instr("LOAD_CONST", "yes"), Instr("JUMP_FORWARD", label_print), label_else, Instr("LOAD_CONST", "no"), label_print, Instr("CALL_FUNCTION", 1), Instr("LOAD_CONST", None), Instr("RETURN_VALUE"), ]) return byte_code
def test_label_at_the_end(self): label = Label() code = Bytecode([Instr('LOAD_NAME', 'x'), Instr('UNARY_NOT'), Instr('POP_JUMP_IF_FALSE', label), Instr('LOAD_CONST', 9), Instr('STORE_NAME', 'y'), label]) cfg = ControlFlowGraph.from_bytecode(code) self.assertBlocksEqual(cfg, [Instr('LOAD_NAME', 'x'), Instr('UNARY_NOT'), Instr('POP_JUMP_IF_FALSE', cfg[2])], [Instr('LOAD_CONST', 9), Instr('STORE_NAME', 'y')], [])