Skip to content

Commit 0c109e0

Browse files
Add type attributes to the optimizer DSL
1 parent 4c20f46 commit 0c109e0

File tree

6 files changed

+80
-39
lines changed

6 files changed

+80
-39
lines changed

Python/optimizer_bytecodes.c

Lines changed: 17 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -104,18 +104,16 @@ dummy_func(void) {
104104
res = sym_new_null(ctx);
105105
}
106106

107-
op(_GUARD_TOS_INT, (tos -- tos)) {
107+
op(_GUARD_TOS_INT, (tos -- type(&PyLong_Type) tos)) {
108108
if (sym_matches_type(tos, &PyLong_Type)) {
109109
REPLACE_OP(this_instr, _NOP, 0, 0);
110110
}
111-
sym_set_type(tos, &PyLong_Type);
112111
}
113112

114-
op(_GUARD_NOS_INT, (nos, unused -- nos, unused)) {
113+
op(_GUARD_NOS_INT, (nos, unused -- type(&PyLong_Type) nos, unused)) {
115114
if (sym_matches_type(nos, &PyLong_Type)) {
116115
REPLACE_OP(this_instr, _NOP, 0, 0);
117116
}
118-
sym_set_type(nos, &PyLong_Type);
119117
}
120118

121119
op(_GUARD_TYPE_VERSION, (type_version/2, owner -- owner)) {
@@ -141,18 +139,16 @@ dummy_func(void) {
141139
}
142140
}
143141

144-
op(_GUARD_TOS_FLOAT, (tos -- tos)) {
142+
op(_GUARD_TOS_FLOAT, (tos -- type(&PyFloat_Type) tos)) {
145143
if (sym_matches_type(tos, &PyFloat_Type)) {
146144
REPLACE_OP(this_instr, _NOP, 0, 0);
147145
}
148-
sym_set_type(tos, &PyFloat_Type);
149146
}
150147

151-
op(_GUARD_NOS_FLOAT, (nos, unused -- nos, unused)) {
148+
op(_GUARD_NOS_FLOAT, (nos, unused -- type(&PyFloat_Type) nos, unused)) {
152149
if (sym_matches_type(nos, &PyFloat_Type)) {
153150
REPLACE_OP(this_instr, _NOP, 0, 0);
154151
}
155-
sym_set_type(nos, &PyFloat_Type);
156152
}
157153

158154
op(_BINARY_OP, (left, right -- res)) {
@@ -408,18 +404,16 @@ dummy_func(void) {
408404
}
409405
}
410406

411-
op(_GUARD_NOS_UNICODE, (nos, unused -- nos, unused)) {
407+
op(_GUARD_NOS_UNICODE, (nos, unused -- type(&PyUnicode_Type) nos, unused)) {
412408
if (sym_matches_type(nos, &PyUnicode_Type)) {
413409
REPLACE_OP(this_instr, _NOP, 0, 0);
414410
}
415-
sym_set_type(nos, &PyUnicode_Type);
416411
}
417412

418-
op(_GUARD_TOS_UNICODE, (value -- value)) {
413+
op(_GUARD_TOS_UNICODE, (value -- type(&PyUnicode_Type) value)) {
419414
if (sym_matches_type(value, &PyUnicode_Type)) {
420415
REPLACE_OP(this_instr, _NOP, 0, 0);
421416
}
422-
sym_set_type(value, &PyUnicode_Type);
423417
}
424418

425419
op(_TO_BOOL_STR, (value -- res)) {
@@ -429,8 +423,7 @@ dummy_func(void) {
429423
}
430424
}
431425

432-
op(_UNARY_NOT, (value -- res)) {
433-
sym_set_type(value, &PyBool_Type);
426+
op(_UNARY_NOT, (type(&PyBool_Type) value -- res)) {
434427
res = sym_new_truthiness(ctx, value, false);
435428
}
436429

@@ -631,13 +624,12 @@ dummy_func(void) {
631624
self_or_null = sym_new_not_null(ctx);
632625
}
633626

634-
op(_CHECK_FUNCTION_VERSION, (func_version/2, callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) {
627+
op(_CHECK_FUNCTION_VERSION, (func_version/2, callable, self_or_null, unused[oparg] -- type(&PyFunction_Type) callable, self_or_null, unused[oparg])) {
635628
if (sym_is_const(ctx, callable) && sym_matches_type(callable, &PyFunction_Type)) {
636629
assert(PyFunction_Check(sym_get_const(ctx, callable)));
637630
REPLACE_OP(this_instr, _CHECK_FUNCTION_VERSION_INLINE, 0, func_version);
638631
this_instr->operand1 = (uintptr_t)sym_get_const(ctx, callable);
639632
}
640-
sym_set_type(callable, &PyFunction_Type);
641633
}
642634

643635
op(_CHECK_FUNCTION_EXACT_ARGS, (callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) {
@@ -653,9 +645,9 @@ dummy_func(void) {
653645
}
654646
}
655647

656-
op(_CHECK_CALL_BOUND_METHOD_EXACT_ARGS, (callable, null, unused[oparg] -- callable, null, unused[oparg])) {
648+
op(_CHECK_CALL_BOUND_METHOD_EXACT_ARGS, (callable, null, unused[oparg] -- type(&PyMethod_Type) callable, null, unused[oparg])) {
657649
sym_set_null(null);
658-
sym_set_type(callable, &PyMethod_Type);
650+
(void)callable;
659651
}
660652

661653
op(_INIT_CALL_PY_EXACT_ARGS, (callable, self_or_null, args[oparg] -- new_frame: _Py_UOpsAbstractFrame *)) {
@@ -977,46 +969,40 @@ dummy_func(void) {
977969
}
978970
}
979971

980-
op(_GUARD_TOS_LIST, (tos -- tos)) {
972+
op(_GUARD_TOS_LIST, (tos -- type(&PyList_Type) tos)) {
981973
if (sym_matches_type(tos, &PyList_Type)) {
982974
REPLACE_OP(this_instr, _NOP, 0, 0);
983975
}
984-
sym_set_type(tos, &PyList_Type);
985976
}
986977

987-
op(_GUARD_NOS_LIST, (nos, unused -- nos, unused)) {
978+
op(_GUARD_NOS_LIST, (nos, unused -- type(&PyList_Type) nos, unused)) {
988979
if (sym_matches_type(nos, &PyList_Type)) {
989980
REPLACE_OP(this_instr, _NOP, 0, 0);
990981
}
991-
sym_set_type(nos, &PyList_Type);
992982
}
993983

994-
op(_GUARD_TOS_TUPLE, (tos -- tos)) {
984+
op(_GUARD_TOS_TUPLE, (tos -- type(&PyTuple_Type) tos)) {
995985
if (sym_matches_type(tos, &PyTuple_Type)) {
996986
REPLACE_OP(this_instr, _NOP, 0, 0);
997987
}
998-
sym_set_type(tos, &PyTuple_Type);
999988
}
1000989

1001-
op(_GUARD_NOS_TUPLE, (nos, unused -- nos, unused)) {
990+
op(_GUARD_NOS_TUPLE, (nos, unused -- type(&PyTuple_Type) nos, unused)) {
1002991
if (sym_matches_type(nos, &PyTuple_Type)) {
1003992
REPLACE_OP(this_instr, _NOP, 0, 0);
1004993
}
1005-
sym_set_type(nos, &PyTuple_Type);
1006994
}
1007995

1008-
op(_GUARD_TOS_DICT, (tos -- tos)) {
996+
op(_GUARD_TOS_DICT, (tos -- type(&PyDict_Type) tos)) {
1009997
if (sym_matches_type(tos, &PyDict_Type)) {
1010998
REPLACE_OP(this_instr, _NOP, 0, 0);
1011999
}
1012-
sym_set_type(tos, &PyDict_Type);
10131000
}
10141001

1015-
op(_GUARD_NOS_DICT, (nos, unused -- nos, unused)) {
1002+
op(_GUARD_NOS_DICT, (nos, unused -- type(&PyDict_Type) nos, unused)) {
10161003
if (sym_matches_type(nos, &PyDict_Type)) {
10171004
REPLACE_OP(this_instr, _NOP, 0, 0);
10181005
}
1019-
sym_set_type(nos, &PyDict_Type);
10201006
}
10211007

10221008
op(_GUARD_TOS_ANY_SET, (tos -- tos)) {
@@ -1055,7 +1041,7 @@ dummy_func(void) {
10551041
sym_set_const(callable, (PyObject *)&PyUnicode_Type);
10561042
}
10571043

1058-
op(_CALL_LEN, (callable[1], self_or_null[1], args[oparg] -- res)) {
1044+
op(_CALL_LEN, (callable[1], self_or_null[1], args[oparg] -- type(res)) {
10591045
res = sym_new_type(ctx, &PyLong_Type);
10601046
}
10611047

Python/optimizer_cases.c.h

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Tools/cases_generator/analyzer.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import re
66
from typing import Optional, Callable
77

8-
from parser import Stmt, SimpleStmt, BlockStmt, IfStmt, WhileStmt
8+
from parser import Stmt, SimpleStmt, BlockStmt, IfStmt, WhileStmt, StackAttribute
99

1010
@dataclass
1111
class EscapingCall:
@@ -137,13 +137,14 @@ class StackItem:
137137
name: str
138138
type: str | None
139139
size: str
140+
attributes: list[StackAttribute]
140141
peek: bool = False
141142
used: bool = False
142143

143144
def __str__(self) -> str:
144145
size = f"[{self.size}]" if self.size else ""
145146
type = "" if self.type is None else f"{self.type} "
146-
return f"{type}{self.name}{size} {self.peek}"
147+
return f"{self.attributes} {type}{self.name}{size} {self.peek}"
147148

148149
def is_array(self) -> bool:
149150
return self.size != ""
@@ -345,7 +346,7 @@ def override_error(
345346
def convert_stack_item(
346347
item: parser.StackEffect, replace_op_arg_1: str | None
347348
) -> StackItem:
348-
return StackItem(item.name, item.type, item.size)
349+
return StackItem(item.name, item.type, item.size, item.attributes)
349350

350351
def check_unused(stack: list[StackItem], input_names: dict[str, lexer.Token]) -> None:
351352
"Unused items cannot be on the stack above used, non-peek items"

Tools/cases_generator/optimizer_generator.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from typing import TextIO
2525
from lexer import Token
2626
from stack import Local, Stack, StackError, Storage
27+
from parser import TYPE
2728

2829
DEFAULT_OUTPUT = ROOT / "Python/optimizer_cases.c.h"
2930
DEFAULT_ABSTRACT_INPUT = (ROOT / "Python/optimizer_bytecodes.c").absolute().as_posix()
@@ -111,6 +112,19 @@ def goto_label(self, goto: Token, label: Token, storage: Storage) -> None:
111112
self.out.emit(goto)
112113
self.out.emit(label)
113114

115+
def get_type(item: StackItem) -> str | None:
116+
for attribute in item.attributes:
117+
if attribute.ident == TYPE:
118+
return attribute.expr
119+
return None
120+
121+
def emit_sym_set_type_for_stack_effect(emitter: Emitter, items: list[StackItem]) -> None:
122+
for var in items:
123+
typ = get_type(var)
124+
if typ is not None:
125+
emitter.emit(f"sym_set_type({var.name}, {typ});\n")
126+
127+
114128
def write_uop(
115129
override: Uop | None,
116130
uop: Uop,
@@ -146,6 +160,10 @@ def write_uop(
146160
for var in storage.inputs: # type: ignore[possibly-undefined]
147161
var.in_local = False
148162
_, storage = emitter.emit_tokens(override, storage, None, False)
163+
# Emit type effects.
164+
out.start_line()
165+
emit_sym_set_type_for_stack_effect(emitter, override.stack.inputs)
166+
emit_sym_set_type_for_stack_effect(emitter, override.stack.outputs)
149167
out.start_line()
150168
storage.flush(out)
151169
else:

Tools/cases_generator/parser.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
WhileStmt,
1919
BlockStmt,
2020
MacroIfStmt,
21+
StackAttribute,
22+
TYPE,
2123
)
2224

2325
import pprint

Tools/cases_generator/parsing.py

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,15 +244,24 @@ def accept(self, visitor: Visitor) -> None:
244244

245245
__hash__ = object.__hash__
246246

247+
@dataclass
248+
class StackAttribute(Node):
249+
ident: str
250+
expr: str
251+
252+
def __str__(self):
253+
return f"{self.ident}({self.expr})"
254+
247255
@dataclass
248256
class StackEffect(Node):
249257
name: str = field(compare=False) # __eq__ only uses type, cond, size
258+
attributes: list[StackAttribute] = field(compare=False)
250259
type: str = "" # Optional `:type`
251260
size: str = "" # Optional `[size]`
252261
# Note: size cannot be combined with type or cond
253262

254263
def __repr__(self) -> str:
255-
items = [self.name, self.type, self.size]
264+
items = [self.attributes, self.name, self.type, self.size]
256265
while items and items[-1] == "":
257266
del items[-1]
258267
return f"StackEffect({', '.join(repr(item) for item in items)})"
@@ -274,6 +283,14 @@ class OpName(Node):
274283
name: str
275284

276285

286+
TYPE = "type"
287+
# We have to do this at the parsing stage and not
288+
# lexing stage as we want to allow this to be used as
289+
# a normal identifier in C code.
290+
STACK_ATTRIBUTES = {
291+
TYPE,
292+
}
293+
277294
InputEffect = StackEffect | CacheEffect
278295
OutputEffect = StackEffect
279296
UOp = OpName | CacheEffect
@@ -458,10 +475,26 @@ def cache_effect(self) -> CacheEffect | None:
458475
return CacheEffect(tkn.text, size)
459476
return None
460477

478+
def stack_attributes(self) -> list[StackAttribute]:
479+
# IDENTIFIER '(' expression ')'
480+
res = []
481+
while tkn := self.expect(lx.IDENTIFIER):
482+
if self.expect(lx.LPAREN):
483+
if tkn.text not in STACK_ATTRIBUTES:
484+
raise self.make_syntax_error(f"Stack attribute {tkn.text} is not recognized.")
485+
expr = self.expression()
486+
self.require(lx.RPAREN)
487+
res.append(StackAttribute(tkn.text.strip(), expr.text.strip()))
488+
else:
489+
self.backup()
490+
break
491+
return res
492+
461493
@contextual
462494
def stack_effect(self) -> StackEffect | None:
463-
# IDENTIFIER [':' IDENTIFIER [TIMES]] ['if' '(' expression ')']
464-
# | IDENTIFIER '[' expression ']'
495+
# stack_attributes IDENTIFIER [':' IDENTIFIER [TIMES]] ['if' '(' expression ')']
496+
# | stack_attributes IDENTIFIER '[' expression ']'
497+
stack_attributes = self.stack_attributes()
465498
if tkn := self.expect(lx.IDENTIFIER):
466499
type_text = ""
467500
if self.expect(lx.COLON):
@@ -476,7 +509,7 @@ def stack_effect(self) -> StackEffect | None:
476509
raise self.make_syntax_error("Expected expression")
477510
self.require(lx.RBRACKET)
478511
size_text = size.text.strip()
479-
return StackEffect(tkn.text, type_text, size_text)
512+
return StackEffect(tkn.text, stack_attributes, type_text, size_text)
480513
return None
481514

482515
@contextual

0 commit comments

Comments
 (0)