GH-128682: Make PyStackRef_CLOSE escaping. (GH-129404)

This commit is contained in:
Mark Shannon 2025-02-03 12:41:32 +00:00 committed by GitHub
parent 218f205f20
commit 808071b994
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 1348 additions and 657 deletions

View file

@ -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 },

View file

@ -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:

View file

@ -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());

View file

@ -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) {

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -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++;
}

View file

@ -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])) {

View file

@ -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;
}

View file

@ -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(

View file

@ -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,

View file

@ -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,