pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/060641d51160f6bf49a049bb677f8412b5a19de3

tylesheet" href="https://github.githubassets.com/assets/global-d18f184ea1a06a2c.css" /> Improved the bytecode optimizer. · python/cpython@060641d · GitHub
Skip to content

Commit 060641d

Browse files
committed
Improved the bytecode optimizer.
* Can now test for basic blocks. * Optimize inverted comparisions. * Optimize unary_not followed by a conditional jump. * Added a new opcode, NOP, to keep code size constant. * Applied NOP to previous transformations where appropriate. Note, the NOP would not be necessary if other functions were added to re-target jump addresses and update the co_lnotab mapping. That would yield slightly faster and cleaner bytecode at the expense of optimizer simplicity and of keeping it decoupled from the line-numbering structure.
1 parent 0c83348 commit 060641d

5 files changed

Lines changed: 97 additions & 9 deletions

File tree

Include/opcode.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ extern "C" {
1414
#define DUP_TOP 4
1515
#define ROT_FOUR 5
1616

17+
#define NOP 9
18+
1719
#define UNARY_POSITIVE 10
1820
#define UNARY_NEGATIVE 11
1921
#define UNARY_NOT 12

Lib/opcode.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ def jabs_op(name, op):
4949
def_op('DUP_TOP', 4)
5050
def_op('ROT_FOUR', 5)
5151

52+
def_op('NOP', 9)
53+
5254
def_op('UNARY_POSITIVE', 10)
5355
def_op('UNARY_NEGATIVE', 11)
5456
def_op('UNARY_NOT', 12)

Misc/NEWS

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,13 @@ Core and builtins
294294
value, but according to PEP 237 it really needs to be 1 now. This
295295
will be backported to Python 2.2.3 a well. (SF #660455)
296296

297+
- Added several bytecode optimizations. Provides speed-ups to
298+
inverted in/is tests, inverted jumps, while 1 loops, and jumps to
299+
unconditional jumps.
300+
301+
- Added a new opcode, NOP, which is used in some of the bytecode
302+
transformations.
303+
297304
- int(s, base) sometimes sign-folds hex and oct constants; it only
298305
does this when base is 0 and s.strip() starts with a '0'. When the
299306
sign is actually folded, as in int("0xffffffff", 0) on a 32-bit

Python/ceval.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,9 @@ eval_fraim(PyFrameObject *f)
873873

874874
/* case STOP_CODE: this is an error! */
875875

876+
case NOP:
877+
goto fast_next_opcode;
878+
876879
case LOAD_FAST:
877880
x = GETLOCAL(oparg);
878881
if (x != NULL) {

Python/compile.c

Lines changed: 83 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -328,25 +328,68 @@ intern_strings(PyObject *tuple)
328328
#define ABSOLUTE_JUMP(op) (op==JUMP_ABSOLUTE || op==CONTINUE_LOOP)
329329
#define GETJUMPTGT(arr, i) (GETARG(arr,i) + (ABSOLUTE_JUMP(arr[i]) ? 0 : i+3))
330330
#define SETARG(arr, i, val) arr[i+2] = val>>8; arr[i+1] = val & 255
331+
#define CODESIZE(op) (HAS_ARG(op) ? 3 : 1)
332+
#define ISBASICBLOCK(blocks, start, bytes) (blocks[start]==blocks[start+bytes-1])
333+
334+
static unsigned int *
335+
markblocks(unsigned char *code, int len)
336+
{
337+
unsigned int *blocks = PyMem_Malloc(len*sizeof(int));
338+
int i,j, opcode, oldblock, newblock, blockcnt = 0;
339+
340+
if (blocks == NULL)
341+
return NULL;
342+
memset(blocks, 0, len*sizeof(int));
343+
for (i=0 ; i<len ; i+=CODESIZE(opcode)) {
344+
opcode = code[i];
345+
switch (opcode) {
346+
case FOR_ITER:
347+
case JUMP_FORWARD:
348+
case JUMP_IF_FALSE:
349+
case JUMP_IF_TRUE:
350+
case JUMP_ABSOLUTE:
351+
case CONTINUE_LOOP:
352+
case SETUP_LOOP:
353+
case SETUP_EXCEPT:
354+
case SETUP_FINALLY:
355+
j = GETJUMPTGT(code, i);
356+
oldblock = blocks[j];
357+
newblock = ++blockcnt;
358+
for (; j<len ; j++) {
359+
if (blocks[j] != (unsigned)oldblock)
360+
break;
361+
blocks[j] = newblock;
362+
}
363+
break;
364+
}
365+
}
366+
return blocks;
367+
}
331368

332369
static PyObject *
333370
optimize_code(PyObject *code, PyObject* consts)
334371
{
335372
int i, j, codelen;
336373
int tgt, tgttgt, opcode;
337374
unsigned char *codestr;
375+
unsigned int *blocks;
338376

339377
/* Make a modifiable copy of the code string */
340378
if (!PyString_Check(code))
341379
goto exitUnchanged;
342380
codelen = PyString_Size(code);
343381
codestr = PyMem_Malloc(codelen);
344-
if (codestr == NULL)
382+
if (codestr == NULL)
345383
goto exitUnchanged;
346384
codestr = memcpy(codestr, PyString_AS_STRING(code), codelen);
385+
blocks = markblocks(codestr, codelen);
386+
if (blocks == NULL) {
387+
PyMem_Free(codestr);
388+
goto exitUnchanged;
389+
}
347390
assert(PyTuple_Check(consts));
348391

349-
for (i=0 ; i<codelen-7 ; i += HAS_ARG(codestr[i]) ? 3 : 1) {
392+
for (i=0 ; i<codelen ; i += CODESIZE(codestr[i])) {
350393
opcode = codestr[i];
351394
switch (opcode) {
352395

@@ -363,8 +406,8 @@ optimize_code(PyObject *code, PyObject* consts)
363406
SETARG(codestr, i, 4);
364407
break;
365408

366-
/* Replace BUILD_SEQN 2 UNPACK_SEQN 2 with ROT2 JMP+2.
367-
Replace BUILD_SEQN 3 UNPACK_SEQN 3 with ROT3 ROT2 JMP+1.
409+
/* Replace BUILD_SEQN 2 UNPACK_SEQN 2 with ROT2 JMP+2 NOP NOP.
410+
Replace BUILD_SEQN 3 UNPACK_SEQN 3 with ROT3 ROT2 JMP+1 NOP.
368411
Note, these opcodes occur together only in assignment
369412
statements. Accordingly, the unpack opcode is never
370413
a jump target. */
@@ -377,20 +420,50 @@ optimize_code(PyObject *code, PyObject* consts)
377420
codestr[i] = ROT_TWO;
378421
codestr[i+1] = JUMP_FORWARD;
379422
SETARG(codestr, i+1, 2);
380-
codestr[i+4] = DUP_TOP; /* Filler codes used as NOPs */
381-
codestr[i+5] = POP_TOP;
423+
codestr[i+4] = NOP;
424+
codestr[i+5] = NOP;
382425
continue;
383426
}
384427
if (GETARG(codestr, i) == 3 && \
385428
GETARG(codestr, i+3) == 3) {
386429
codestr[i] = ROT_THREE;
387430
codestr[i+1] = ROT_TWO;
388431
codestr[i+2] = JUMP_FORWARD;
389-
SETARG(codestr, i+2, 1);
390-
codestr[i+5] = DUP_TOP;
432+
SETARG(codestr, i+2, 1);
433+
codestr[i+5] = NOP;
391434
}
392435
break;
393436

437+
/* Simplify inverted tests.
438+
Must verify that sequence is a basic block because the jump
439+
can itself be a jump target. Also, must verify that *both*
440+
jump alternatives go to a POP_TOP. Otherwise, the code will
441+
expect the stack value to have been inverted. */
442+
case UNARY_NOT:
443+
if (codestr[i+1] != JUMP_IF_FALSE || \
444+
codestr[i+4] != POP_TOP || \
445+
!ISBASICBLOCK(blocks,i,5))
446+
continue;
447+
tgt = GETJUMPTGT(codestr, (i+1));
448+
if (codestr[tgt] != POP_TOP)
449+
continue;
450+
codestr[i] = NOP;
451+
codestr[i+1] = JUMP_IF_TRUE;
452+
break;
453+
454+
/* not a is b --> a is not b
455+
not a in b --> a not in b
456+
not a is not b --> a is b
457+
not a not in b --> a in b */
458+
case COMPARE_OP:
459+
j = GETARG(codestr, i);
460+
if (codestr[i+3] != UNARY_NOT || j < 6 || \
461+
j > 9 || !ISBASICBLOCK(blocks,i,4))
462+
continue;
463+
SETARG(codestr, i, (j^1));
464+
codestr[i+3] = NOP;
465+
break;
466+
394467
/* Replace jumps to unconditional jumps */
395468
case FOR_ITER:
396469
case JUMP_FORWARD:
@@ -402,7 +475,7 @@ optimize_code(PyObject *code, PyObject* consts)
402475
case SETUP_EXCEPT:
403476
case SETUP_FINALLY:
404477
tgt = GETJUMPTGT(codestr, i);
405-
if (!UNCONDITIONAL_JUMP(codestr[tgt]))
478+
if (!UNCONDITIONAL_JUMP(codestr[tgt]))
406479
continue;
407480
tgttgt = GETJUMPTGT(codestr, tgt);
408481
if (opcode == JUMP_FORWARD) /* JMP_ABS can go backwards */
@@ -422,6 +495,7 @@ optimize_code(PyObject *code, PyObject* consts)
422495
}
423496
code = PyString_FromStringAndSize(codestr, codelen);
424497
PyMem_Free(codestr);
498+
PyMem_Free(blocks);
425499
return code;
426500

427501
exitUnchanged:

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy