[Python-checkins] r69961 - in python/branches/py3k: Doc/library/dis.rst Include/opcode.h Lib/opcode.py Lib/test/test_peepholer.py Python/ceval.c Python/compile.c Python/import.c Python/opcode_targets.h Python/peephole.c

jeffrey.yasskin python-checkins at python.org
Wed Feb 25 03:25:05 CET 2009


Author: jeffrey.yasskin
Date: Wed Feb 25 03:25:04 2009
New Revision: 69961
Log:
http://bugs.python.org/issue4715
This patch by Antoine Pitrou optimizes the bytecode for conditional branches by
merging the following "POP_TOP" instruction into the conditional jump. For
example, the list comprehension "[x for x in l if not x]" produced the
following bytecode:
 1 0 BUILD_LIST 0 
 3 LOAD_FAST 0 (.0) 
 >> 6 FOR_ITER 23 (to 32) 
 9 STORE_FAST 1 (x) 
 12 LOAD_FAST 1 (x) 
 15 JUMP_IF_TRUE 10 (to 28) 
 18 POP_TOP 
 19 LOAD_FAST 1 (x) 
 22 LIST_APPEND 2 
 25 JUMP_ABSOLUTE 6 
 >> 28 POP_TOP 
 29 JUMP_ABSOLUTE 6 
 >> 32 RETURN_VALUE 
but after the patch it produces the following bytecode:
 1 0 BUILD_LIST 0 
 3 LOAD_FAST 0 (.0) 
 >> 6 FOR_ITER 18 (to 27) 
 9 STORE_FAST 1 (x) 
 12 LOAD_FAST 1 (x) 
 15 POP_JUMP_IF_TRUE 6 
 18 LOAD_FAST 1 (x) 
 21 LIST_APPEND 2 
 24 JUMP_ABSOLUTE 6 
 >> 27 RETURN_VALUE 
Notice that not only the code is shorter, but the conditional jump
(POP_JUMP_IF_TRUE) jumps right to the start of the loop instead of going through
the JUMP_ABSOLUTE at the end. "continue" statements are helped
similarly.
Furthermore, the old jump opcodes (JUMP_IF_FALSE, JUMP_IF_TRUE) have been
replaced by two new opcodes:
- JUMP_IF_TRUE_OR_POP, which jumps if true and pops otherwise
- JUMP_IF_FALSE_OR_POP, which jumps if false and pops otherwise
Modified:
 python/branches/py3k/Doc/library/dis.rst
 python/branches/py3k/Include/opcode.h
 python/branches/py3k/Lib/opcode.py
 python/branches/py3k/Lib/test/test_peepholer.py
 python/branches/py3k/Python/ceval.c
 python/branches/py3k/Python/compile.c
 python/branches/py3k/Python/import.c
 python/branches/py3k/Python/opcode_targets.h
 python/branches/py3k/Python/peephole.c
Modified: python/branches/py3k/Doc/library/dis.rst
==============================================================================
--- python/branches/py3k/Doc/library/dis.rst	(original)
+++ python/branches/py3k/Doc/library/dis.rst	Wed Feb 25 03:25:04 2009
@@ -595,16 +595,26 @@
 Increments bytecode counter by *delta*.
 
 
-.. opcode:: JUMP_IF_TRUE (delta)
+.. opcode:: POP_JUMP_IF_TRUE (target)
 
- If TOS is true, increment the bytecode counter by *delta*. TOS is left on the
- stack.
+ If TOS is true, sets the bytecode counter to *target*. TOS is popped.
 
 
-.. opcode:: JUMP_IF_FALSE (delta)
+.. opcode:: POP_JUMP_IF_FALSE (target)
 
- If TOS is false, increment the bytecode counter by *delta*. TOS is not
- changed.
+ If TOS is false, sets the bytecode counter to *target*. TOS is popped.
+
+
+.. opcode:: JUMP_IF_TRUE_OR_POP (target)
+
+ If TOS is true, sets the bytecode counter to *target* and leaves TOS
+ on the stack. Otherwise (TOS is false), TOS is popped.
+
+
+.. opcode:: JUMP_IF_FALSE_OR_POP (target)
+
+ If TOS is false, sets the bytecode counter to *target* and leaves
+ TOS on the stack. Otherwise (TOS is true), TOS is popped.
 
 
 .. opcode:: JUMP_ABSOLUTE (target)
