diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 657778c0f4098a..c807c0660cca63 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -1009,12 +1009,15 @@ iterations of the loop. .. versionadded:: 3.1 -.. opcode:: FOR_ITER (delta) +.. opcode:: FOR_END (delta) TOS is an :term:`iterator`. Call its :meth:`~iterator.__next__` method. If this yields a new value, push it on the stack (leaving the iterator below - it). If the iterator indicates it is exhausted, TOS is popped, and the byte - code counter is incremented by *delta*. + it), then decrement the bytecode counter by *delta*. If the iterator + indicates it is exhausted, then pop TOS and do not decrement the bytecode + counter. + + .. versionadded:: 3.11 .. opcode:: LOAD_GLOBAL (namei) diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index d803801f273c55..8d79b298c8cfad 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -774,6 +774,12 @@ CPython bytecode changes opcodes to speed up conditional jumps. +* Replaced :opcode:`FOR_ITER` (jumps if iterator exhausted) + with :opcode:`FOR_END` (jumps if iterator is not exhausted). + At the end of each ``for``-loop iteration, instead of an unconditional + jump targeted at a conditional jump, there is now + only one (conditional) jump. + Deprecated ========== diff --git a/Include/opcode.h b/Include/opcode.h index ca4a18de107160..b526fec30d8577 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -118,6 +118,7 @@ extern "C" { #define POP_JUMP_BACKWARD_IF_NONE 174 #define POP_JUMP_BACKWARD_IF_FALSE 175 #define POP_JUMP_BACKWARD_IF_TRUE 176 +#define FOR_END 177 #define BINARY_OP_ADAPTIVE 3 #define BINARY_OP_ADD_FLOAT 4 #define BINARY_OP_ADD_INT 5 @@ -140,54 +141,55 @@ extern "C" { #define COMPARE_OP_INT_JUMP 28 #define COMPARE_OP_STR_JUMP 29 #define JUMP_BACKWARD_QUICK 34 -#define LOAD_ATTR_ADAPTIVE 38 -#define LOAD_ATTR_INSTANCE_VALUE 39 -#define LOAD_ATTR_MODULE 40 -#define LOAD_ATTR_SLOT 41 -#define LOAD_ATTR_WITH_HINT 42 -#define LOAD_CONST__LOAD_FAST 43 -#define LOAD_FAST__LOAD_CONST 44 -#define LOAD_FAST__LOAD_FAST 45 -#define LOAD_GLOBAL_ADAPTIVE 46 -#define LOAD_GLOBAL_BUILTIN 47 -#define LOAD_GLOBAL_MODULE 48 -#define LOAD_METHOD_ADAPTIVE 55 -#define LOAD_METHOD_CLASS 56 -#define LOAD_METHOD_MODULE 57 -#define LOAD_METHOD_NO_DICT 58 -#define LOAD_METHOD_WITH_DICT 59 -#define LOAD_METHOD_WITH_VALUES 62 -#define PRECALL_ADAPTIVE 63 -#define PRECALL_BOUND_METHOD 64 -#define PRECALL_BUILTIN_CLASS 65 -#define PRECALL_BUILTIN_FAST_WITH_KEYWORDS 66 -#define PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 67 -#define PRECALL_NO_KW_BUILTIN_FAST 72 -#define PRECALL_NO_KW_BUILTIN_O 73 -#define PRECALL_NO_KW_ISINSTANCE 76 -#define PRECALL_NO_KW_LEN 77 -#define PRECALL_NO_KW_LIST_APPEND 78 -#define PRECALL_NO_KW_METHOD_DESCRIPTOR_FAST 79 -#define PRECALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 80 -#define PRECALL_NO_KW_METHOD_DESCRIPTOR_O 81 -#define PRECALL_NO_KW_STR_1 113 -#define PRECALL_NO_KW_TUPLE_1 121 -#define PRECALL_NO_KW_TYPE_1 127 -#define PRECALL_PYFUNC 141 -#define RESUME_QUICK 143 -#define STORE_ATTR_ADAPTIVE 150 -#define STORE_ATTR_INSTANCE_VALUE 153 -#define STORE_ATTR_SLOT 154 -#define STORE_ATTR_WITH_HINT 158 -#define STORE_FAST__LOAD_FAST 159 -#define STORE_FAST__STORE_FAST 161 -#define STORE_SUBSCR_ADAPTIVE 167 -#define STORE_SUBSCR_DICT 168 -#define STORE_SUBSCR_LIST_INT 169 -#define UNPACK_SEQUENCE_ADAPTIVE 170 -#define UNPACK_SEQUENCE_LIST 177 -#define UNPACK_SEQUENCE_TUPLE 178 -#define UNPACK_SEQUENCE_TWO_TUPLE 179 +#define FOR_END_QUICK 38 +#define LOAD_ATTR_ADAPTIVE 39 +#define LOAD_ATTR_INSTANCE_VALUE 40 +#define LOAD_ATTR_MODULE 41 +#define LOAD_ATTR_SLOT 42 +#define LOAD_ATTR_WITH_HINT 43 +#define LOAD_CONST__LOAD_FAST 44 +#define LOAD_FAST__LOAD_CONST 45 +#define LOAD_FAST__LOAD_FAST 46 +#define LOAD_GLOBAL_ADAPTIVE 47 +#define LOAD_GLOBAL_BUILTIN 48 +#define LOAD_GLOBAL_MODULE 55 +#define LOAD_METHOD_ADAPTIVE 56 +#define LOAD_METHOD_CLASS 57 +#define LOAD_METHOD_MODULE 58 +#define LOAD_METHOD_NO_DICT 59 +#define LOAD_METHOD_WITH_DICT 62 +#define LOAD_METHOD_WITH_VALUES 63 +#define PRECALL_ADAPTIVE 64 +#define PRECALL_BOUND_METHOD 65 +#define PRECALL_BUILTIN_CLASS 66 +#define PRECALL_BUILTIN_FAST_WITH_KEYWORDS 67 +#define PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 72 +#define PRECALL_NO_KW_BUILTIN_FAST 73 +#define PRECALL_NO_KW_BUILTIN_O 76 +#define PRECALL_NO_KW_ISINSTANCE 77 +#define PRECALL_NO_KW_LEN 78 +#define PRECALL_NO_KW_LIST_APPEND 79 +#define PRECALL_NO_KW_METHOD_DESCRIPTOR_FAST 80 +#define PRECALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 81 +#define PRECALL_NO_KW_METHOD_DESCRIPTOR_O 113 +#define PRECALL_NO_KW_STR_1 121 +#define PRECALL_NO_KW_TUPLE_1 127 +#define PRECALL_NO_KW_TYPE_1 141 +#define PRECALL_PYFUNC 143 +#define RESUME_QUICK 150 +#define STORE_ATTR_ADAPTIVE 153 +#define STORE_ATTR_INSTANCE_VALUE 154 +#define STORE_ATTR_SLOT 158 +#define STORE_ATTR_WITH_HINT 159 +#define STORE_FAST__LOAD_FAST 161 +#define STORE_FAST__STORE_FAST 167 +#define STORE_SUBSCR_ADAPTIVE 168 +#define STORE_SUBSCR_DICT 169 +#define STORE_SUBSCR_LIST_INT 170 +#define UNPACK_SEQUENCE_ADAPTIVE 178 +#define UNPACK_SEQUENCE_LIST 179 +#define UNPACK_SEQUENCE_TUPLE 180 +#define UNPACK_SEQUENCE_TWO_TUPLE 181 #define DO_TRACING 255 extern const uint8_t _PyOpcode_Caches[256]; @@ -201,7 +203,7 @@ static const uint32_t _PyOpcode_RelativeJump[8] = { 536870912U, 135020544U, 4163U, - 122880U, + 253952U, 0U, 0U, }; @@ -211,7 +213,7 @@ static const uint32_t _PyOpcode_Jump[8] = { 536870912U, 135118848U, 4163U, - 122880U, + 253952U, 0U, 0U, }; @@ -284,6 +286,8 @@ const uint8_t _PyOpcode_Deopt[256] = { [END_ASYNC_FOR] = END_ASYNC_FOR, [EXTENDED_ARG] = EXTENDED_ARG, [FORMAT_VALUE] = FORMAT_VALUE, + [FOR_END] = FOR_END, + [FOR_END_QUICK] = FOR_END, [FOR_ITER] = FOR_ITER, [GET_AITER] = GET_AITER, [GET_ANEXT] = GET_ANEXT, @@ -558,6 +562,7 @@ static const char *const _PyOpcode_OpName[256] = { [POP_JUMP_BACKWARD_IF_NONE] = "POP_JUMP_BACKWARD_IF_NONE", [POP_JUMP_BACKWARD_IF_FALSE] = "POP_JUMP_BACKWARD_IF_FALSE", [POP_JUMP_BACKWARD_IF_TRUE] = "POP_JUMP_BACKWARD_IF_TRUE", + [FOR_END] = "FOR_END", }; #endif diff --git a/Lib/dis.py b/Lib/dis.py index ac900d743a0369..3516c55d755813 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -30,7 +30,6 @@ LOAD_CONST = opmap['LOAD_CONST'] LOAD_GLOBAL = opmap['LOAD_GLOBAL'] BINARY_OP = opmap['BINARY_OP'] -JUMP_BACKWARD = opmap['JUMP_BACKWARD'] CACHE = opmap["CACHE"] @@ -393,7 +392,8 @@ def parse_exception_table(code): return entries def _is_backward_jump(op): - return 'JUMP_BACKWARD' in opname[op] + name = opname[op] + return 'JUMP_BACKWARD' in name or name == "FOR_END" def _get_instructions_bytes(code, varname_from_oparg=None, names=None, co_consts=None, diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index d580e5417387bb..641e76d53ebbb1 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -415,7 +415,7 @@ def _write_atomic(path, data, mode=0o666): # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3492).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3493).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c _PYCACHE = '__pycache__' diff --git a/Lib/opcode.py b/Lib/opcode.py index ee9effbe540b2f..ac6f5a776e3e86 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -201,6 +201,7 @@ def jabs_op(name, op, entries=0): jrel_op('POP_JUMP_BACKWARD_IF_NONE', 174) jrel_op('POP_JUMP_BACKWARD_IF_FALSE', 175) jrel_op('POP_JUMP_BACKWARD_IF_TRUE', 176) +jrel_op('FOR_END', 177) del def_op, name_op, jrel_op, jabs_op @@ -267,6 +268,9 @@ def jabs_op(name, op, entries=0): "JUMP_BACKWARD": [ "JUMP_BACKWARD_QUICK", ], + "FOR_END": [ + "FOR_END_QUICK", + ], "LOAD_ATTR": [ "LOAD_ATTR_ADAPTIVE", "LOAD_ATTR_INSTANCE_VALUE", diff --git a/Lib/test/test__opcode.py b/Lib/test/test__opcode.py index 7c1c0cfdb069b8..2854a960b154ea 100644 --- a/Lib/test/test__opcode.py +++ b/Lib/test/test__opcode.py @@ -37,10 +37,10 @@ def test_stack_effect_jump(self): self.assertEqual(stack_effect(JUMP_IF_TRUE_OR_POP, 0), 0) self.assertEqual(stack_effect(JUMP_IF_TRUE_OR_POP, 0, jump=True), 0) self.assertEqual(stack_effect(JUMP_IF_TRUE_OR_POP, 0, jump=False), -1) - FOR_ITER = dis.opmap['FOR_ITER'] - self.assertEqual(stack_effect(FOR_ITER, 0), 1) - self.assertEqual(stack_effect(FOR_ITER, 0, jump=True), -1) - self.assertEqual(stack_effect(FOR_ITER, 0, jump=False), 1) + FOR_END = dis.opmap['FOR_END'] + self.assertEqual(stack_effect(FOR_END, 0), 1) + self.assertEqual(stack_effect(FOR_END, 0, jump=True), 1) + self.assertEqual(stack_effect(FOR_END, 0, jump=False), -1) JUMP_FORWARD = dis.opmap['JUMP_FORWARD'] self.assertEqual(stack_effect(JUMP_FORWARD, 0), 0) self.assertEqual(stack_effect(JUMP_FORWARD, 0, jump=True), 0) diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 1703e711d56b77..b23ecdb1bfb941 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -146,12 +146,15 @@ def bug708901(): %3d PRECALL 2 CALL 2 GET_ITER - >> FOR_ITER 2 (to 40) - STORE_FAST 0 (res) + FOR_ITER 5 (to 46) + >> STORE_FAST 0 (res) -%3d JUMP_BACKWARD 3 (to 34) +%3d NOP -%3d >> LOAD_CONST 0 (None) +%3d FOR_END 3 (to 36) + LOAD_CONST 0 (None) + RETURN_VALUE + >> LOAD_CONST 0 (None) RETURN_VALUE """ % (bug708901.__code__.co_firstlineno, bug708901.__code__.co_firstlineno + 1, @@ -569,13 +572,13 @@ def foo(x): %3d RESUME 0 BUILD_LIST 0 LOAD_FAST 0 (.0) - >> FOR_ITER 7 (to 24) - STORE_FAST 1 (z) + FOR_ITER 7 (to 24) + >> STORE_FAST 1 (z) LOAD_DEREF 2 (x) LOAD_FAST 1 (z) BINARY_OP 0 (+) LIST_APPEND 2 - JUMP_BACKWARD 8 (to 8) + FOR_END 7 (to 10) >> RETURN_VALUE """ % (dis_nested_1, __file__, @@ -1226,7 +1229,7 @@ def _prepare_test_cases(): Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=20, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='GET_ITER', opcode=68, arg=None, argval=None, argrepr='', offset=30, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='FOR_ITER', opcode=93, arg=32, argval=98, argrepr='to 98', offset=32, starts_line=None, is_jump_target=True, positions=None), - Instruction(opname='STORE_FAST', opcode=125, arg=0, argval='i', argrepr='i', offset=34, starts_line=None, is_jump_target=False, positions=None), + Instruction(opname='STORE_FAST', opcode=125, arg=0, argval='i', argrepr='i', offset=34, starts_line=None, is_jump_target=True, positions=None), Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=36, starts_line=4, is_jump_target=False, positions=None), Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=48, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='PRECALL', opcode=166, arg=1, argval=1, argrepr='', offset=50, starts_line=None, is_jump_target=False, positions=None), @@ -1243,7 +1246,7 @@ def _prepare_test_cases(): Instruction(opname='POP_JUMP_FORWARD_IF_FALSE', opcode=114, arg=2, argval=96, argrepr='to 96', offset=90, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=92, starts_line=8, is_jump_target=False, positions=None), Instruction(opname='JUMP_FORWARD', opcode=110, arg=16, argval=128, argrepr='to 128', offset=94, starts_line=None, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=140, arg=33, argval=32, argrepr='to 32', offset=96, starts_line=7, is_jump_target=True, positions=None), + Instruction(opname='FOR_END', opcode=177, arg=32, argval=34, argrepr='to 34', offset=96, starts_line=3, is_jump_target=True, positions=None), Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=98, starts_line=10, is_jump_target=True, positions=None), Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=110, starts_line=None, is_jump_target=False, positions=None), Instruction(opname='PRECALL', opcode=166, arg=1, argval=1, argrepr='', offset=112, starts_line=None, is_jump_target=False, positions=None), diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 6a2f550d67052a..f486ba97699d7f 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -510,16 +510,16 @@ def f(x): def test_assignment_idiom_in_comprehensions(self): def listcomp(): return [y for x in a for y in [f(x)]] - self.assertEqual(count_instr_recursively(listcomp, 'FOR_ITER'), 1) + self.assertEqual(count_instr_recursively(listcomp, 'FOR_END'), 1) def setcomp(): return {y for x in a for y in [f(x)]} - self.assertEqual(count_instr_recursively(setcomp, 'FOR_ITER'), 1) + self.assertEqual(count_instr_recursively(setcomp, 'FOR_END'), 1) def dictcomp(): return {y: y for x in a for y in [f(x)]} - self.assertEqual(count_instr_recursively(dictcomp, 'FOR_ITER'), 1) + self.assertEqual(count_instr_recursively(dictcomp, 'FOR_END'), 1) def genexpr(): return (y for x in a for y in [f(x)]) - self.assertEqual(count_instr_recursively(genexpr, 'FOR_ITER'), 1) + self.assertEqual(count_instr_recursively(genexpr, 'FOR_END'), 1) def test_format_combinations(self): flags = '-+ #0' diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-04-10-21-49-23.gh-issue-91432.2yBtR8.rst b/Misc/NEWS.d/next/Core and Builtins/2022-04-10-21-49-23.gh-issue-91432.2yBtR8.rst new file mode 100644 index 00000000000000..c57e4eb22f4342 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-04-10-21-49-23.gh-issue-91432.2yBtR8.rst @@ -0,0 +1,5 @@ +Replaced the ``FOR_ITER`` opcode that lived at the top of ``for``-loops with a +``FOR_END`` opcode that lives at the bottom. This form of `Loop Inversion +`_ replaces the (unconditional +jump, conditional jump) pair with just one conditional jump during most +iterations. diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 7a4d2fac9382ce..d5cf942b9a7bd8 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -283,6 +283,19 @@ mark_stacks(PyCodeObject *code_obj, int len) stacks[j] = target_stack; break; } + case FOR_END: + { + int64_t target_stack = push_value(next_stack, Object); + stacks[i+1] = pop_value(next_stack); + j = i + 1 - get_arg(code, i); + if (stacks[j] == UNINITIALIZED && j < i) { + todo = 1; + } + assert(j < len); + assert(stacks[j] == UNINITIALIZED || stacks[j] == target_stack); + stacks[j] = target_stack; + break; + } case END_ASYNC_FOR: next_stack = pop_value(pop_value(pop_value(next_stack))); stacks[i+1] = next_stack; @@ -580,7 +593,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore } else if (err < 0) { if (start_stack == OVERFLOWED) { - msg = "stack to deep to analyze"; + msg = "stack too deep to analyze"; } else if (start_stack == UNINITIALIZED) { msg = "can't jump from within an exception handler"; diff --git a/Programs/test_frozenmain.h b/Programs/test_frozenmain.h index a3c09529116cc1..7bf06852ca81c0 100644 --- a/Programs/test_frozenmain.h +++ b/Programs/test_frozenmain.h @@ -1,7 +1,7 @@ // Auto-generated by Programs/freeze_test_frozenmain.py unsigned char M_test_frozenmain[] = { 227,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0, - 0,0,0,0,0,115,176,0,0,0,151,0,100,0,100,1, + 0,0,0,0,0,115,180,0,0,0,151,0,100,0,100,1, 108,0,90,0,100,0,100,1,108,1,90,1,2,0,101,2, 100,2,166,1,0,0,171,1,0,0,0,0,0,0,0,0, 1,0,2,0,101,2,100,3,101,0,106,3,0,0,0,0, @@ -9,38 +9,39 @@ unsigned char M_test_frozenmain[] = { 0,0,1,0,2,0,101,1,106,4,0,0,0,0,0,0, 0,0,166,0,0,0,171,0,0,0,0,0,0,0,0,0, 100,4,25,0,0,0,0,0,0,0,0,0,90,5,100,5, - 68,0,93,25,90,6,2,0,101,2,100,6,101,6,155,0, + 68,0,93,27,90,6,2,0,101,2,100,6,101,6,155,0, 100,7,101,5,101,6,25,0,0,0,0,0,0,0,0,0, 155,0,157,4,166,1,0,0,171,1,0,0,0,0,0,0, - 0,0,1,0,140,26,100,1,83,0,41,8,233,0,0,0, - 0,78,122,18,70,114,111,122,101,110,32,72,101,108,108,111, - 32,87,111,114,108,100,122,8,115,121,115,46,97,114,103,118, - 218,6,99,111,110,102,105,103,41,5,90,12,112,114,111,103, - 114,97,109,95,110,97,109,101,218,10,101,120,101,99,117,116, - 97,98,108,101,90,15,117,115,101,95,101,110,118,105,114,111, - 110,109,101,110,116,90,17,99,111,110,102,105,103,117,114,101, - 95,99,95,115,116,100,105,111,90,14,98,117,102,102,101,114, - 101,100,95,115,116,100,105,111,122,7,99,111,110,102,105,103, - 32,122,2,58,32,41,7,218,3,115,121,115,90,17,95,116, - 101,115,116,105,110,116,101,114,110,97,108,99,97,112,105,218, - 5,112,114,105,110,116,218,4,97,114,103,118,90,11,103,101, - 116,95,99,111,110,102,105,103,115,114,2,0,0,0,218,3, - 107,101,121,169,0,243,0,0,0,0,250,18,116,101,115,116, - 95,102,114,111,122,101,110,109,97,105,110,46,112,121,250,8, - 60,109,111,100,117,108,101,62,114,11,0,0,0,1,0,0, - 0,115,18,0,0,0,2,128,8,3,8,1,22,2,34,1, - 42,1,8,1,48,7,4,249,115,20,0,0,0,2,128,8, - 3,8,1,22,2,34,1,42,1,2,7,4,1,2,249,52, - 7,115,176,0,0,0,0,0,1,11,1,11,1,11,1,11, - 1,25,1,25,1,25,1,25,1,6,1,6,7,27,1,28, - 1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,6, - 1,6,7,17,19,22,19,27,19,27,19,27,19,27,19,27, - 1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28, - 10,39,10,27,10,39,10,39,10,39,10,39,10,39,10,41, - 10,41,10,41,10,41,10,41,10,41,10,41,42,50,10,51, - 10,51,10,51,10,51,10,51,1,7,12,2,1,42,1,42, - 5,8,5,10,5,10,11,41,21,24,11,41,11,41,28,34, - 35,38,28,39,28,39,28,39,28,39,28,39,11,41,11,41, - 5,42,5,42,5,42,5,42,5,42,5,42,5,42,5,42, - 5,42,1,42,1,42,114,9,0,0,0, + 0,0,1,0,177,25,100,1,83,0,100,1,83,0,41,8, + 233,0,0,0,0,78,122,18,70,114,111,122,101,110,32,72, + 101,108,108,111,32,87,111,114,108,100,122,8,115,121,115,46, + 97,114,103,118,218,6,99,111,110,102,105,103,41,5,90,12, + 112,114,111,103,114,97,109,95,110,97,109,101,218,10,101,120, + 101,99,117,116,97,98,108,101,90,15,117,115,101,95,101,110, + 118,105,114,111,110,109,101,110,116,90,17,99,111,110,102,105, + 103,117,114,101,95,99,95,115,116,100,105,111,90,14,98,117, + 102,102,101,114,101,100,95,115,116,100,105,111,122,7,99,111, + 110,102,105,103,32,122,2,58,32,41,7,218,3,115,121,115, + 90,17,95,116,101,115,116,105,110,116,101,114,110,97,108,99, + 97,112,105,218,5,112,114,105,110,116,218,4,97,114,103,118, + 90,11,103,101,116,95,99,111,110,102,105,103,115,114,2,0, + 0,0,218,3,107,101,121,169,0,243,0,0,0,0,250,18, + 116,101,115,116,95,102,114,111,122,101,110,109,97,105,110,46, + 112,121,250,8,60,109,111,100,117,108,101,62,114,11,0,0, + 0,1,0,0,0,115,18,0,0,0,2,128,8,3,8,1, + 22,2,34,1,42,1,8,1,46,7,10,249,115,24,0,0, + 0,2,128,8,3,8,1,22,2,34,1,42,1,2,7,4, + 1,2,249,46,7,6,255,4,1,115,180,0,0,0,0,0, + 1,11,1,11,1,11,1,11,1,25,1,25,1,25,1,25, + 1,6,1,6,7,27,1,28,1,28,1,28,1,28,1,28, + 1,28,1,28,1,28,1,6,1,6,7,17,19,22,19,27, + 19,27,19,27,19,27,19,27,1,28,1,28,1,28,1,28, + 1,28,1,28,1,28,1,28,10,39,10,27,10,39,10,39, + 10,39,10,39,10,39,10,41,10,41,10,41,10,41,10,41, + 10,41,10,41,42,50,10,51,10,51,10,51,10,51,10,51, + 1,7,12,2,1,42,1,42,5,8,5,10,5,10,11,41, + 21,24,11,41,11,41,28,34,35,38,28,39,28,39,28,39, + 28,39,28,39,11,41,11,41,5,42,5,42,5,42,5,42, + 5,42,5,42,5,42,5,42,12,2,12,2,12,2,1,42, + 1,42,114,9,0,0,0, }; diff --git a/Python/ceval.c b/Python/ceval.c index b46b1ef8429392..7bcdba98fd73bd 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4260,15 +4260,48 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int DISPATCH(); } - TARGET(FOR_ITER) { - PREDICTED(FOR_ITER); + TARGET(FOR_END) { /* before: [iter]; after: [iter, iter()] *or* [] */ + _PyCode_Warmup(frame->f_code); + JUMP_TO_INSTRUCTION(FOR_END_QUICK); + } + + TARGET(FOR_END_QUICK) { + PREDICTED(FOR_END_QUICK); PyObject *iter = TOP(); #ifdef Py_STATS extern int _PySpecialization_ClassifyIterator(PyObject *); - _py_stats.opcode_stats[FOR_ITER].specialization.failure++; - _py_stats.opcode_stats[FOR_ITER].specialization.failure_kinds[_PySpecialization_ClassifyIterator(iter)]++; + _py_stats.opcode_stats[FOR_END].specialization.failure++; + _py_stats.opcode_stats[FOR_END].specialization.failure_kinds[_PySpecialization_ClassifyIterator(iter)]++; #endif + PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter); + if (next != NULL) { + PUSH(next); + JUMPBY(-oparg); + CHECK_EVAL_BREAKER(); + PREDICT(STORE_FAST); + PREDICT(UNPACK_SEQUENCE); + DISPATCH(); + } + if (_PyErr_Occurred(tstate)) { + if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { + goto error; + } + else if (tstate->c_tracefunc != NULL) { + call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, frame); + } + _PyErr_Clear(tstate); + } + /* iterator ended normally */ + STACK_SHRINK(1); + Py_DECREF(iter); + DISPATCH(); + } + + TARGET(FOR_ITER) { + PREDICTED(FOR_ITER); + /* before: [iter]; after: [iter, iter()] *or* [] */ + PyObject *iter = TOP(); PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter); if (next != NULL) { PUSH(next); @@ -6835,13 +6868,17 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj, return -1; } if (line != -1 && f->f_trace_lines) { - /* Trace backward edges (except in 'yield from') or if line number has changed */ - int trace = line != lastline || - (_PyInterpreterFrame_LASTI(frame) < instr_prev && - // SEND has no quickened forms, so no need to use _PyOpcode_Deopt - // here: - _Py_OPCODE(*frame->prev_instr) != SEND); - if (trace) { + int prev_op = _PyOpcode_Deopt[_Py_OPCODE(code[instr_prev])]; + int next_op = _PyOpcode_Deopt[_Py_OPCODE(*frame->prev_instr)]; + // Trace before FOR_END, not after, even though a backwards + // jump happens after. However, don't trace on the first FOR_END + // of the for loop, since we're staying on the same line. + // Also don't trace JUMP_NO_INTERRUPT --> SEND. + bool line_number_changed = (line != lastline); + bool for_loop_end = (next_op == FOR_END); + bool back_jump = (_PyInterpreterFrame_LASTI(frame) < instr_prev && + next_op != SEND && prev_op != FOR_END); + if (line_number_changed || for_loop_end || back_jump) { result = call_trace(func, obj, tstate, frame, PyTrace_LINE, Py_None); } } diff --git a/Python/compile.c b/Python/compile.c index 38cf5f362705a7..890771430e7923 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -120,7 +120,8 @@ (opcode) == POP_JUMP_BACKWARD_IF_NONE || \ (opcode) == POP_JUMP_BACKWARD_IF_NOT_NONE || \ (opcode) == POP_JUMP_BACKWARD_IF_TRUE || \ - (opcode) == POP_JUMP_BACKWARD_IF_FALSE) + (opcode) == POP_JUMP_BACKWARD_IF_FALSE || \ + (opcode) == FOR_END) #define IS_TOP_LEVEL_AWAIT(c) ( \ @@ -1006,6 +1007,9 @@ stack_effect(int opcode, int oparg, int jump) case FOR_ITER: /* -1 at end of iterator, 1 if continue iterating. */ return jump > 0 ? -1 : 1; + case FOR_END: + /* -1 at end of iterator, 1 if continue iterating. */ + return jump == 0 ? -1 : 1; case SEND: return jump > 0 ? -1 : 0; case STORE_ATTR: @@ -3083,13 +3087,16 @@ compiler_if(struct compiler *c, stmt_ty s) static int compiler_for(struct compiler *c, stmt_ty s) { - basicblock *start, *body, *cleanup, *end; + basicblock *start, *body, *cleanup, *end, *back_jump; start = compiler_new_block(c); body = compiler_new_block(c); cleanup = compiler_new_block(c); + back_jump = compiler_new_block(c); end = compiler_new_block(c); - if (start == NULL || body == NULL || end == NULL || cleanup == NULL) { + if (start == NULL || body == NULL || end == NULL || + cleanup == NULL || back_jump == NULL) + { return 0; } if (!compiler_push_fblock(c, FOR_LOOP, start, end, NULL)) { @@ -3102,11 +3109,10 @@ compiler_for(struct compiler *c, stmt_ty s) compiler_use_next_block(c, body); VISIT(c, expr, s->v.For.target); VISIT_SEQ(c, stmt, s->v.For.body); - /* Mark jump as artificial */ - UNSET_LOC(c); - ADDOP_JUMP(c, JUMP, start); + compiler_use_next_block(c, back_jump); + SET_LOC(c, s->v.For.iter); + ADDOP_JUMP(c, FOR_END, body); compiler_use_next_block(c, cleanup); - compiler_pop_fblock(c, FOR_LOOP, start); VISIT_SEQ(c, stmt, s->v.For.orelse); @@ -5168,14 +5174,15 @@ compiler_sync_comprehension_generator(struct compiler *c, and then write to the element */ comprehension_ty gen; - basicblock *start, *anchor, *if_cleanup; + basicblock *start, *anchor, *if_cleanup, *body; Py_ssize_t i, n; start = compiler_new_block(c); if_cleanup = compiler_new_block(c); anchor = compiler_new_block(c); + body = compiler_new_block(c); - if (start == NULL || if_cleanup == NULL || anchor == NULL) { + if (start == NULL || if_cleanup == NULL || anchor == NULL || body == NULL) { return 0; } @@ -5218,6 +5225,7 @@ compiler_sync_comprehension_generator(struct compiler *c, depth++; compiler_use_next_block(c, start); ADDOP_JUMP(c, FOR_ITER, anchor); + compiler_use_next_block(c, body); } VISIT(c, expr, gen->target); @@ -5265,7 +5273,7 @@ compiler_sync_comprehension_generator(struct compiler *c, } compiler_use_next_block(c, if_cleanup); if (start) { - ADDOP_JUMP(c, JUMP, start); + ADDOP_JUMP(c, FOR_END, body); compiler_use_next_block(c, anchor); } @@ -8920,16 +8928,10 @@ optimize_basic_block(struct compiler *c, basicblock *bb, PyObject *consts) i -= jump_thread(inst, target, JUMP); } break; - case FOR_ITER: - if (target->i_opcode == JUMP) { - /* This will not work now because the jump (at target) could - * be forward or backward and FOR_ITER only jumps forward. We - * can re-enable this if ever we implement a backward version - * of FOR_ITER. - */ - /* - i -= jump_thread(inst, target, FOR_ITER); - */ + case FOR_END: + switch (target->i_opcode) { + case JUMP: + i -= jump_thread(inst, target, FOR_END); } break; case SWAP: @@ -9061,7 +9063,7 @@ normalize_basic_block(basicblock *bb) { case POP_JUMP_IF_TRUE: case JUMP_IF_FALSE_OR_POP: case JUMP_IF_TRUE_OR_POP: - case FOR_ITER: + case FOR_END: if (i != bb->b_iused-1) { PyErr_SetString(PyExc_SystemError, "malformed control flow graph."); return -1; diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 5268c4f2a27750..6c28fe22b5d6f0 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -37,6 +37,7 @@ static void *opcode_targets[256] = { &&TARGET_PUSH_EXC_INFO, &&TARGET_CHECK_EXC_MATCH, &&TARGET_CHECK_EG_MATCH, + &&TARGET_FOR_END_QUICK, &&TARGET_LOAD_ATTR_ADAPTIVE, &&TARGET_LOAD_ATTR_INSTANCE_VALUE, &&TARGET_LOAD_ATTR_MODULE, @@ -47,40 +48,39 @@ static void *opcode_targets[256] = { &&TARGET_LOAD_FAST__LOAD_FAST, &&TARGET_LOAD_GLOBAL_ADAPTIVE, &&TARGET_LOAD_GLOBAL_BUILTIN, - &&TARGET_LOAD_GLOBAL_MODULE, &&TARGET_WITH_EXCEPT_START, &&TARGET_GET_AITER, &&TARGET_GET_ANEXT, &&TARGET_BEFORE_ASYNC_WITH, &&TARGET_BEFORE_WITH, &&TARGET_END_ASYNC_FOR, + &&TARGET_LOAD_GLOBAL_MODULE, &&TARGET_LOAD_METHOD_ADAPTIVE, &&TARGET_LOAD_METHOD_CLASS, &&TARGET_LOAD_METHOD_MODULE, &&TARGET_LOAD_METHOD_NO_DICT, - &&TARGET_LOAD_METHOD_WITH_DICT, &&TARGET_STORE_SUBSCR, &&TARGET_DELETE_SUBSCR, + &&TARGET_LOAD_METHOD_WITH_DICT, &&TARGET_LOAD_METHOD_WITH_VALUES, &&TARGET_PRECALL_ADAPTIVE, &&TARGET_PRECALL_BOUND_METHOD, &&TARGET_PRECALL_BUILTIN_CLASS, &&TARGET_PRECALL_BUILTIN_FAST_WITH_KEYWORDS, - &&TARGET_PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, &&TARGET_GET_ITER, &&TARGET_GET_YIELD_FROM_ITER, &&TARGET_PRINT_EXPR, &&TARGET_LOAD_BUILD_CLASS, + &&TARGET_PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, &&TARGET_PRECALL_NO_KW_BUILTIN_FAST, - &&TARGET_PRECALL_NO_KW_BUILTIN_O, &&TARGET_LOAD_ASSERTION_ERROR, &&TARGET_RETURN_GENERATOR, + &&TARGET_PRECALL_NO_KW_BUILTIN_O, &&TARGET_PRECALL_NO_KW_ISINSTANCE, &&TARGET_PRECALL_NO_KW_LEN, &&TARGET_PRECALL_NO_KW_LIST_APPEND, &&TARGET_PRECALL_NO_KW_METHOD_DESCRIPTOR_FAST, &&TARGET_PRECALL_NO_KW_METHOD_DESCRIPTOR_NOARGS, - &&TARGET_PRECALL_NO_KW_METHOD_DESCRIPTOR_O, &&TARGET_LIST_TO_TUPLE, &&TARGET_RETURN_VALUE, &&TARGET_IMPORT_STAR, @@ -112,7 +112,7 @@ static void *opcode_targets[256] = { &&TARGET_JUMP_FORWARD, &&TARGET_JUMP_IF_FALSE_OR_POP, &&TARGET_JUMP_IF_TRUE_OR_POP, - &&TARGET_PRECALL_NO_KW_STR_1, + &&TARGET_PRECALL_NO_KW_METHOD_DESCRIPTOR_O, &&TARGET_POP_JUMP_FORWARD_IF_FALSE, &&TARGET_POP_JUMP_FORWARD_IF_TRUE, &&TARGET_LOAD_GLOBAL, @@ -120,13 +120,13 @@ static void *opcode_targets[256] = { &&TARGET_CONTAINS_OP, &&TARGET_RERAISE, &&TARGET_COPY, - &&TARGET_PRECALL_NO_KW_TUPLE_1, + &&TARGET_PRECALL_NO_KW_STR_1, &&TARGET_BINARY_OP, &&TARGET_SEND, &&TARGET_LOAD_FAST, &&TARGET_STORE_FAST, &&TARGET_DELETE_FAST, - &&TARGET_PRECALL_NO_KW_TYPE_1, + &&TARGET_PRECALL_NO_KW_TUPLE_1, &&TARGET_POP_JUMP_FORWARD_IF_NOT_NONE, &&TARGET_POP_JUMP_FORWARD_IF_NONE, &&TARGET_RAISE_VARARGS, @@ -140,42 +140,44 @@ static void *opcode_targets[256] = { &&TARGET_STORE_DEREF, &&TARGET_DELETE_DEREF, &&TARGET_JUMP_BACKWARD, - &&TARGET_PRECALL_PYFUNC, + &&TARGET_PRECALL_NO_KW_TYPE_1, &&TARGET_CALL_FUNCTION_EX, - &&TARGET_RESUME_QUICK, + &&TARGET_PRECALL_PYFUNC, &&TARGET_EXTENDED_ARG, &&TARGET_LIST_APPEND, &&TARGET_SET_ADD, &&TARGET_MAP_ADD, &&TARGET_LOAD_CLASSDEREF, &&TARGET_COPY_FREE_VARS, - &&TARGET_STORE_ATTR_ADAPTIVE, + &&TARGET_RESUME_QUICK, &&TARGET_RESUME, &&TARGET_MATCH_CLASS, + &&TARGET_STORE_ATTR_ADAPTIVE, &&TARGET_STORE_ATTR_INSTANCE_VALUE, - &&TARGET_STORE_ATTR_SLOT, &&TARGET_FORMAT_VALUE, &&TARGET_BUILD_CONST_KEY_MAP, &&TARGET_BUILD_STRING, + &&TARGET_STORE_ATTR_SLOT, &&TARGET_STORE_ATTR_WITH_HINT, - &&TARGET_STORE_FAST__LOAD_FAST, &&TARGET_LOAD_METHOD, - &&TARGET_STORE_FAST__STORE_FAST, + &&TARGET_STORE_FAST__LOAD_FAST, &&TARGET_LIST_EXTEND, &&TARGET_SET_UPDATE, &&TARGET_DICT_MERGE, &&TARGET_DICT_UPDATE, &&TARGET_PRECALL, + &&TARGET_STORE_FAST__STORE_FAST, &&TARGET_STORE_SUBSCR_ADAPTIVE, &&TARGET_STORE_SUBSCR_DICT, &&TARGET_STORE_SUBSCR_LIST_INT, - &&TARGET_UNPACK_SEQUENCE_ADAPTIVE, &&TARGET_CALL, &&TARGET_KW_NAMES, &&TARGET_POP_JUMP_BACKWARD_IF_NOT_NONE, &&TARGET_POP_JUMP_BACKWARD_IF_NONE, &&TARGET_POP_JUMP_BACKWARD_IF_FALSE, &&TARGET_POP_JUMP_BACKWARD_IF_TRUE, + &&TARGET_FOR_END, + &&TARGET_UNPACK_SEQUENCE_ADAPTIVE, &&TARGET_UNPACK_SEQUENCE_LIST, &&TARGET_UNPACK_SEQUENCE_TUPLE, &&TARGET_UNPACK_SEQUENCE_TWO_TUPLE, @@ -252,7 +254,5 @@ static void *opcode_targets[256] = { &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, - &&_unknown_opcode, - &&_unknown_opcode, &&TARGET_DO_TRACING }; diff --git a/Python/specialize.c b/Python/specialize.c index 3a8b768549c63d..38325602c14a8f 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -141,7 +141,7 @@ print_spec_stats(FILE *out, OpcodeStats *stats) { /* Mark some opcodes as specializable for stats, * even though we don't specialize them yet. */ - fprintf(out, "opcode[%d].specializable : 1\n", FOR_ITER); + fprintf(out, "opcode[%d].specializable : 1\n", FOR_END); for (int i = 0; i < 256; i++) { if (_PyOpcode_Adaptive[i]) { fprintf(out, "opcode[%d].specializable : 1\n", i); @@ -273,6 +273,9 @@ _PyCode_Quicken(PyCodeObject *code) case JUMP_BACKWARD: _Py_SET_OPCODE(instructions[i], JUMP_BACKWARD_QUICK); break; + case FOR_END: + _Py_SET_OPCODE(instructions[i], FOR_END_QUICK); + break; case RESUME: _Py_SET_OPCODE(instructions[i], RESUME_QUICK); break; @@ -451,21 +454,21 @@ initial_counter_value(void) { #define SPEC_FAIL_COMPARE_OP_LONG_FLOAT 23 #define SPEC_FAIL_COMPARE_OP_EXTENDED_ARG 24 -/* FOR_ITER */ -#define SPEC_FAIL_FOR_ITER_GENERATOR 10 -#define SPEC_FAIL_FOR_ITER_COROUTINE 11 -#define SPEC_FAIL_FOR_ITER_ASYNC_GENERATOR 12 -#define SPEC_FAIL_FOR_ITER_LIST 13 -#define SPEC_FAIL_FOR_ITER_TUPLE 14 -#define SPEC_FAIL_FOR_ITER_SET 15 -#define SPEC_FAIL_FOR_ITER_STRING 16 -#define SPEC_FAIL_FOR_ITER_BYTES 17 -#define SPEC_FAIL_FOR_ITER_RANGE 18 -#define SPEC_FAIL_FOR_ITER_ITERTOOLS 19 -#define SPEC_FAIL_FOR_ITER_DICT_KEYS 20 -#define SPEC_FAIL_FOR_ITER_DICT_ITEMS 21 -#define SPEC_FAIL_FOR_ITER_DICT_VALUES 22 -#define SPEC_FAIL_FOR_ITER_ENUMERATE 23 +/* FOR_END */ +#define SPEC_FAIL_FOR_END_GENERATOR 10 +#define SPEC_FAIL_FOR_END_COROUTINE 11 +#define SPEC_FAIL_FOR_END_ASYNC_GENERATOR 12 +#define SPEC_FAIL_FOR_END_LIST 13 +#define SPEC_FAIL_FOR_END_TUPLE 14 +#define SPEC_FAIL_FOR_END_SET 15 +#define SPEC_FAIL_FOR_END_STRING 16 +#define SPEC_FAIL_FOR_END_BYTES 17 +#define SPEC_FAIL_FOR_END_RANGE 18 +#define SPEC_FAIL_FOR_END_ITERTOOLS 19 +#define SPEC_FAIL_FOR_END_DICT_KEYS 20 +#define SPEC_FAIL_FOR_END_DICT_ITEMS 21 +#define SPEC_FAIL_FOR_END_DICT_VALUES 22 +#define SPEC_FAIL_FOR_END_ENUMERATE 23 // UNPACK_SEQUENCE @@ -2009,48 +2012,48 @@ int _PySpecialization_ClassifyIterator(PyObject *iter) { if (PyGen_CheckExact(iter)) { - return SPEC_FAIL_FOR_ITER_GENERATOR; + return SPEC_FAIL_FOR_END_GENERATOR; } if (PyCoro_CheckExact(iter)) { - return SPEC_FAIL_FOR_ITER_COROUTINE; + return SPEC_FAIL_FOR_END_COROUTINE; } if (PyAsyncGen_CheckExact(iter)) { - return SPEC_FAIL_FOR_ITER_ASYNC_GENERATOR; + return SPEC_FAIL_FOR_END_ASYNC_GENERATOR; } PyTypeObject *t = Py_TYPE(iter); if (t == &PyListIter_Type) { - return SPEC_FAIL_FOR_ITER_LIST; + return SPEC_FAIL_FOR_END_LIST; } if (t == &PyTupleIter_Type) { - return SPEC_FAIL_FOR_ITER_TUPLE; + return SPEC_FAIL_FOR_END_TUPLE; } if (t == &PyDictIterKey_Type) { - return SPEC_FAIL_FOR_ITER_DICT_KEYS; + return SPEC_FAIL_FOR_END_DICT_KEYS; } if (t == &PyDictIterValue_Type) { - return SPEC_FAIL_FOR_ITER_DICT_VALUES; + return SPEC_FAIL_FOR_END_DICT_VALUES; } if (t == &PyDictIterItem_Type) { - return SPEC_FAIL_FOR_ITER_DICT_ITEMS; + return SPEC_FAIL_FOR_END_DICT_ITEMS; } if (t == &PySetIter_Type) { - return SPEC_FAIL_FOR_ITER_SET; + return SPEC_FAIL_FOR_END_SET; } if (t == &PyUnicodeIter_Type) { - return SPEC_FAIL_FOR_ITER_STRING; + return SPEC_FAIL_FOR_END_STRING; } if (t == &PyBytesIter_Type) { - return SPEC_FAIL_FOR_ITER_BYTES; + return SPEC_FAIL_FOR_END_BYTES; } if (t == &PyRangeIter_Type) { - return SPEC_FAIL_FOR_ITER_RANGE; + return SPEC_FAIL_FOR_END_RANGE; } if (t == &PyEnum_Type) { - return SPEC_FAIL_FOR_ITER_ENUMERATE; + return SPEC_FAIL_FOR_END_ENUMERATE; } if (strncmp(t->tp_name, "itertools", 8) == 0) { - return SPEC_FAIL_FOR_ITER_ITERTOOLS; + return SPEC_FAIL_FOR_END_ITERTOOLS; } return SPEC_FAIL_OTHER; }