Skip to content

Commit 53f91ad

Browse files
committed
peephole: fix UNPACK_SEQUENCE optim
1 parent 4f03c21 commit 53f91ad

3 files changed

Lines changed: 74 additions & 19 deletions

File tree

bytecode/peephole_opt.py

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -193,27 +193,45 @@ def replace_container_of_consts(self, instr, container_type):
193193
self.replace_load_const(instr.arg, instr, value)
194194

195195
def build_tuple_unpack_seq(self, instr):
196-
nconst = instr.arg
197-
if nconst < 1:
198-
return
199-
200196
next_instr = self.get_next_instr('UNPACK_SEQUENCE')
201197
if next_instr is None or next_instr.arg != instr.arg:
202198
return
203199

204-
start = self.index - 1
200+
if instr.arg < 1:
201+
return
205202

206-
# Rewrite LOAD_CONST instructions in the reverse order
207-
load_consts = self.block[start - nconst:start]
208-
self.block[start - nconst:start] = reversed(load_consts)
203+
if (self.const_stack
204+
and instr.arg <= len(self.const_stack)):
205+
nconst = instr.arg
206+
start = self.index - 1
209207

210-
# Remove BUILD_TUPLE+UNPACK_SEQUENCE
211-
self.block[start:start + 2] = ()
212-
self.index -= 2
213-
self.const_stack.clear()
208+
# Rewrite LOAD_CONST instructions in the reverse order
209+
load_consts = self.block[start - nconst:start]
210+
self.block[start - nconst:start] = reversed(load_consts)
214211

215-
# FIXME: why not rewriting LOAD_CONST in the reverse order to support
216-
# any number of aguments, rather than using ROT_TWO/ROT_THREE tricks?
212+
# Remove BUILD_TUPLE+UNPACK_SEQUENCE
213+
self.block[start:start + 2] = ()
214+
self.index -= 2
215+
self.const_stack.clear()
216+
return
217+
218+
if instr.arg == 1:
219+
# Replace BUILD_TUPLE 1 + UNPACK_SEQUENCE 1 with NOP
220+
del self.block[self.index-1:self.index+1]
221+
elif instr.arg == 2:
222+
# Replace BUILD_TUPLE 2 + UNPACK_SEQUENCE 2 with ROT_TWO
223+
rot2 = Instr('ROT_TWO', lineno=instr.lineno)
224+
self.block[self.index - 1:self.index+1] = (rot2,)
225+
self.index -= 1
226+
self.const_stack.clear()
227+
elif instr.arg == 3:
228+
# Replace BUILD_TUPLE 3 + UNPACK_SEQUENCE 3
229+
# with ROT_THREE + ROT_TWO
230+
rot3 = Instr('ROT_THREE', lineno=instr.lineno)
231+
rot2 = Instr('ROT_TWO', lineno=instr.lineno)
232+
self.block[self.index-1:self.index+1] = (rot3, rot2)
233+
self.index -= 1
234+
self.const_stack.clear()
217235

218236
def build_tuple(self, instr, container_type):
219237
if instr.arg > len(self.const_stack):

bytecode/tests/test_peephole_opt.py

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,9 @@ def test_build_list_unpack_seq(self):
243243
Instr('STORE_NAME', 'x'),
244244
Instr('STORE_NAME', 'y')])
245245
self.check(code,
246-
Instr('LOAD_NAME', 'b'),
247246
Instr('LOAD_NAME', 'a'),
247+
Instr('LOAD_NAME', 'b'),
248+
Instr('ROT_TWO'),
248249
Instr('STORE_NAME', 'x'),
249250
Instr('STORE_NAME', 'y'))
250251

@@ -258,13 +259,47 @@ def test_build_list_unpack_seq(self):
258259
Instr('STORE_NAME', 'y'),
259260
Instr('STORE_NAME', 'z')])
260261
self.check(code,
261-
Instr('LOAD_NAME', 'c'),
262-
Instr('LOAD_NAME', 'b'),
263262
Instr('LOAD_NAME', 'a'),
263+
Instr('LOAD_NAME', 'b'),
264+
Instr('LOAD_NAME', 'c'),
265+
Instr('ROT_THREE'),
266+
Instr('ROT_TWO'),
264267
Instr('STORE_NAME', 'x'),
265268
Instr('STORE_NAME', 'y'),
266269
Instr('STORE_NAME', 'z'))
267270

271+
def test_build_tuple_unpack_seq_const(self):
272+
# x, y = (3, 4)
273+
code = Bytecode([Instr('LOAD_CONST', 3),
274+
Instr('LOAD_CONST', 4),
275+
Instr('BUILD_TUPLE', 2),
276+
Instr('UNPACK_SEQUENCE', 2),
277+
Instr('STORE_NAME', 'x'),
278+
Instr('STORE_NAME', 'y')])
279+
self.check(code,
280+
Instr('LOAD_CONST', (3, 4)),
281+
Instr('UNPACK_SEQUENCE', 2),
282+
Instr('STORE_NAME', 'x'),
283+
Instr('STORE_NAME', 'y'))
284+
285+
def test_build_list_unpack_seq_const(self):
286+
# x, y, z = [3, 4, 5]
287+
code = Bytecode([Instr('LOAD_CONST', 3),
288+
Instr('LOAD_CONST', 4),
289+
Instr('LOAD_CONST', 5),
290+
Instr('BUILD_LIST', 3),
291+
Instr('UNPACK_SEQUENCE', 3),
292+
Instr('STORE_NAME', 'x'),
293+
Instr('STORE_NAME', 'y'),
294+
Instr('STORE_NAME', 'z')])
295+
self.check(code,
296+
Instr('LOAD_CONST', 5),
297+
Instr('LOAD_CONST', 4),
298+
Instr('LOAD_CONST', 3),
299+
Instr('STORE_NAME', 'x'),
300+
Instr('STORE_NAME', 'y'),
301+
Instr('STORE_NAME', 'z'))
302+
268303
def test_build_set(self):
269304
# test = x in {1, 2, 3}
270305
code = Bytecode([Instr('LOAD_NAME', 'x'),

doc/peephole.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,10 @@ Optimizations implemented in the peephole optimizer:
8989
* a << b, a >> b, a & b, a | b, a ^ b
9090

9191
- BUILD_TUPLE: convert to a constant
92-
- BUILD_TUPLE <n> + UNPACK_SEQUENCE <n> and BUILD_LIST <n>
93-
+ UNPACK_SEQUENCE <n>: rewrite LOAD_CONST instructions in the reverse order
92+
- Replace BUILD_TUPLE <n> + UNPACK_SEQUENCE <n> and BUILD_LIST <n>
93+
+ UNPACK_SEQUENCE <n> with ROT_TWO (2 arguments) or ROT_THREE+ROT_TWO (3
94+
arguments). For BUILD_LIST, if inputs are LOAD_CONST, rewrite LOAD_CONST in
95+
the reverse order.
9496
- BUILD_LIST + COMPARE_OP(in/not in): convert list to tuple
9597
- BUILD_SET + COMPARE_OP(in/not in): convert set to frozenset
9698
- COMPARE_OP:

0 commit comments

Comments
 (0)