Modified: python/branches/py3k/Include/opcode.h
==============================================================================
--- python/branches/py3k/Include/opcode.h	(original)
+++ python/branches/py3k/Include/opcode.h	Wed Feb 25 03:25:04 2009
@@ -96,9 +96,11 @@
 #define IMPORT_FROM	109	/* Index in name list */
 
 #define JUMP_FORWARD	110	/* Number of bytes to skip */
-#define JUMP_IF_FALSE	111	/* "" */
-#define JUMP_IF_TRUE	112	/* "" */
-#define JUMP_ABSOLUTE	113	/* Target byte offset from beginning of code */
+#define JUMP_IF_FALSE_OR_POP 111	/* Target byte offset from beginning of code */
+#define JUMP_IF_TRUE_OR_POP 112	/* "" */
+#define JUMP_ABSOLUTE	113	/* "" */
+#define POP_JUMP_IF_FALSE 114	/* "" */
+#define POP_JUMP_IF_TRUE 115	/* "" */
 
 #define LOAD_GLOBAL	116	/* Index in name list */
 
Modified: python/branches/py3k/Lib/opcode.py
==============================================================================
--- python/branches/py3k/Lib/opcode.py	(original)
+++ python/branches/py3k/Lib/opcode.py	Wed Feb 25 03:25:04 2009
@@ -131,9 +131,11 @@
 name_op('IMPORT_FROM', 109) # Index in name list
 
 jrel_op('JUMP_FORWARD', 110) # Number of bytes to skip
-jrel_op('JUMP_IF_FALSE', 111) # ""
-jrel_op('JUMP_IF_TRUE', 112) # ""
-jabs_op('JUMP_ABSOLUTE', 113) # Target byte offset from beginning of code
+jabs_op('JUMP_IF_FALSE_OR_POP', 111) # Target byte offset from beginning of code
+jabs_op('JUMP_IF_TRUE_OR_POP', 112) # ""
+jabs_op('JUMP_ABSOLUTE', 113) # ""
+jabs_op('POP_JUMP_IF_FALSE', 114) # ""
+jabs_op('POP_JUMP_IF_TRUE', 115) # ""
 
 name_op('LOAD_GLOBAL', 116) # Index in name list
 
Modified: python/branches/py3k/Lib/test/test_peepholer.py
==============================================================================
--- python/branches/py3k/Lib/test/test_peepholer.py	(original)
+++ python/branches/py3k/Lib/test/test_peepholer.py	Wed Feb 25 03:25:04 2009
@@ -19,14 +19,14 @@
 class TestTranforms(unittest.TestCase):
 
 def test_unot(self):
- # UNARY_NOT JUMP_IF_FALSE POP_TOP --> JUMP_IF_TRUE POP_TOP'
+ # UNARY_NOT POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE'
 def unot(x):
 if not x == 2:
 del x
 asm = disassemble(unot)
- for elem in ('UNARY_NOT', 'JUMP_IF_FALSE'):
+ for elem in ('UNARY_NOT', 'POP_JUMP_IF_FALSE'):
 self.assert_(elem not in asm)
- for elem in ('JUMP_IF_TRUE', 'POP_TOP'):
+ for elem in ('POP_JUMP_IF_TRUE',):
 self.assert_(elem in asm)
 
 def test_elim_inversion_of_is_or_in(self):
@@ -64,13 +64,13 @@
 self.assert_('LOAD_GLOBAL' not in disassemble(f))
 
 def test_while_one(self):
- # Skip over: LOAD_CONST trueconst JUMP_IF_FALSE xx POP_TOP
+ # Skip over: LOAD_CONST trueconst POP_JUMP_IF_FALSE xx
 def f():
 while 1:
 pass
 return list
 asm = disassemble(f)
- for elem in ('LOAD_CONST', 'JUMP_IF_FALSE'):
+ for elem in ('LOAD_CONST', 'POP_JUMP_IF_FALSE'):
 self.assert_(elem not in asm)
 for elem in ('JUMP_ABSOLUTE',):
 self.assert_(elem in asm)
Modified: python/branches/py3k/Python/ceval.c
==============================================================================
--- python/branches/py3k/Python/ceval.c	(original)
+++ python/branches/py3k/Python/ceval.c	Wed Feb 25 03:25:04 2009
@@ -1295,7 +1295,6 @@
 			SETLOCAL(oparg, v);
 			FAST_DISPATCH();
 
-		PREDICTED(POP_TOP);
 		TARGET(POP_TOP)
 			v = POP();
 			Py_DECREF(v);
@@ -2204,8 +2203,8 @@
 			Py_DECREF(w);
 			SET_TOP(x);
 			if (x == NULL) break;
-			PREDICT(JUMP_IF_FALSE);
-			PREDICT(JUMP_IF_TRUE);
+			PREDICT(POP_JUMP_IF_FALSE);
+			PREDICT(POP_JUMP_IF_TRUE);
 			DISPATCH();
 
 		TARGET(IMPORT_NAME)
@@ -2282,41 +2281,45 @@
 			JUMPBY(oparg);
 			FAST_DISPATCH();
 
-		PREDICTED_WITH_ARG(JUMP_IF_FALSE);
-		TARGET(JUMP_IF_FALSE)
-			w = TOP();
+		PREDICTED_WITH_ARG(POP_JUMP_IF_FALSE);
+		TARGET(POP_JUMP_IF_FALSE)
+			w = POP();
 			if (w == Py_True) {
-				PREDICT(POP_TOP);
+				Py_DECREF(w);
 				FAST_DISPATCH();
 			}
 			if (w == Py_False) {
-				JUMPBY(oparg);
+				Py_DECREF(w);
+				JUMPTO(oparg);
 				FAST_DISPATCH();
 			}
 			err = PyObject_IsTrue(w);
+			Py_DECREF(w);
 			if (err > 0)
 				err = 0;
 			else if (err == 0)
-				JUMPBY(oparg);
+				JUMPTO(oparg);
 			else
 				break;
 			DISPATCH();
 
-		PREDICTED_WITH_ARG(JUMP_IF_TRUE);
-		TARGET(JUMP_IF_TRUE)
-			w = TOP();
+		PREDICTED_WITH_ARG(POP_JUMP_IF_TRUE);
+		TARGET(POP_JUMP_IF_TRUE)
+			w = POP();
 			if (w == Py_False) {
-				PREDICT(POP_TOP);
+				Py_DECREF(w);
 				FAST_DISPATCH();
 			}
 			if (w == Py_True) {
-				JUMPBY(oparg);
+				Py_DECREF(w);
+				JUMPTO(oparg);
 				FAST_DISPATCH();
 			}
 			err = PyObject_IsTrue(w);
+			Py_DECREF(w);
 			if (err > 0) {
 				err = 0;
-				JUMPBY(oparg);
+				JUMPTO(oparg);
 			}
 			else if (err == 0)
 				;
@@ -2324,6 +2327,53 @@
 				break;
 			DISPATCH();
 
+		TARGET(JUMP_IF_FALSE_OR_POP)
+			w = TOP();
+			if (w == Py_True) {
+				STACKADJ(-1);
+				Py_DECREF(w);
+				FAST_DISPATCH();
+			}
+			if (w == Py_False) {
+				JUMPTO(oparg);
+				FAST_DISPATCH();
+			}
+			err = PyObject_IsTrue(w);
+			if (err > 0) {
+				STACKADJ(-1);
+				Py_DECREF(w);
+				err = 0;
+			}
+			else if (err == 0)
+				JUMPTO(oparg);
+			else
+				break;
+			DISPATCH();
+
+		TARGET(JUMP_IF_TRUE_OR_POP)
+			w = TOP();
+			if (w == Py_False) {
+				STACKADJ(-1);
+				Py_DECREF(w);
+				FAST_DISPATCH();
+			}
+			if (w == Py_True) {
+				JUMPTO(oparg);
+				FAST_DISPATCH();
+			}
+			err = PyObject_IsTrue(w);
+			if (err > 0) {
+				err = 0;
+				JUMPTO(oparg);
+			}
+			else if (err == 0) {
+				STACKADJ(-1);
+				Py_DECREF(w);
+			}
+			else
+				break;
+			DISPATCH();
+
 		PREDICTED_WITH_ARG(JUMP_ABSOLUTE);
 		TARGET(JUMP_ABSOLUTE)
 			JUMPTO(oparg);
Modified: python/branches/py3k/Python/compile.c
==============================================================================
--- python/branches/py3k/Python/compile.c	(original)
+++ python/branches/py3k/Python/compile.c	Wed Feb 25 03:25:04 2009
@@ -818,11 +818,15 @@
 			return 1;
 
 		case JUMP_FORWARD:
-		case JUMP_IF_FALSE:
-		case JUMP_IF_TRUE:
+		case JUMP_IF_TRUE_OR_POP: /* -1 if jump not taken */
+		case JUMP_IF_FALSE_OR_POP: /* "" */
 		case JUMP_ABSOLUTE:
 			return 0;
 
+		case POP_JUMP_IF_FALSE:
+		case POP_JUMP_IF_TRUE:
+			return -1;
+
 		case LOAD_GLOBAL:
 			return 1;
 
@@ -1664,12 +1668,10 @@
 	if (next == NULL)
 		return 0;
 	VISIT(c, expr, e->v.IfExp.test);
-	ADDOP_JREL(c, JUMP_IF_FALSE, next);
-	ADDOP(c, POP_TOP);
+	ADDOP_JABS(c, POP_JUMP_IF_FALSE, next);
 	VISIT(c, expr, e->v.IfExp.body);
 	ADDOP_JREL(c, JUMP_FORWARD, end);
 	compiler_use_next_block(c, next);
-	ADDOP(c, POP_TOP);
 	VISIT(c, expr, e->v.IfExp.orelse);
 	compiler_use_next_block(c, end);
 	return 1;
@@ -1732,9 +1734,6 @@
 	end = compiler_new_block(c);
 	if (end == NULL)
 		return 0;
-	next = compiler_new_block(c);
-	if (next == NULL)
-	 return 0;
 	
 	constant = expr_constant(s->v.If.test);
 	/* constant = 0: "if 0"
@@ -1746,15 +1745,21 @@
 	} else if (constant == 1) {
 		VISIT_SEQ(c, stmt, s->v.If.body);
 	} else {
+		if (s->v.If.orelse) {
+			next = compiler_new_block(c);
+			if (next == NULL)
+			 return 0;
+		}
+		else
+			next = end;
 		VISIT(c, expr, s->v.If.test);
-		ADDOP_JREL(c, JUMP_IF_FALSE, next);
-		ADDOP(c, POP_TOP);
+		ADDOP_JABS(c, POP_JUMP_IF_FALSE, next);
 		VISIT_SEQ(c, stmt, s->v.If.body);
 		ADDOP_JREL(c, JUMP_FORWARD, end);
-		compiler_use_next_block(c, next);
-		ADDOP(c, POP_TOP);
-		if (s->v.If.orelse)
+		if (s->v.If.orelse) {
+			compiler_use_next_block(c, next);
 			VISIT_SEQ(c, stmt, s->v.If.orelse);
+		}
 	}
 	compiler_use_next_block(c, end);
 	return 1;
@@ -1828,8 +1833,7 @@
 		 so we need to set an extra line number. */
 		c->u->u_lineno_set = 0;
 		VISIT(c, expr, s->v.While.test);
-		ADDOP_JREL(c, JUMP_IF_FALSE, anchor);
-		ADDOP(c, POP_TOP);
+		ADDOP_JABS(c, POP_JUMP_IF_FALSE, anchor);
 	}
 	VISIT_SEQ(c, stmt, s->v.While.body);
 	ADDOP_JABS(c, JUMP_ABSOLUTE, loop);
@@ -1840,7 +1844,6 @@
 
 	if (constant == -1) {
 		compiler_use_next_block(c, anchor);
-		ADDOP(c, POP_TOP);
 		ADDOP(c, POP_BLOCK);
 	}
 	compiler_pop_fblock(c, LOOP, loop);
