mirror of
https://github.com/python/cpython.git
synced 2025-08-04 17:08:35 +00:00
GH-128682: Make PyStackRef_CLOSE
escaping. (GH-129404)
This commit is contained in:
parent
218f205f20
commit
808071b994
12 changed files with 1348 additions and 657 deletions
2
Include/internal/pycore_opcode_metadata.h
generated
2
Include/internal/pycore_opcode_metadata.h
generated
|
@ -2136,7 +2136,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = {
|
|||
[LOAD_ATTR_CLASS] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_EXIT_FLAG },
|
||||
[LOAD_ATTR_CLASS_WITH_METACLASS_CHECK] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_EXIT_FLAG },
|
||||
[LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG },
|
||||
[LOAD_ATTR_INSTANCE_VALUE] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG },
|
||||
[LOAD_ATTR_INSTANCE_VALUE] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ESCAPES_FLAG },
|
||||
[LOAD_ATTR_METHOD_LAZY_DICT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG },
|
||||
[LOAD_ATTR_METHOD_NO_DICT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_EXIT_FLAG },
|
||||
[LOAD_ATTR_METHOD_WITH_VALUES] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG },
|
||||
|
|
12
Include/internal/pycore_uop_metadata.h
generated
12
Include/internal/pycore_uop_metadata.h
generated
|
@ -154,7 +154,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = {
|
|||
[_GUARD_TYPE_VERSION] = HAS_EXIT_FLAG,
|
||||
[_GUARD_TYPE_VERSION_AND_LOCK] = HAS_EXIT_FLAG,
|
||||
[_CHECK_MANAGED_OBJECT_HAS_VALUES] = HAS_DEOPT_FLAG,
|
||||
[_LOAD_ATTR_INSTANCE_VALUE] = HAS_DEOPT_FLAG,
|
||||
[_LOAD_ATTR_INSTANCE_VALUE] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG,
|
||||
[_CHECK_ATTR_MODULE_PUSH_KEYS] = HAS_DEOPT_FLAG,
|
||||
[_LOAD_ATTR_MODULE_FROM_KEYS] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG,
|
||||
[_CHECK_ATTR_WITH_HINT] = HAS_EXIT_FLAG,
|
||||
|
@ -964,7 +964,7 @@ int _PyUop_num_popped(int opcode, int oparg)
|
|||
case _CHECK_METHOD_VERSION:
|
||||
return 0;
|
||||
case _EXPAND_METHOD:
|
||||
return 2 + oparg;
|
||||
return 0;
|
||||
case _CHECK_IS_NOT_PY_CALLABLE:
|
||||
return 0;
|
||||
case _CALL_NON_PY_GENERAL:
|
||||
|
@ -972,7 +972,7 @@ int _PyUop_num_popped(int opcode, int oparg)
|
|||
case _CHECK_CALL_BOUND_METHOD_EXACT_ARGS:
|
||||
return 0;
|
||||
case _INIT_CALL_BOUND_METHOD_EXACT_ARGS:
|
||||
return 2 + oparg;
|
||||
return 0;
|
||||
case _CHECK_PEP_523:
|
||||
return 0;
|
||||
case _CHECK_FUNCTION_EXACT_ARGS:
|
||||
|
@ -1036,7 +1036,7 @@ int _PyUop_num_popped(int opcode, int oparg)
|
|||
case _CHECK_METHOD_VERSION_KW:
|
||||
return 0;
|
||||
case _EXPAND_METHOD_KW:
|
||||
return 3 + oparg;
|
||||
return 0;
|
||||
case _CHECK_IS_NOT_PY_CALLABLE_KW:
|
||||
return 0;
|
||||
case _CALL_KW_NON_PY:
|
||||
|
@ -1062,7 +1062,7 @@ int _PyUop_num_popped(int opcode, int oparg)
|
|||
case _BINARY_OP:
|
||||
return 2;
|
||||
case _SWAP:
|
||||
return 2 + (oparg-2);
|
||||
return 0;
|
||||
case _GUARD_IS_TRUE_POP:
|
||||
return 1;
|
||||
case _GUARD_IS_FALSE_POP:
|
||||
|
@ -1110,7 +1110,7 @@ int _PyUop_num_popped(int opcode, int oparg)
|
|||
case _DEOPT:
|
||||
return 0;
|
||||
case _ERROR_POP_N:
|
||||
return oparg;
|
||||
return 0;
|
||||
case _TIER2_RESUME_CHECK:
|
||||
return 0;
|
||||
default:
|
||||
|
|
|
@ -538,7 +538,9 @@ class TestGeneratedCases(unittest.TestCase):
|
|||
frame->instr_ptr = next_instr;
|
||||
next_instr += 1;
|
||||
INSTRUCTION_STATS(OP);
|
||||
if (cond) goto label;
|
||||
if (cond) {
|
||||
goto label;
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
"""
|
||||
|
@ -555,7 +557,9 @@ class TestGeneratedCases(unittest.TestCase):
|
|||
frame->instr_ptr = next_instr;
|
||||
next_instr += 1;
|
||||
INSTRUCTION_STATS(OP);
|
||||
if (cond) goto label;
|
||||
if (cond) {
|
||||
goto label;
|
||||
}
|
||||
// Comment is ok
|
||||
DISPATCH();
|
||||
}
|
||||
|
@ -582,7 +586,9 @@ class TestGeneratedCases(unittest.TestCase):
|
|||
right = stack_pointer[-1];
|
||||
left = stack_pointer[-2];
|
||||
SPAM(left, right);
|
||||
if (cond) goto pop_2_label;
|
||||
if (cond) {
|
||||
goto pop_2_label;
|
||||
}
|
||||
res = 0;
|
||||
stack_pointer[-2] = res;
|
||||
stack_pointer += -1;
|
||||
|
@ -611,7 +617,9 @@ class TestGeneratedCases(unittest.TestCase):
|
|||
right = stack_pointer[-1];
|
||||
left = stack_pointer[-2];
|
||||
res = SPAM(left, right);
|
||||
if (cond) goto pop_2_label;
|
||||
if (cond) {
|
||||
goto pop_2_label;
|
||||
}
|
||||
stack_pointer[-2] = res;
|
||||
stack_pointer += -1;
|
||||
assert(WITHIN_STACK_BOUNDS());
|
||||
|
@ -1392,7 +1400,9 @@ class TestGeneratedCases(unittest.TestCase):
|
|||
// THIRD
|
||||
{
|
||||
// Mark j and k as used
|
||||
if (cond) goto pop_2_error;
|
||||
if (cond) {
|
||||
goto pop_2_error;
|
||||
}
|
||||
}
|
||||
stack_pointer += -2;
|
||||
assert(WITHIN_STACK_BOUNDS());
|
||||
|
|
|
@ -2130,8 +2130,7 @@ dummy_func(
|
|||
PyStackRef_CLOSE(self_st);
|
||||
self_or_null = PyStackRef_NULL;
|
||||
}
|
||||
PyStackRef_CLOSE(class_st);
|
||||
PyStackRef_CLOSE(global_super_st);
|
||||
DECREF_INPUTS();
|
||||
|
||||
attr = PyStackRef_FromPyObjectSteal(attr_o);
|
||||
}
|
||||
|
@ -2245,7 +2244,7 @@ dummy_func(
|
|||
attr = PyStackRef_FromPyObjectNew(attr_o);
|
||||
#endif
|
||||
STAT_INC(LOAD_ATTR, hit);
|
||||
DECREF_INPUTS();
|
||||
PyStackRef_CLOSE(owner);
|
||||
}
|
||||
|
||||
macro(LOAD_ATTR_INSTANCE_VALUE) =
|
||||
|
@ -3671,15 +3670,14 @@ dummy_func(
|
|||
EXIT_IF(!PyStackRef_IsNull(null[0]));
|
||||
}
|
||||
|
||||
op(_EXPAND_METHOD, (callable[1], null[1], unused[oparg] -- method[1], self[1], unused[oparg])) {
|
||||
op(_EXPAND_METHOD, (callable[1], self_or_null[1], unused[oparg] -- callable[1], self_or_null[1], unused[oparg])) {
|
||||
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
|
||||
assert(PyStackRef_IsNull(null[0]));
|
||||
DEAD(null);
|
||||
assert(PyStackRef_IsNull(self_or_null[0]));
|
||||
assert(Py_TYPE(callable_o) == &PyMethod_Type);
|
||||
self[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self);
|
||||
self_or_null[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self);
|
||||
_PyStackRef temp = callable[0];
|
||||
method[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func);
|
||||
assert(PyStackRef_FunctionCheck(method[0]));
|
||||
callable[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func);
|
||||
assert(PyStackRef_FunctionCheck(callable[0]));
|
||||
PyStackRef_CLOSE(temp);
|
||||
}
|
||||
|
||||
|
@ -3740,13 +3738,13 @@ dummy_func(
|
|||
EXIT_IF(Py_TYPE(PyStackRef_AsPyObjectBorrow(callable[0])) != &PyMethod_Type);
|
||||
}
|
||||
|
||||
op(_INIT_CALL_BOUND_METHOD_EXACT_ARGS, (callable[1], null[1], unused[oparg] -- func[1], self[1], unused[oparg])) {
|
||||
DEAD(null);
|
||||
op(_INIT_CALL_BOUND_METHOD_EXACT_ARGS, (callable[1], self_or_null[1], unused[oparg] -- callable[1], self_or_null[1], unused[oparg])) {
|
||||
assert(PyStackRef_IsNull(self_or_null[0]));
|
||||
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
|
||||
STAT_INC(CALL, hit);
|
||||
self[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self);
|
||||
self_or_null[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self);
|
||||
_PyStackRef temp = callable[0];
|
||||
func[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func);
|
||||
callable[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func);
|
||||
PyStackRef_CLOSE(temp);
|
||||
}
|
||||
|
||||
|
@ -4171,8 +4169,9 @@ dummy_func(
|
|||
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
|
||||
|
||||
int total_args = oparg;
|
||||
_PyStackRef *arguments = args;
|
||||
if (!PyStackRef_IsNull(self_or_null[0])) {
|
||||
args--;
|
||||
arguments--;
|
||||
total_args++;
|
||||
}
|
||||
|
||||
|
@ -4183,8 +4182,8 @@ dummy_func(
|
|||
EXIT_IF(meth->ml_flags != METH_O);
|
||||
// CPython promises to check all non-vectorcall function calls.
|
||||
EXIT_IF(tstate->c_recursion_remaining <= 0);
|
||||
_PyStackRef arg_stackref = args[1];
|
||||
_PyStackRef self_stackref = args[0];
|
||||
_PyStackRef arg_stackref = arguments[1];
|
||||
_PyStackRef self_stackref = arguments[0];
|
||||
EXIT_IF(!Py_IS_TYPE(PyStackRef_AsPyObjectBorrow(self_stackref),
|
||||
method->d_common.d_type));
|
||||
STAT_INC(CALL, hit);
|
||||
|
@ -4195,11 +4194,7 @@ dummy_func(
|
|||
PyStackRef_AsPyObjectBorrow(arg_stackref));
|
||||
_Py_LeaveRecursiveCallTstate(tstate);
|
||||
assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
|
||||
PyStackRef_CLOSE(self_stackref);
|
||||
PyStackRef_CLOSE(arg_stackref);
|
||||
DEAD(args);
|
||||
DEAD(self_or_null);
|
||||
PyStackRef_CLOSE(callable[0]);
|
||||
DECREF_INPUTS();
|
||||
ERROR_IF(res_o == NULL, error);
|
||||
res = PyStackRef_FromPyObjectSteal(res_o);
|
||||
}
|
||||
|
@ -4486,15 +4481,14 @@ dummy_func(
|
|||
EXIT_IF(!PyStackRef_IsNull(null[0]));
|
||||
}
|
||||
|
||||
op(_EXPAND_METHOD_KW, (callable[1], null[1], unused[oparg], unused -- method[1], self[1], unused[oparg], unused)) {
|
||||
op(_EXPAND_METHOD_KW, (callable[1], self_or_null[1], unused[oparg], unused -- callable[1], self_or_null[1], unused[oparg], unused)) {
|
||||
assert(PyStackRef_IsNull(self_or_null[0]));
|
||||
_PyStackRef callable_s = callable[0];
|
||||
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable_s);
|
||||
|
||||
assert(PyStackRef_IsNull(null[0]));
|
||||
assert(Py_TYPE(callable_o) == &PyMethod_Type);
|
||||
self[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self);
|
||||
method[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func);
|
||||
assert(PyStackRef_FunctionCheck(method[0]));
|
||||
self_or_null[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self);
|
||||
callable[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func);
|
||||
assert(PyStackRef_FunctionCheck(callable[0]));
|
||||
PyStackRef_CLOSE(callable_s);
|
||||
}
|
||||
|
||||
|
@ -4600,7 +4594,8 @@ dummy_func(
|
|||
}
|
||||
}
|
||||
|
||||
op(_DO_CALL_FUNCTION_EX, (func_st, unused, callargs_st, kwargs_st -- result)) {
|
||||
op(_DO_CALL_FUNCTION_EX, (func_st, null, callargs_st, kwargs_st -- result)) {
|
||||
(void)null;
|
||||
PyObject *func = PyStackRef_AsPyObjectBorrow(func_st);
|
||||
|
||||
// DICT_MERGE is called before this opcode if there are kwargs.
|
||||
|
@ -4671,8 +4666,8 @@ dummy_func(
|
|||
result_o = PyObject_Call(func, callargs, kwargs);
|
||||
}
|
||||
PyStackRef_XCLOSE(kwargs_st);
|
||||
DEAD(kwargs_st);
|
||||
PyStackRef_CLOSE(callargs_st);
|
||||
DEAD(null);
|
||||
PyStackRef_CLOSE(func_st);
|
||||
ERROR_IF(result_o == NULL, error);
|
||||
result = PyStackRef_FromPyObjectSteal(result_o);
|
||||
|
@ -4809,12 +4804,11 @@ dummy_func(
|
|||
|
||||
macro(BINARY_OP) = _SPECIALIZE_BINARY_OP + unused/4 + _BINARY_OP;
|
||||
|
||||
pure inst(SWAP, (bottom_in, unused[oparg-2], top_in --
|
||||
top_out, unused[oparg-2], bottom_out)) {
|
||||
bottom_out = bottom_in;
|
||||
DEAD(bottom_in);
|
||||
top_out = top_in;
|
||||
DEAD(top_in);
|
||||
pure inst(SWAP, (bottom[1], unused[oparg-2], top[1] --
|
||||
bottom[1], unused[oparg-2], top[1])) {
|
||||
_PyStackRef temp = bottom[0];
|
||||
bottom[0] = top[0];
|
||||
top[0] = temp;
|
||||
assert(oparg >= 2);
|
||||
}
|
||||
|
||||
|
@ -5174,7 +5168,8 @@ dummy_func(
|
|||
EXIT_TO_TIER1();
|
||||
}
|
||||
|
||||
tier2 op(_ERROR_POP_N, (target/2, unused[oparg] --)) {
|
||||
tier2 op(_ERROR_POP_N, (target/2 --)) {
|
||||
assert(oparg == 0);
|
||||
frame->instr_ptr = _PyFrame_GetBytecode(frame) + target;
|
||||
SYNC_SP();
|
||||
GOTO_UNWIND();
|
||||
|
@ -5193,18 +5188,18 @@ dummy_func(
|
|||
}
|
||||
|
||||
label(pop_4_error) {
|
||||
STACK_SHRINK(1);
|
||||
goto pop_3_error;
|
||||
STACK_SHRINK(4);
|
||||
goto error;
|
||||
}
|
||||
|
||||
label(pop_3_error) {
|
||||
STACK_SHRINK(1);
|
||||
goto pop_2_error;
|
||||
STACK_SHRINK(3);
|
||||
goto error;
|
||||
}
|
||||
|
||||
label(pop_2_error) {
|
||||
STACK_SHRINK(1);
|
||||
goto pop_1_error;
|
||||
STACK_SHRINK(2);
|
||||
goto error;
|
||||
}
|
||||
|
||||
label(pop_1_error) {
|
||||
|
|
804
Python/executor_cases.c.h
generated
804
Python/executor_cases.c.h
generated
File diff suppressed because it is too large
Load diff
879
Python/generated_cases.c.h
generated
879
Python/generated_cases.c.h
generated
File diff suppressed because it is too large
Load diff
|
@ -989,7 +989,6 @@ prepare_for_execution(_PyUOpInstruction *buffer, int length)
|
|||
current_error = next_spare;
|
||||
current_error_target = target;
|
||||
make_exit(&buffer[next_spare], _ERROR_POP_N, 0);
|
||||
buffer[next_spare].oparg = popped;
|
||||
buffer[next_spare].operand0 = target;
|
||||
next_spare++;
|
||||
}
|
||||
|
|
|
@ -512,10 +512,11 @@ dummy_func(void) {
|
|||
top = bottom;
|
||||
}
|
||||
|
||||
op(_SWAP, (bottom_in, unused[oparg-2], top_in --
|
||||
top_out, unused[oparg-2], bottom_out)) {
|
||||
bottom_out = bottom_in;
|
||||
top_out = top_in;
|
||||
op(_SWAP, (bottom[1], unused[oparg-2], top[1] -- bottom[1], unused[oparg-2], top[1])) {
|
||||
JitOptSymbol *temp = bottom[0];
|
||||
bottom[0] = top[0];
|
||||
top[0] = temp;
|
||||
assert(oparg >= 2);
|
||||
}
|
||||
|
||||
op(_LOAD_ATTR_INSTANCE_VALUE, (offset/1, owner -- attr)) {
|
||||
|
@ -629,10 +630,10 @@ dummy_func(void) {
|
|||
ctx->done = true;
|
||||
}
|
||||
|
||||
op(_INIT_CALL_BOUND_METHOD_EXACT_ARGS, (callable, unused, unused[oparg] -- func, self, unused[oparg])) {
|
||||
op(_INIT_CALL_BOUND_METHOD_EXACT_ARGS, (callable[1], self_or_null[1], unused[oparg] -- callable[1], self_or_null[1], unused[oparg])) {
|
||||
(void)callable;
|
||||
func = sym_new_not_null(ctx);
|
||||
self = sym_new_not_null(ctx);
|
||||
callable[0] = sym_new_not_null(ctx);
|
||||
self_or_null[0] = sym_new_not_null(ctx);
|
||||
}
|
||||
|
||||
op(_CHECK_FUNCTION_VERSION, (func_version/2, callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) {
|
||||
|
|
46
Python/optimizer_cases.c.h
generated
46
Python/optimizer_cases.c.h
generated
|
@ -1837,12 +1837,6 @@
|
|||
}
|
||||
|
||||
case _EXPAND_METHOD: {
|
||||
JitOptSymbol **method;
|
||||
JitOptSymbol **self;
|
||||
method = &stack_pointer[-2 - oparg];
|
||||
self = &stack_pointer[-1 - oparg];
|
||||
method[0] = sym_new_not_null(ctx);
|
||||
self[0] = sym_new_not_null(ctx);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1870,15 +1864,13 @@
|
|||
}
|
||||
|
||||
case _INIT_CALL_BOUND_METHOD_EXACT_ARGS: {
|
||||
JitOptSymbol *callable;
|
||||
JitOptSymbol *func;
|
||||
JitOptSymbol *self;
|
||||
callable = stack_pointer[-2 - oparg];
|
||||
JitOptSymbol **self_or_null;
|
||||
JitOptSymbol **callable;
|
||||
self_or_null = &stack_pointer[-1 - oparg];
|
||||
callable = &stack_pointer[-2 - oparg];
|
||||
(void)callable;
|
||||
func = sym_new_not_null(ctx);
|
||||
self = sym_new_not_null(ctx);
|
||||
stack_pointer[-2 - oparg] = func;
|
||||
stack_pointer[-1 - oparg] = self;
|
||||
callable[0] = sym_new_not_null(ctx);
|
||||
self_or_null[0] = sym_new_not_null(ctx);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2214,12 +2206,6 @@
|
|||
}
|
||||
|
||||
case _EXPAND_METHOD_KW: {
|
||||
JitOptSymbol **method;
|
||||
JitOptSymbol **self;
|
||||
method = &stack_pointer[-3 - oparg];
|
||||
self = &stack_pointer[-2 - oparg];
|
||||
method[0] = sym_new_not_null(ctx);
|
||||
self[0] = sym_new_not_null(ctx);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2410,16 +2396,14 @@
|
|||
}
|
||||
|
||||
case _SWAP: {
|
||||
JitOptSymbol *top_in;
|
||||
JitOptSymbol *bottom_in;
|
||||
JitOptSymbol *top_out;
|
||||
JitOptSymbol *bottom_out;
|
||||
top_in = stack_pointer[-1];
|
||||
bottom_in = stack_pointer[-2 - (oparg-2)];
|
||||
bottom_out = bottom_in;
|
||||
top_out = top_in;
|
||||
stack_pointer[-2 - (oparg-2)] = top_out;
|
||||
stack_pointer[-1] = bottom_out;
|
||||
JitOptSymbol **top;
|
||||
JitOptSymbol **bottom;
|
||||
top = &stack_pointer[-1];
|
||||
bottom = &stack_pointer[-2 - (oparg-2)];
|
||||
JitOptSymbol *temp = bottom[0];
|
||||
bottom[0] = top[0];
|
||||
top[0] = temp;
|
||||
assert(oparg >= 2);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2639,8 +2623,6 @@
|
|||
}
|
||||
|
||||
case _ERROR_POP_N: {
|
||||
stack_pointer += -oparg;
|
||||
assert(WITHIN_STACK_BOUNDS());
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,9 +5,16 @@ import parser
|
|||
import re
|
||||
from typing import Optional
|
||||
|
||||
@dataclass
|
||||
class EscapingCall:
|
||||
start: lexer.Token
|
||||
call: lexer.Token
|
||||
end: lexer.Token
|
||||
kills: lexer.Token | None
|
||||
|
||||
@dataclass
|
||||
class Properties:
|
||||
escaping_calls: dict[lexer.Token, tuple[lexer.Token, lexer.Token]]
|
||||
escaping_calls: dict[lexer.Token, EscapingCall]
|
||||
escapes: bool
|
||||
error_with_pop: bool
|
||||
error_without_pop: bool
|
||||
|
@ -41,7 +48,7 @@ class Properties:
|
|||
|
||||
@staticmethod
|
||||
def from_list(properties: list["Properties"]) -> "Properties":
|
||||
escaping_calls: dict[lexer.Token, tuple[lexer.Token, lexer.Token]] = {}
|
||||
escaping_calls: dict[lexer.Token, EscapingCall] = {}
|
||||
for p in properties:
|
||||
escaping_calls.update(p.escaping_calls)
|
||||
return Properties(
|
||||
|
@ -330,6 +337,17 @@ def convert_stack_item(
|
|||
cond = replace_op_arg_1
|
||||
return StackItem(item.name, item.type, cond, item.size)
|
||||
|
||||
def check_unused(stack: list[StackItem], input_names: dict[str, lexer.Token]) -> None:
|
||||
"Unused items cannot be on the stack above used, non-peek items"
|
||||
seen_unused = False
|
||||
for item in reversed(stack):
|
||||
if item.name == "unused":
|
||||
seen_unused = True
|
||||
elif item.peek:
|
||||
break
|
||||
elif seen_unused:
|
||||
raise analysis_error(f"Cannot have used input '{item.name}' below an unused value on the stack", input_names[item.name])
|
||||
|
||||
|
||||
def analyze_stack(
|
||||
op: parser.InstDef | parser.Pseudo, replace_op_arg_1: str | None = None
|
||||
|
@ -374,6 +392,7 @@ def analyze_stack(
|
|||
for output in outputs:
|
||||
if variable_used(op, output.name):
|
||||
output.used = True
|
||||
check_unused(inputs, input_names)
|
||||
return StackEffect(inputs, outputs)
|
||||
|
||||
|
||||
|
@ -548,7 +567,6 @@ NON_ESCAPING_FUNCTIONS = (
|
|||
"PyStackRef_AsPyObjectNew",
|
||||
"PyStackRef_AsPyObjectSteal",
|
||||
"PyStackRef_CLEAR",
|
||||
"PyStackRef_CLOSE",
|
||||
"PyStackRef_CLOSE_SPECIALIZED",
|
||||
"PyStackRef_DUP",
|
||||
"PyStackRef_False",
|
||||
|
@ -665,8 +683,8 @@ def find_stmt_end(node: parser.InstDef, idx: int) -> lexer.Token:
|
|||
if tkn.kind == "SEMI":
|
||||
return node.block.tokens[idx+1]
|
||||
|
||||
def check_escaping_calls(instr: parser.InstDef, escapes: dict[lexer.Token, tuple[lexer.Token, lexer.Token]]) -> None:
|
||||
calls = {escapes[t][0] for t in escapes}
|
||||
def check_escaping_calls(instr: parser.InstDef, escapes: dict[lexer.Token, EscapingCall]) -> None:
|
||||
calls = {e.call for e in escapes.values()}
|
||||
in_if = 0
|
||||
tkn_iter = iter(instr.block.tokens)
|
||||
for tkn in tkn_iter:
|
||||
|
@ -684,8 +702,8 @@ def check_escaping_calls(instr: parser.InstDef, escapes: dict[lexer.Token, tuple
|
|||
elif tkn in calls and in_if:
|
||||
raise analysis_error(f"Escaping call '{tkn.text} in condition", tkn)
|
||||
|
||||
def find_escaping_api_calls(instr: parser.InstDef) -> dict[lexer.Token, tuple[lexer.Token, lexer.Token]]:
|
||||
result: dict[lexer.Token, tuple[lexer.Token, lexer.Token]] = {}
|
||||
def find_escaping_api_calls(instr: parser.InstDef) -> dict[lexer.Token, EscapingCall]:
|
||||
result: dict[lexer.Token, EscapingCall] = {}
|
||||
tokens = instr.block.tokens
|
||||
for idx, tkn in enumerate(tokens):
|
||||
try:
|
||||
|
@ -720,9 +738,17 @@ def find_escaping_api_calls(instr: parser.InstDef) -> dict[lexer.Token, tuple[le
|
|||
continue
|
||||
elif tkn.kind != "RBRACKET":
|
||||
continue
|
||||
if tkn.text in ("PyStackRef_CLOSE", "PyStackRef_XCLOSE"):
|
||||
if len(tokens) <= idx+2:
|
||||
raise analysis_error("Unexpected end of file", next_tkn)
|
||||
kills = tokens[idx+2]
|
||||
if kills.kind != "IDENTIFIER":
|
||||
raise analysis_error(f"Expected identifier, got '{kills.text}'", kills)
|
||||
else:
|
||||
kills = None
|
||||
start = find_stmt_start(instr, idx)
|
||||
end = find_stmt_end(instr, idx)
|
||||
result[start] = tkn, end
|
||||
result[start] = EscapingCall(start, tkn, end, kills)
|
||||
check_escaping_calls(instr, result)
|
||||
return result
|
||||
|
||||
|
@ -821,9 +847,6 @@ def compute_properties(op: parser.InstDef) -> Properties:
|
|||
variable_used(op, "Py_DECREF") or
|
||||
variable_used(op, "Py_XDECREF") or
|
||||
variable_used(op, "Py_CLEAR") or
|
||||
variable_used(op, "PyStackRef_CLOSE") or
|
||||
variable_used(op, "PyStackRef_XCLOSE") or
|
||||
variable_used(op, "PyStackRef_CLEAR") or
|
||||
variable_used(op, "SETLOCAL")
|
||||
)
|
||||
return Properties(
|
||||
|
|
|
@ -120,8 +120,6 @@ class Emitter:
|
|||
"SYNC_SP": self.sync_sp,
|
||||
"SAVE_STACK": self.save_stack,
|
||||
"RELOAD_STACK": self.reload_stack,
|
||||
"PyStackRef_CLOSE": self.stackref_close,
|
||||
"PyStackRef_XCLOSE": self.stackref_close,
|
||||
"PyStackRef_CLOSE_SPECIALIZED": self.stackref_close_specialized,
|
||||
"PyStackRef_AsPyObjectSteal": self.stackref_steal,
|
||||
"DISPATCH": self.dispatch,
|
||||
|
@ -166,6 +164,13 @@ class Emitter:
|
|||
|
||||
exit_if = deopt_if
|
||||
|
||||
def goto_error(self, offset: int, label: str, storage: Storage) -> str:
|
||||
if offset > 0:
|
||||
return f"goto pop_{offset}_{label};"
|
||||
if offset < 0:
|
||||
storage.copy().flush(self.out)
|
||||
return f"goto {label};"
|
||||
|
||||
def error_if(
|
||||
self,
|
||||
tkn: Token,
|
||||
|
@ -188,30 +193,20 @@ class Emitter:
|
|||
self.out.emit_at("if ", tkn)
|
||||
self.emit(lparen)
|
||||
emit_to(self.out, tkn_iter, "COMMA")
|
||||
self.out.emit(") ")
|
||||
self.out.emit(") {\n")
|
||||
label = next(tkn_iter).text
|
||||
next(tkn_iter) # RPAREN
|
||||
next(tkn_iter) # Semi colon
|
||||
storage.clear_inputs("at ERROR_IF")
|
||||
|
||||
c_offset = storage.stack.peek_offset()
|
||||
try:
|
||||
offset = -int(c_offset)
|
||||
except ValueError:
|
||||
offset = -1
|
||||
if offset > 0:
|
||||
self.out.emit(f"goto pop_{offset}_")
|
||||
self.out.emit(label)
|
||||
self.out.emit(";\n")
|
||||
elif offset == 0:
|
||||
self.out.emit("goto ")
|
||||
self.out.emit(label)
|
||||
self.out.emit(";\n")
|
||||
else:
|
||||
self.out.emit("{\n")
|
||||
storage.copy().flush(self.out)
|
||||
self.out.emit("goto ")
|
||||
self.out.emit(label)
|
||||
self.out.emit(";\n")
|
||||
self.out.emit(self.goto_error(offset, label, storage))
|
||||
self.out.emit("\n")
|
||||
if not unconditional:
|
||||
self.out.emit("}\n")
|
||||
return not unconditional
|
||||
|
||||
|
@ -226,7 +221,7 @@ class Emitter:
|
|||
next(tkn_iter) # LPAREN
|
||||
next(tkn_iter) # RPAREN
|
||||
next(tkn_iter) # Semi colon
|
||||
self.out.emit_at("goto error;", tkn)
|
||||
self.out.emit_at(self.goto_error(0, "error", storage), tkn)
|
||||
return False
|
||||
|
||||
def decref_inputs(
|
||||
|
@ -320,26 +315,6 @@ class Emitter:
|
|||
live = var.name
|
||||
return True
|
||||
|
||||
def stackref_close(
|
||||
self,
|
||||
tkn: Token,
|
||||
tkn_iter: TokenIterator,
|
||||
uop: Uop,
|
||||
storage: Storage,
|
||||
inst: Instruction | None,
|
||||
) -> bool:
|
||||
self.out.emit(tkn)
|
||||
tkn = next(tkn_iter)
|
||||
assert tkn.kind == "LPAREN"
|
||||
self.out.emit(tkn)
|
||||
name = next(tkn_iter)
|
||||
self.out.emit(name)
|
||||
if name.kind == "IDENTIFIER":
|
||||
return self.stackref_kill(name, storage, True)
|
||||
rparen = emit_to(self.out, tkn_iter, "RPAREN")
|
||||
self.emit(rparen)
|
||||
return True
|
||||
|
||||
def stackref_close_specialized(
|
||||
self,
|
||||
tkn: Token,
|
||||
|
@ -590,9 +565,15 @@ class Emitter:
|
|||
self.out.start_line()
|
||||
line = tkn.line
|
||||
if tkn in escaping_calls:
|
||||
if tkn != reload:
|
||||
escape = escaping_calls[tkn]
|
||||
if escape.kills is not None:
|
||||
if tkn == reload:
|
||||
self.emit_reload(storage)
|
||||
self.stackref_kill(escape.kills, storage, True)
|
||||
self.emit_save(storage)
|
||||
_, reload = escaping_calls[tkn]
|
||||
elif tkn != reload:
|
||||
self.emit_save(storage)
|
||||
reload = escape.end
|
||||
elif tkn == reload:
|
||||
self.emit_reload(storage)
|
||||
if tkn.kind == "LBRACE":
|
||||
|
@ -634,7 +615,6 @@ class Emitter:
|
|||
raise analysis_error(ex.args[0], tkn) from None
|
||||
raise analysis_error("Expecting closing brace. Reached end of file", tkn)
|
||||
|
||||
|
||||
def emit_tokens(
|
||||
self,
|
||||
uop: Uop,
|
||||
|
|
|
@ -69,41 +69,11 @@ class Tier2Emitter(Emitter):
|
|||
super().__init__(out)
|
||||
self._replacers["oparg"] = self.oparg
|
||||
|
||||
def error_if(
|
||||
self,
|
||||
tkn: Token,
|
||||
tkn_iter: TokenIterator,
|
||||
uop: Uop,
|
||||
storage: Storage,
|
||||
inst: Instruction | None,
|
||||
) -> bool:
|
||||
self.out.emit_at("if ", tkn)
|
||||
lparen = next(tkn_iter)
|
||||
self.emit(lparen)
|
||||
assert lparen.kind == "LPAREN"
|
||||
first_tkn = next(tkn_iter)
|
||||
self.out.emit(first_tkn)
|
||||
emit_to(self.out, tkn_iter, "COMMA")
|
||||
label = next(tkn_iter).text
|
||||
next(tkn_iter) # RPAREN
|
||||
next(tkn_iter) # Semi colon
|
||||
self.emit(") JUMP_TO_ERROR();\n")
|
||||
return not always_true(first_tkn)
|
||||
|
||||
|
||||
def error_no_pop(
|
||||
self,
|
||||
tkn: Token,
|
||||
tkn_iter: TokenIterator,
|
||||
uop: Uop,
|
||||
storage: Storage,
|
||||
inst: Instruction | None,
|
||||
) -> bool:
|
||||
next(tkn_iter) # LPAREN
|
||||
next(tkn_iter) # RPAREN
|
||||
next(tkn_iter) # Semi colon
|
||||
self.out.emit_at("JUMP_TO_ERROR();", tkn)
|
||||
return False
|
||||
def goto_error(self, offset: int, label: str, storage: Storage) -> str:
|
||||
# To do: Add jump targets for popping values.
|
||||
if offset != 0:
|
||||
storage.copy().flush(self.out)
|
||||
return f"JUMP_TO_ERROR();"
|
||||
|
||||
def deopt_if(
|
||||
self,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue