Simplify EXTENDED_ARG on 3.x

We largely remove them and fold them itno the next op.
MAKE_FUNCTION though before 3.6 is an exception as that indicates an
annotated function
This commit is contained in:
rocky
2017-05-19 22:06:18 -04:00
parent 49fd430505
commit 2ab7aa2f48
3 changed files with 41 additions and 101 deletions

View File

@@ -627,12 +627,6 @@ class Python3Parser(PythonParser):
self.custom_build_class_rule(opname, i, token, tokens, customize)
elif opname_base in ('BUILD_LIST', 'BUILD_TUPLE', 'BUILD_SET'):
v = token.attr
if self.version >= 3.6:
extended_arg = 1 << 8
else:
extended_arg = 1 << 16
if v >= extended_arg:
opname = "EXTENDED_ARG %s" % opname
rule = ('build_list ::= ' + 'expr1024 ' * int(v//1024) +
'expr32 ' * int((v//32) % 32) +
'expr ' * (v % 32) + opname)
@@ -764,7 +758,18 @@ class Python3Parser(PythonParser):
('pos_arg ' * args_pos, opname))
self.add_unique_rule(rule, opname, token.attr, customize)
if opname.startswith('MAKE_FUNCTION_A'):
if self.version >= 3.6:
rule = ('mkfunc_annotate ::= %s%sannotate_tuple LOAD_CONST LOAD_CONST %s' %
(('pos_arg ' * (args_pos)),
('call_function ' * (annotate_args-1)), opname))
self.add_unique_rule(rule, opname, token.attr, customize)
rule = ('mkfunc_annotate ::= %s%sannotate_tuple LOAD_CONST LOAD_CONST %s' %
(('pos_arg ' * (args_pos)),
('annotate_arg ' * (annotate_args-1)), opname))
if self.version >= 3.3:
# Normally we remove EXTENDED_ARG from the opcodes, but in the case of
# annotated functions can use the EXTENDED_ARG tuple to signal we have an annotated function.
# Yes this is a little hacky
rule = ('mkfunc_annotate ::= %s%sannotate_tuple LOAD_CONST LOAD_CONST EXTENDED_ARG %s' %
(('pos_arg ' * (args_pos)),
('call_function ' * (annotate_args-1)), opname))
@@ -773,6 +778,7 @@ class Python3Parser(PythonParser):
(('pos_arg ' * (args_pos)),
('annotate_arg ' * (annotate_args-1)), opname))
else:
# See above comment about use of EXTENDED_ARG
rule = ('mkfunc_annotate ::= %s%sannotate_tuple LOAD_CONST EXTENDED_ARG %s' %
(('pos_arg ' * (args_pos)),
('annotate_arg ' * (annotate_args-1)), opname))

View File

@@ -14,63 +14,12 @@ class Python36Parser(Python35Parser):
super(Python36Parser, self).__init__(debug_parser)
self.customized = {}
def p_loop_stmt3(self, args):
"""
# The range of values in arguments in 3.6 has been reduced from 2**16 to 2**8.
# As a result, EXTENDED_ARG needs to be used where it didn't before.
# Is this relevant to < 3.6 as well?
setup_loop ::= SETUP_LOOP
setup_loop ::= EXTENDED_ARG SETUP_LOOP
forstmt ::= setup_loop expr _for designator for_block POP_BLOCK
opt_come_from_loop
forelsestmt ::= setup_loop expr _for designator for_block POP_BLOCK else_suite
COME_FROM_LOOP
forelselaststmt ::= setup_loop expr _for designator for_block POP_BLOCK else_suitec
COME_FROM_LOOP
forelselaststmtl ::= setup_loop expr _for designator for_block POP_BLOCK else_suitel
COME_FROM_LOOP
whilestmt ::= setup_loop testexpr l_stmts_opt COME_FROM jump_back POP_BLOCK
COME_FROM_LOOP
whilestmt ::= setup_loop testexpr l_stmts_opt jump_back POP_BLOCK
COME_FROM_LOOP
whileTruestmt ::= setup_loop l_stmts_opt JUMP_BACK POP_BLOCK COME_FROM_LOOP
"""
def p_36misc(self, args):
"""
# The range of values in arguments in 3.6 has been reduced from 2**16 to 2**8.
# As a result, EXTENDED_ARG needs to be used where it didn't before.
# Is this relevant to < 3.6 as well?
jmp_false ::= EXTENDED_ARG POP_JUMP_IF_FALSE
jmp_true ::= EXTENDED_ARG POP_JUMP_IF_TRUE
_jump ::= EXTENDED_ARG JUMP_BACK
jump_back ::= EXTENDED_ARG JUMP_BACK
continue_stmt ::= EXTENDED_ARG CONTINUE
continue_stmt ::= EXTENDED_ARG CONTINUE_LOOP
for_block ::= l_stmts_opt opt_come_from_loop jump_back
cmp_list1 ::= expr DUP_TOP ROT_THREE COMPARE_OP
EXTENDED_ARG JUMP_IF_FALSE_OR_POP cmp_list2 COME_FROM
# 3.6 redoes how return_closure works
return_closure ::= LOAD_CLOSURE DUP_TOP STORE_NAME RETURN_VALUE RETURN_LAST
expr ::= LOAD_NAME EXTENDED_ARG
expr ::= LOAD_CONST EXTENDED_ARG
fstring_multi ::= fstring_expr_or_strs BUILD_STRING
fstring_expr_or_strs ::= fstring_expr_or_str+
@@ -86,29 +35,6 @@ class Python36Parser(Python35Parser):
def add_custom_rules(self, tokens, customize):
super(Python36Parser, self).add_custom_rules(tokens, customize)
self.remove_rule("""
forstmt ::= SETUP_LOOP expr _for designator for_block POP_BLOCK
opt_come_from_loop
forelsestmt ::= SETUP_LOOP expr _for designator for_block POP_BLOCK else_suite
COME_FROM_LOOP
forelselaststmt ::= SETUP_LOOP expr _for designator for_block POP_BLOCK else_suitec
COME_FROM_LOOP
forelselaststmtl ::= SETUP_LOOP expr _for designator for_block POP_BLOCK else_suitel
COME_FROM_LOOP
whilestmt ::= SETUP_LOOP testexpr l_stmts_opt COME_FROM jump_back POP_BLOCK
COME_FROM_LOOP
whilestmt ::= SETUP_LOOP testexpr l_stmts_opt jump_back POP_BLOCK
COME_FROM_LOOP
whileTruestmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK POP_BLOCK COME_FROM_LOOP
COME_FROM_LOOP
""")
for i, token in enumerate(tokens):
opname = token.type

View File

@@ -208,9 +208,12 @@ class Scanner3(Scanner):
jump_targets = self.find_jump_targets(show_asm)
last_op_was_break = False
for inst in bytecode:
extended_arg = 0
for i, inst in enumerate(bytecode):
argval = inst.argval
if isinstance(argval, int):
argval += extended_arg
if inst.offset in jump_targets:
jump_idx = 0
# We want to process COME_FROMs to the same offset to be in *descending*
@@ -247,12 +250,28 @@ class Scanner3(Scanner):
pass
pattr = inst.argrepr
pattr = inst.argrepr
opname = inst.opname
op = inst.opcode
op = inst.opcode
has_arg = op_has_argument(op, self.opc)
if has_arg:
extended_arg = 0
if op == self.opc.EXTENDED_ARG:
if self.version < 3.6:
extended_arg = argval * (1<<16)
else:
extended_arg = argval * (1<<8)
# Normally we remove EXTENDED_ARG from the
# opcodes, but in the case of annotated functions
# can use the EXTENDED_ARG tuple to signal we have
# an annotated function.
if not bs[i+1].opname.startswith("MAKE_FUNCTION"):
continue
if opname in ['LOAD_CONST']:
const = inst.argval
const = argval
if iscode(const):
if const.co_name == '<lambda>':
opname = 'LOAD_LAMBDA'
@@ -276,7 +295,7 @@ class Scanner3(Scanner):
elif opname in ('MAKE_FUNCTION', 'MAKE_CLOSURE'):
if self.version >= 3.6:
# 3.6+ doesn't have MAKE_CLOSURE, so opname == 'MAKE_FUNCTION'
flags = inst.argval
flags = argval
opname = 'MAKE_FUNCTION_%d' % (flags)
attr = []
for flag in self.MAKE_FUNCTION_FLAGS:
@@ -315,7 +334,7 @@ class Scanner3(Scanner):
)
continue
elif op in self.varargs_ops:
pos_args = inst.argval
pos_args = argval
if self.is_pypy and not pos_args and opname == 'BUILD_MAP':
opname = 'BUILD_MAP_n'
else:
@@ -327,9 +346,9 @@ class Scanner3(Scanner):
customize[opname] = 0
elif opname == 'UNPACK_EX':
# FIXME: try with scanner and parser by
# changing inst.argval
before_args = inst.argval & 0xFF
after_args = (inst.argval >> 8) & 0xff
# changing argval
before_args = argval & 0xFF
after_args = (argval >> 8) & 0xff
pattr = "%d before vararg, %d after" % (before_args, after_args)
argval = (before_args, after_args)
opname = '%s_%d+%d' % (opname, before_args, after_args)
@@ -346,7 +365,7 @@ class Scanner3(Scanner):
# comprehensions we might sometimes classify JUMP_BACK
# as CONTINUE, but that's okay since we add a grammar
# rule for that.
pattr = inst.argval
pattr = argval
target = self.get_target(inst.offset)
if target <= inst.offset:
next_opname = self.opname[self.code[inst.offset+3]]
@@ -606,27 +625,16 @@ class Scanner3(Scanner):
rel_offset = 0
if self.version >= 3.6:
target = self.code[offset+1]
arg_offset = 1
extended_arg_mult = 1 << 8
if op in self.opc.hasjrel:
rel_offset = offset + 2
else:
target = self.code[offset+1] + self.code[offset+2] * 256
arg_offset = 2
extended_arg_mult = 1 << 16
if op in self.opc.hasjrel:
rel_offset = offset + 3
pass
pass
target += rel_offset
if offset > 0:
prev_offset = self.prev_op[offset]
prev_op = self.code[prev_offset]
if prev_op == self.opc.EXTENDED_ARG:
target += (self.code[prev_offset + arg_offset]
* extended_arg_mult)
pass
return target
def detect_control_flow(self, offset, targets):