@@ -1947,7 +1950,7 @@
 }
 
 /*
- Code generated for "try: S except E1, V1: S1 except E2, V2: S2 ...":
+ Code generated for "try: S except E1 as V1: S1 except E2 as V2: S2 ...":
 (The contents of the value stack is shown in [], with the top
 at the right; 'tb' is trace-back info, 'val' the exception's
 associated value, and 'exc' the exception.)
@@ -1961,20 +1964,17 @@
 [tb, val, exc]	L1:	DUP				)
 [tb, val, exc, exc]		<evaluate E1>			)
 [tb, val, exc, exc, E1]	COMPARE_OP	EXC_MATCH	) only if E1
- [tb, val, exc, 1-or-0]	JUMP_IF_FALSE	L2		)
- [tb, val, exc, 1]		POP				)
+ [tb, val, exc, 1-or-0]	POP_JUMP_IF_FALSE	L2	)
 [tb, val, exc]		POP
 [tb, val]			<assign to V1>	(or POP if no V1)
 [tb]				POP
 []				<code for S1>
 				JUMP_FORWARD	L0
 
- [tb, val, exc, 0]	L2:	POP
- [tb, val, exc]		DUP
+ [tb, val, exc]	L2:	DUP
 .............................etc.......................
 
- [tb, val, exc, 0]	Ln+1:	POP
- [tb, val, exc]		END_FINALLY	# re-raise exception
+ [tb, val, exc]	Ln+1:	END_FINALLY	# re-raise exception
 
 []			L0:	<next statement>
 
@@ -2016,8 +2016,7 @@
 			ADDOP(c, DUP_TOP);
 			VISIT(c, expr, handler->v.ExceptHandler.type);
 			ADDOP_I(c, COMPARE_OP, PyCmp_EXC_MATCH);
-			ADDOP_JREL(c, JUMP_IF_FALSE, except);
-			ADDOP(c, POP_TOP);
+			ADDOP_JABS(c, POP_JUMP_IF_FALSE, except);
 		}
 		ADDOP(c, POP_TOP);
 		if (handler->v.ExceptHandler.name) {
@@ -2088,8 +2087,6 @@
 		}
 		ADDOP_JREL(c, JUMP_FORWARD, end);
 		compiler_use_next_block(c, except);
-		if (handler->v.ExceptHandler.type)
-			ADDOP(c, POP_TOP);
 	}
 	ADDOP(c, END_FINALLY);
 	compiler_use_next_block(c, orelse);
@@ -2268,8 +2265,7 @@
 	end = compiler_new_block(c);
 	if (end == NULL)
 		return 0;
-	ADDOP_JREL(c, JUMP_IF_TRUE, end);
-	ADDOP(c, POP_TOP);
+	ADDOP_JABS(c, POP_JUMP_IF_TRUE, end);
 	ADDOP_O(c, LOAD_GLOBAL, assertion_error, names);
 	if (s->v.Assert.msg) {
 		VISIT(c, expr, s->v.Assert.msg);
@@ -2277,7 +2273,6 @@
 	}
 	ADDOP_I(c, RAISE_VARARGS, 1);
 	compiler_use_next_block(c, end);
-	ADDOP(c, POP_TOP);
 	return 1;
 }
 
@@ -2629,9 +2624,9 @@
 
 	assert(e->kind == BoolOp_kind);
 	if (e->v.BoolOp.op == And)
-		jumpi = JUMP_IF_FALSE;
+		jumpi = JUMP_IF_FALSE_OR_POP;
 	else
-		jumpi = JUMP_IF_TRUE;
+		jumpi = JUMP_IF_TRUE_OR_POP;
 	end = compiler_new_block(c);
 	if (end == NULL)
 		return 0;
@@ -2640,8 +2635,7 @@
 	assert(n >= 0);
 	for (i = 0; i < n; ++i) {
 		VISIT(c, expr, (expr_ty)asdl_seq_GET(s, i));
-		ADDOP_JREL(c, jumpi, end);
-		ADDOP(c, POP_TOP)
+		ADDOP_JABS(c, jumpi, end);
 	}
 	VISIT(c, expr, (expr_ty)asdl_seq_GET(s, n));
 	compiler_use_next_block(c, end);
@@ -2737,9 +2731,8 @@
 		ADDOP_I(c, COMPARE_OP,
 			cmpop((cmpop_ty)(asdl_seq_GET(
 						 e->v.Compare.ops, i - 1))));
-		ADDOP_JREL(c, JUMP_IF_FALSE, cleanup);
+		ADDOP_JABS(c, JUMP_IF_FALSE_OR_POP, cleanup);
 		NEXT_BLOCK(c);
-		ADDOP(c, POP_TOP);
 		if (i < (n - 1))
 		 VISIT(c, expr, 
 			 (expr_ty)asdl_seq_GET(e->v.Compare.comparators, i));
@@ -2872,10 +2865,9 @@
 	for (i = 0; i < n; i++) {
 		expr_ty e = (expr_ty)asdl_seq_GET(gen->ifs, i);
 		VISIT(c, expr, e);
-		ADDOP_JREL(c, JUMP_IF_FALSE, if_cleanup);
+		ADDOP_JABS(c, POP_JUMP_IF_FALSE, if_cleanup);
 		NEXT_BLOCK(c);
-		ADDOP(c, POP_TOP);
-	} 
+	}
 
 	if (++gen_index < asdl_seq_LEN(generators))
 		if (!compiler_comprehension_generator(c, 
@@ -2913,13 +2905,7 @@
 
 		compiler_use_next_block(c, skip);
 	}
-	for (i = 0; i < n; i++) {
-		ADDOP_I(c, JUMP_FORWARD, 1);
-		if (i == 0)
-			compiler_use_next_block(c, if_cleanup);
-		
-		ADDOP(c, POP_TOP);
-	} 
+	compiler_use_next_block(c, if_cleanup);
 	ADDOP_JABS(c, JUMP_ABSOLUTE, start);
 	compiler_use_next_block(c, anchor);
 
Modified: python/branches/py3k/Python/import.c
==============================================================================
--- python/branches/py3k/Python/import.c	(original)
+++ python/branches/py3k/Python/import.c	Wed Feb 25 03:25:04 2009
@@ -87,8 +87,10 @@
 Python 3.0a5: 3130 (lexical exception stacking, including POP_EXCEPT)
 Python 3.1a0: 3140 (optimize list, set and dict comprehensions:
 			 change LIST_APPEND and SET_ADD, add MAP_ADD)
+ Python 3.1a0: 3150 (optimize conditional branches:
+			 introduce POP_JUMP_IF_FALSE and POP_JUMP_IF_TRUE)
 */
-#define MAGIC (3140 | ((long)'\r'<<16) | ((long)'\n'<<24))
+#define MAGIC (3150 | ((long)'\r'<<16) | ((long)'\n'<<24))
 
 /* Magic word as global; note that _PyImport_Init() can change the
 value of this global to accommodate for alterations of how the
Modified: python/branches/py3k/Python/opcode_targets.h
==============================================================================
--- python/branches/py3k/Python/opcode_targets.h	(original)
+++ python/branches/py3k/Python/opcode_targets.h	Wed Feb 25 03:25:04 2009
@@ -110,11 +110,11 @@
 	&&TARGET_IMPORT_NAME,
 	&&TARGET_IMPORT_FROM,
 	&&TARGET_JUMP_FORWARD,
-	&&TARGET_JUMP_IF_FALSE,
-	&&TARGET_JUMP_IF_TRUE,
+	&&TARGET_JUMP_IF_FALSE_OR_POP,
+	&&TARGET_JUMP_IF_TRUE_OR_POP,
 	&&TARGET_JUMP_ABSOLUTE,
-	&&_unknown_opcode,
-	&&_unknown_opcode,
+	&&TARGET_POP_JUMP_IF_FALSE,
+	&&TARGET_POP_JUMP_IF_TRUE,
 	&&TARGET_LOAD_GLOBAL,
 	&&_unknown_opcode,
 	&&_unknown_opcode,
Modified: python/branches/py3k/Python/peephole.c
==============================================================================
--- python/branches/py3k/Python/peephole.c	(original)
+++ python/branches/py3k/Python/peephole.c	Wed Feb 25 03:25:04 2009
@@ -13,7 +13,12 @@
 
 #define GETARG(arr, i) ((int)((arr[i+2]<<8) + arr[i+1]))
 #define UNCONDITIONAL_JUMP(op)	(op==JUMP_ABSOLUTE || op==JUMP_FORWARD)
-#define ABSOLUTE_JUMP(op) (op==JUMP_ABSOLUTE || op==CONTINUE_LOOP)
+#define CONDITIONAL_JUMP(op) (op==POP_JUMP_IF_FALSE || op==POP_JUMP_IF_TRUE \
+	|| op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP)
+#define ABSOLUTE_JUMP(op) (op==JUMP_ABSOLUTE || op==CONTINUE_LOOP \
+	|| op==POP_JUMP_IF_FALSE || op==POP_JUMP_IF_TRUE \
+	|| op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP)
+#define JUMPS_ON_TRUE(op) (op==POP_JUMP_IF_TRUE || op==JUMP_IF_TRUE_OR_POP)
 #define GETJUMPTGT(arr, i) (GETARG(arr,i) + (ABSOLUTE_JUMP(arr[i]) ? 0 : i+3))
 #define SETARG(arr, i, val) arr[i+2] = val>>8; arr[i+1] = val & 255
 #define CODESIZE(op) (HAS_ARG(op) ? 3 : 1)
@@ -237,8 +242,10 @@
 		switch (opcode) {
 			case FOR_ITER:
 			case JUMP_FORWARD:
-			case JUMP_IF_FALSE:
-			case JUMP_IF_TRUE:
+			case JUMP_IF_FALSE_OR_POP:
+			case JUMP_IF_TRUE_OR_POP:
+			case POP_JUMP_IF_FALSE:
+			case POP_JUMP_IF_TRUE:
 			case JUMP_ABSOLUTE:
 			case CONTINUE_LOOP:
 			case SETUP_LOOP:
@@ -363,29 +370,24 @@
 	assert(PyList_Check(consts));
 
 	for (i=0 ; i<codelen ; i += CODESIZE(codestr[i])) {
+	 reoptimize_current:
 		opcode = codestr[i];
 
 		lastlc = cumlc;
 		cumlc = 0;
 
 		switch (opcode) {
-
-			/* Replace UNARY_NOT JUMP_IF_FALSE POP_TOP with 
-			 with	 JUMP_IF_TRUE POP_TOP */
+			/* Replace UNARY_NOT POP_JUMP_IF_FALSE 
+			 with POP_JUMP_IF_TRUE */
 			case UNARY_NOT:
-				if (codestr[i+1] != JUMP_IF_FALSE ||
-				 codestr[i+4] != POP_TOP ||
-				 !ISBASICBLOCK(blocks,i,5))
-					continue;
-				tgt = GETJUMPTGT(codestr, (i+1));
-				if (codestr[tgt] != POP_TOP)
+				if (codestr[i+1] != POP_JUMP_IF_FALSE
+				 || !ISBASICBLOCK(blocks,i,4))
 					continue;
-				j = GETARG(codestr, i+1) + 1;
-				codestr[i] = JUMP_IF_TRUE;
+				j = GETARG(codestr, i+1);
+				codestr[i] = POP_JUMP_IF_TRUE;
 				SETARG(codestr, i, j);
-				codestr[i+3] = POP_TOP;
-				codestr[i+4] = NOP;
-				break;
+				codestr[i+3] = NOP;
+				goto reoptimize_current;
 
 				/* not a is b --> a is not b
 				 not a in b --> a not in b
@@ -417,29 +419,19 @@
 				break;
 
 				/* Skip over LOAD_CONST trueconst
- JUMP_IF_FALSE xx POP_TOP */
+				 POP_JUMP_IF_FALSE xx. This improves
+				 "while 1" performance. */
 			case LOAD_CONST:
 				cumlc = lastlc + 1;
 				j = GETARG(codestr, i);
-				if (codestr[i+3] != JUMP_IF_FALSE ||
-				 codestr[i+6] != POP_TOP ||
+				if (codestr[i+3] != POP_JUMP_IF_FALSE ||
 				 !ISBASICBLOCK(blocks,i,7) ||
 				 !PyObject_IsTrue(PyList_GET_ITEM(consts, j)))
 					continue;
-				memset(codestr+i, NOP, 7);
+				memset(codestr+i, NOP, 6);
 				cumlc = 0;
 				break;
 
-				/* Replace POP_TOP JUMP_FORWARD 1 POP_TOP 
-				 with NOP NOP NOP NOP POP_TOP. */
-			case POP_TOP:
-				if (UNCONDITIONAL_JUMP(codestr[i+1]) &&
-					GETJUMPTGT(codestr, i+1) == i+5 &&
-					codestr[i+4] == POP_TOP &&
-					ISBASICBLOCK(blocks,i,4))
-						memset(codestr+i, NOP, 4);
-				break;
-
 				/* Try to fold tuples of constants (includes a case for lists
 				 which are only used for "in" and "not in" tests).
 				 Skip over BUILD_SEQN 1 UNPACK_SEQN 1.
@@ -524,27 +516,47 @@
 				 "if a or b:"
 				 "a and b or c"
 				 "(a and b) and c"
-				 x:JUMP_IF_FALSE y y:JUMP_IF_FALSE z --> x:JUMP_IF_FALSE z
-				 x:JUMP_IF_FALSE y y:JUMP_IF_TRUE z	 --> x:JUMP_IF_FALSE y+3
+				 x:POP_OR_JUMP y y:POP_OR_JUMP z --> x:POP_OR_JUMP z
+				 x:POP_OR_JUMP y y:JUMP_OR_POP z --> x:POP_JUMP_IF_FALSE y+3
 				 where y+3 is the instruction following the second test.
 				*/
-			case JUMP_IF_FALSE:
-			case JUMP_IF_TRUE:
+			case JUMP_IF_FALSE_OR_POP:
+			case JUMP_IF_TRUE_OR_POP:
 				tgt = GETJUMPTGT(codestr, i);
 				j = codestr[tgt];
-				if (j == JUMP_IF_FALSE	|| j == JUMP_IF_TRUE) {
-					if (j == opcode) {
-						tgttgt = GETJUMPTGT(codestr, tgt) - i - 3;
+				if (CONDITIONAL_JUMP(j)) {
+					/* NOTE: all possible jumps here are
+					 absolute! */
+					if (JUMPS_ON_TRUE(j) == JUMPS_ON_TRUE(opcode)) {
+						/* The second jump will be
+						 taken iff the first is. */
+						tgttgt = GETJUMPTGT(codestr, tgt);
+						/* The current opcode inherits
+						 its target's stack behaviour */
+						codestr[i] = j;
 						SETARG(codestr, i, tgttgt);
+						goto reoptimize_current;
 					} else {
-						tgt -= i;
-						SETARG(codestr, i, tgt);
+						/* The second jump is not taken
+						 if the first is (so jump past
+						 it), and all conditional
+						 jumps pop their argument when
+						 they're not taken (so change
+						 the first jump to pop its
+						 argument when it's taken). */
+						if (JUMPS_ON_TRUE(opcode))
+							codestr[i] = POP_JUMP_IF_TRUE;
+						else
+							codestr[i] = POP_JUMP_IF_FALSE;
+						SETARG(codestr, i, (tgt + 3));
+						goto reoptimize_current;
 					}
-					break;
 				}
-				/* Intentional fallthrough */ 
+				/* Intentional fallthrough */
 
 				/* Replace jumps to unconditional jumps */
+			case POP_JUMP_IF_FALSE:
+			case POP_JUMP_IF_TRUE:
 			case FOR_ITER:
 			case JUMP_FORWARD:
 			case JUMP_ABSOLUTE:
@@ -621,14 +633,16 @@
 
 			case JUMP_ABSOLUTE:
 			case CONTINUE_LOOP:
+			case POP_JUMP_IF_FALSE:
+			case POP_JUMP_IF_TRUE:
+			case JUMP_IF_FALSE_OR_POP:
+			case JUMP_IF_TRUE_OR_POP:
 				j = addrmap[GETARG(codestr, i)];
 				SETARG(codestr, i, j);
 				break;
 
 			case FOR_ITER:
 			case JUMP_FORWARD:
-			case JUMP_IF_FALSE:
-			case JUMP_IF_TRUE:
 			case SETUP_LOOP:
 			case SETUP_EXCEPT:
 			case SETUP_FINALLY:


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /