[END_SEND] = { .nuops = 1, .uops = { { END_SEND, 0, 0 } } },
[UNARY_NEGATIVE] = { .nuops = 1, .uops = { { UNARY_NEGATIVE, 0, 0 } } },
[UNARY_NOT] = { .nuops = 1, .uops = { { UNARY_NOT, 0, 0 } } },
+ [TO_BOOL] = { .nuops = 1, .uops = { { _TO_BOOL, 0, 0 } } },
[TO_BOOL_BOOL] = { .nuops = 1, .uops = { { TO_BOOL_BOOL, 0, 0 } } },
[TO_BOOL_INT] = { .nuops = 1, .uops = { { TO_BOOL_INT, 0, 0 } } },
[TO_BOOL_LIST] = { .nuops = 1, .uops = { { TO_BOOL_LIST, 0, 0 } } },
[BINARY_OP_ADD_FLOAT] = { .nuops = 2, .uops = { { _GUARD_BOTH_FLOAT, 0, 0 }, { _BINARY_OP_ADD_FLOAT, 0, 0 } } },
[BINARY_OP_SUBTRACT_FLOAT] = { .nuops = 2, .uops = { { _GUARD_BOTH_FLOAT, 0, 0 }, { _BINARY_OP_SUBTRACT_FLOAT, 0, 0 } } },
[BINARY_OP_ADD_UNICODE] = { .nuops = 2, .uops = { { _GUARD_BOTH_UNICODE, 0, 0 }, { _BINARY_OP_ADD_UNICODE, 0, 0 } } },
+ [BINARY_SUBSCR] = { .nuops = 1, .uops = { { _BINARY_SUBSCR, 0, 0 } } },
[BINARY_SLICE] = { .nuops = 1, .uops = { { BINARY_SLICE, 0, 0 } } },
[STORE_SLICE] = { .nuops = 1, .uops = { { STORE_SLICE, 0, 0 } } },
[BINARY_SUBSCR_LIST_INT] = { .nuops = 1, .uops = { { BINARY_SUBSCR_LIST_INT, 0, 0 } } },
[BINARY_SUBSCR_DICT] = { .nuops = 1, .uops = { { BINARY_SUBSCR_DICT, 0, 0 } } },
[LIST_APPEND] = { .nuops = 1, .uops = { { LIST_APPEND, 0, 0 } } },
[SET_ADD] = { .nuops = 1, .uops = { { SET_ADD, 0, 0 } } },
+ [STORE_SUBSCR] = { .nuops = 1, .uops = { { _STORE_SUBSCR, 0, 0 } } },
[STORE_SUBSCR_LIST_INT] = { .nuops = 1, .uops = { { STORE_SUBSCR_LIST_INT, 0, 0 } } },
[STORE_SUBSCR_DICT] = { .nuops = 1, .uops = { { STORE_SUBSCR_DICT, 0, 0 } } },
[DELETE_SUBSCR] = { .nuops = 1, .uops = { { DELETE_SUBSCR, 0, 0 } } },
[LOAD_BUILD_CLASS] = { .nuops = 1, .uops = { { LOAD_BUILD_CLASS, 0, 0 } } },
[STORE_NAME] = { .nuops = 1, .uops = { { STORE_NAME, 0, 0 } } },
[DELETE_NAME] = { .nuops = 1, .uops = { { DELETE_NAME, 0, 0 } } },
- [UNPACK_SEQUENCE] = { .nuops = 2, .uops = { { _SPECIALIZE_UNPACK_SEQUENCE, 1, 0 }, { _UNPACK_SEQUENCE, 0, 0 } } },
+ [UNPACK_SEQUENCE] = { .nuops = 1, .uops = { { _UNPACK_SEQUENCE, 0, 0 } } },
[UNPACK_SEQUENCE_TWO_TUPLE] = { .nuops = 1, .uops = { { UNPACK_SEQUENCE_TWO_TUPLE, 0, 0 } } },
[UNPACK_SEQUENCE_TUPLE] = { .nuops = 1, .uops = { { UNPACK_SEQUENCE_TUPLE, 0, 0 } } },
[UNPACK_SEQUENCE_LIST] = { .nuops = 1, .uops = { { UNPACK_SEQUENCE_LIST, 0, 0 } } },
[UNPACK_EX] = { .nuops = 1, .uops = { { UNPACK_EX, 0, 0 } } },
+ [STORE_ATTR] = { .nuops = 1, .uops = { { _STORE_ATTR, 0, 0 } } },
[DELETE_ATTR] = { .nuops = 1, .uops = { { DELETE_ATTR, 0, 0 } } },
[STORE_GLOBAL] = { .nuops = 1, .uops = { { STORE_GLOBAL, 0, 0 } } },
[DELETE_GLOBAL] = { .nuops = 1, .uops = { { DELETE_GLOBAL, 0, 0 } } },
[LOAD_LOCALS] = { .nuops = 1, .uops = { { LOAD_LOCALS, 0, 0 } } },
[LOAD_FROM_DICT_OR_GLOBALS] = { .nuops = 1, .uops = { { LOAD_FROM_DICT_OR_GLOBALS, 0, 0 } } },
[LOAD_NAME] = { .nuops = 1, .uops = { { LOAD_NAME, 0, 0 } } },
+ [LOAD_GLOBAL] = { .nuops = 1, .uops = { { _LOAD_GLOBAL, 0, 0 } } },
[LOAD_GLOBAL_MODULE] = { .nuops = 2, .uops = { { _GUARD_GLOBALS_VERSION, 1, 1 }, { _LOAD_GLOBAL_MODULE, 1, 3 } } },
[LOAD_GLOBAL_BUILTIN] = { .nuops = 3, .uops = { { _GUARD_GLOBALS_VERSION, 1, 1 }, { _GUARD_BUILTINS_VERSION, 1, 2 }, { _LOAD_GLOBAL_BUILTINS, 1, 3 } } },
[DELETE_FAST] = { .nuops = 1, .uops = { { DELETE_FAST, 0, 0 } } },
[MAP_ADD] = { .nuops = 1, .uops = { { MAP_ADD, 0, 0 } } },
[LOAD_SUPER_ATTR_ATTR] = { .nuops = 1, .uops = { { LOAD_SUPER_ATTR_ATTR, 0, 0 } } },
[LOAD_SUPER_ATTR_METHOD] = { .nuops = 1, .uops = { { LOAD_SUPER_ATTR_METHOD, 0, 0 } } },
+ [LOAD_ATTR] = { .nuops = 1, .uops = { { _LOAD_ATTR, 0, 0 } } },
[LOAD_ATTR_INSTANCE_VALUE] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_MANAGED_OBJECT_HAS_VALUES, 0, 0 }, { _LOAD_ATTR_INSTANCE_VALUE, 1, 3 } } },
[LOAD_ATTR_MODULE] = { .nuops = 2, .uops = { { _CHECK_ATTR_MODULE, 2, 1 }, { _LOAD_ATTR_MODULE, 1, 3 } } },
[LOAD_ATTR_WITH_HINT] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_ATTR_WITH_HINT, 0, 0 }, { _LOAD_ATTR_WITH_HINT, 1, 3 } } },
[LOAD_ATTR_CLASS] = { .nuops = 2, .uops = { { _CHECK_ATTR_CLASS, 2, 1 }, { _LOAD_ATTR_CLASS, 4, 5 } } },
[STORE_ATTR_INSTANCE_VALUE] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_VALUES, 0, 0 }, { _STORE_ATTR_INSTANCE_VALUE, 1, 3 } } },
[STORE_ATTR_SLOT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _STORE_ATTR_SLOT, 1, 3 } } },
+ [COMPARE_OP] = { .nuops = 1, .uops = { { _COMPARE_OP, 0, 0 } } },
[COMPARE_OP_FLOAT] = { .nuops = 1, .uops = { { COMPARE_OP_FLOAT, 0, 0 } } },
[COMPARE_OP_INT] = { .nuops = 1, .uops = { { COMPARE_OP_INT, 0, 0 } } },
[COMPARE_OP_STR] = { .nuops = 1, .uops = { { COMPARE_OP_STR, 0, 0 } } },
[FORMAT_SIMPLE] = { .nuops = 1, .uops = { { FORMAT_SIMPLE, 0, 0 } } },
[FORMAT_WITH_SPEC] = { .nuops = 1, .uops = { { FORMAT_WITH_SPEC, 0, 0 } } },
[COPY] = { .nuops = 1, .uops = { { COPY, 0, 0 } } },
+ [BINARY_OP] = { .nuops = 1, .uops = { { _BINARY_OP, 0, 0 } } },
[SWAP] = { .nuops = 1, .uops = { { SWAP, 0, 0 } } },
};
#endif // NEED_OPCODE_METADATA
"""
self.run_cases_test(input, output)
+ def test_annotated_inst(self):
+ input = """
+ guard inst(OP, (--)) {
+ ham();
+ }
+ """
+ output = """
+ TARGET(OP) {
+ frame->instr_ptr = next_instr;
+ next_instr += 1;
+ INSTRUCTION_STATS(OP);
+ ham();
+ DISPATCH();
+ }
+ """
+ self.run_cases_test(input, output)
+
+ def test_annotated_op(self):
+ input = """
+ guard op(OP, (--)) {
+ spam();
+ }
+ macro(M) = OP;
+ """
+ output = """
+ TARGET(M) {
+ frame->instr_ptr = next_instr;
+ next_instr += 1;
+ INSTRUCTION_STATS(M);
+ spam();
+ DISPATCH();
+ }
+ """
+ self.run_cases_test(input, output)
+
+ input = """
+ guard register specializing op(OP, (--)) {
+ spam();
+ }
+ macro(M) = OP;
+ """
+ self.run_cases_test(input, output)
+
if __name__ == "__main__":
unittest.main()
break;
}
- case _SPECIALIZE_UNPACK_SEQUENCE: {
- break;
- }
-
case _UNPACK_SEQUENCE: {
STACK_SHRINK(1);
STACK_GROW(oparg);
#define family(name, ...) static int family_##name
#define pseudo(name) static int pseudo_##name
+/* Annotations */
+#define guard
+#define override
+#define specializing
+
// Dummy variables for stack effects.
static PyObject *value, *value1, *value2, *left, *right, *res, *sum, *prod, *sub;
static PyObject *container, *start, *stop, *v, *lhs, *rhs, *res2;
TO_BOOL_STR,
};
- op(_SPECIALIZE_TO_BOOL, (counter/1, value -- value)) {
+ specializing op(_SPECIALIZE_TO_BOOL, (counter/1, value -- value)) {
TIER_ONE_ONLY
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
BINARY_SUBSCR_TUPLE_INT,
};
- op(_SPECIALIZE_BINARY_SUBSCR, (counter/1, container, sub -- container, sub)) {
+ specializing op(_SPECIALIZE_BINARY_SUBSCR, (counter/1, container, sub -- container, sub)) {
TIER_ONE_ONLY
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
STORE_SUBSCR_LIST_INT,
};
- op(_SPECIALIZE_STORE_SUBSCR, (counter/1, container, sub -- container, sub)) {
+ specializing op(_SPECIALIZE_STORE_SUBSCR, (counter/1, container, sub -- container, sub)) {
TIER_ONE_ONLY
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
SEND_GEN,
};
- op(_SPECIALIZE_SEND, (counter/1, receiver, unused -- receiver, unused)) {
+ specializing op(_SPECIALIZE_SEND, (counter/1, receiver, unused -- receiver, unused)) {
TIER_ONE_ONLY
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
UNPACK_SEQUENCE_LIST,
};
- op(_SPECIALIZE_UNPACK_SEQUENCE, (counter/1, seq -- seq)) {
+ specializing op(_SPECIALIZE_UNPACK_SEQUENCE, (counter/1, seq -- seq)) {
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
next_instr = this_instr;
STORE_ATTR_WITH_HINT,
};
- op(_SPECIALIZE_STORE_ATTR, (counter/1, owner -- owner)) {
+ specializing op(_SPECIALIZE_STORE_ATTR, (counter/1, owner -- owner)) {
TIER_ONE_ONLY
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
LOAD_GLOBAL_BUILTIN,
};
- op(_SPECIALIZE_LOAD_GLOBAL, (counter/1 -- )) {
+ specializing op(_SPECIALIZE_LOAD_GLOBAL, (counter/1 -- )) {
TIER_ONE_ONLY
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
LOAD_SUPER_ATTR_METHOD,
};
- op(_SPECIALIZE_LOAD_SUPER_ATTR, (counter/1, global_super, class, unused -- global_super, class, unused)) {
+ specializing op(_SPECIALIZE_LOAD_SUPER_ATTR, (counter/1, global_super, class, unused -- global_super, class, unused)) {
TIER_ONE_ONLY
#if ENABLE_SPECIALIZATION
int load_method = oparg & 1;
LOAD_ATTR_NONDESCRIPTOR_NO_DICT,
};
- op(_SPECIALIZE_LOAD_ATTR, (counter/1, owner -- owner)) {
+ specializing op(_SPECIALIZE_LOAD_ATTR, (counter/1, owner -- owner)) {
TIER_ONE_ONLY
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
COMPARE_OP_STR,
};
- op(_SPECIALIZE_COMPARE_OP, (counter/1, left, right -- left, right)) {
+ specializing op(_SPECIALIZE_COMPARE_OP, (counter/1, left, right -- left, right)) {
TIER_ONE_ONLY
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
FOR_ITER_GEN,
};
- op(_SPECIALIZE_FOR_ITER, (counter/1, iter -- iter)) {
+ specializing op(_SPECIALIZE_FOR_ITER, (counter/1, iter -- iter)) {
TIER_ONE_ONLY
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
CALL_ALLOC_AND_ENTER_INIT,
};
- op(_SPECIALIZE_CALL, (counter/1, callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) {
+ specializing op(_SPECIALIZE_CALL, (counter/1, callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) {
TIER_ONE_ONLY
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
top = Py_NewRef(bottom);
}
- op(_SPECIALIZE_BINARY_OP, (counter/1, lhs, rhs -- lhs, rhs)) {
+ specializing op(_SPECIALIZE_BINARY_OP, (counter/1, lhs, rhs -- lhs, rhs)) {
TIER_ONE_ONLY
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
break;
}
- case _SPECIALIZE_UNPACK_SEQUENCE: {
- PyObject *seq;
- seq = stack_pointer[-1];
- uint16_t counter = (uint16_t)next_uop[-1].operand;
- #if ENABLE_SPECIALIZATION
- if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
- next_instr = this_instr;
- _Py_Specialize_UnpackSequence(seq, next_instr, oparg);
- DISPATCH_SAME_OPARG();
- }
- STAT_INC(UNPACK_SEQUENCE, deferred);
- DECREMENT_ADAPTIVE_COUNTER(this_instr[1].cache);
- #endif /* ENABLE_SPECIALIZATION */
- (void)seq;
- (void)counter;
- break;
- }
-
case _UNPACK_SEQUENCE: {
PyObject *seq;
seq = stack_pointer[-1];
match thing:
case parsing.InstDef(name=name):
macro: parsing.Macro | None = None
- if thing.kind == "inst" and not thing.override:
+ if thing.kind == "inst" and "override" not in thing.annotations:
macro = parsing.Macro(name, [parsing.OpName(name)])
if name in self.instrs:
- if not thing.override:
+ if "override" not in thing.annotations:
raise psr.make_syntax_error(
f"Duplicate definition of '{name}' @ {thing.context} "
f"previous definition @ {self.instrs[name].inst.context}",
thing_first_token,
)
self.everything[instrs_idx[name]] = thing
- if name not in self.instrs and thing.override:
+ if name not in self.instrs and "override" in thing.annotations:
raise psr.make_syntax_error(
f"Definition of '{name}' @ {thing.context} is supposed to be "
"an override but no previous definition exists.",
expansions: list[tuple[str, int, int]] = [] # [(name, size, offset), ...]
for part in parts:
if isinstance(part, Component):
- # All component instructions must be viable uops
+ # Skip specializations
+ if "specializing" in part.instr.annotations:
+ continue
+ # All other component instructions must be viable uops
if not part.instr.is_viable_uop():
# This note just reminds us about macros that cannot
# be expanded to Tier 2 uops. It is not an error.
# Parts of the underlying instruction definition
inst: parsing.InstDef
name: str
+ annotations: list[str]
block: parsing.Block
block_text: list[str] # Block.text, less curlies, less PREDICT() calls
block_line: int # First line of block in original code
def __init__(self, inst: parsing.InstDef):
self.inst = inst
self.name = inst.name
+ self.annotations = inst.annotations
self.block = inst.block
self.block_text, self.check_eval_breaker, self.block_line = extract_block_text(
self.block
if self.name == "_EXIT_TRACE":
return True # This has 'return frame' but it's okay
+ if self.name == "_SAVE_RETURN_OFFSET":
+ return True # Adjusts next_instr, but only in tier 1 code
if self.always_exits:
dprint(f"Skipping {self.name} because it always exits: {self.always_exits}")
return False
# Macros
macro = r"# *(ifdef|ifndef|undef|define|error|endif|if|else|include|#)"
-MACRO = "MACRO"
+CMACRO = "CMACRO"
id_re = r"[a-zA-Z_][0-9a-zA-Z_]*"
IDENTIFIER = "IDENTIFIER"
+
suffix = r"([uU]?[lL]?[lL]?)"
octal = r"0[0-7]+" + suffix
hex = r"0[xX][0-9a-fA-F]+"
kwds.append(INT)
LONG = "LONG"
kwds.append(LONG)
-OVERRIDE = "OVERRIDE"
-kwds.append(OVERRIDE)
-REGISTER = "REGISTER"
-kwds.append(REGISTER)
OFFSETOF = "OFFSETOF"
kwds.append(OFFSETOF)
RESTRICT = "RESTRICT"
kwds.append(VOLATILE)
WHILE = "WHILE"
kwds.append(WHILE)
+# An instruction in the DSL
+INST = "INST"
+kwds.append(INST)
+# A micro-op in the DSL
+OP = "OP"
+kwds.append(OP)
+# A macro in the DSL
+MACRO = "MACRO"
+kwds.append(MACRO)
keywords = {name.lower(): name for name in kwds}
+ANNOTATION = "ANNOTATION"
+annotations = {"specializing", "guard", "override", "register"}
+
__all__ = []
__all__.extend(kwds)
text = m.group(0)
if text in keywords:
kind = keywords[text]
+ elif text in annotations:
+ kind = ANNOTATION
elif letter.match(text):
kind = IDENTIFIER
elif text == "...":
elif text[0] == "'":
kind = CHARACTER
elif text[0] == "#":
- kind = MACRO
+ kind = CMACRO
elif text[0] == "/" and text[1] in "/*":
kind = COMMENT
else:
@dataclass
class InstHeader(Node):
- override: bool
- register: bool
+ annotations : list[str]
kind: Literal["inst", "op"]
name: str
inputs: list[InputEffect]
@dataclass
class InstDef(Node):
- override: bool
- register: bool
+ annotations : list[str]
kind: Literal["inst", "op"]
name: str
inputs: list[InputEffect]
class Parser(PLexer):
@contextual
def definition(self) -> InstDef | Macro | Pseudo | Family | None:
- if inst := self.inst_def():
- return inst
if macro := self.macro_def():
return macro
if family := self.family_def():
return family
if pseudo := self.pseudo_def():
return pseudo
+ if inst := self.inst_def():
+ return inst
return None
@contextual
if hdr := self.inst_header():
if block := self.block():
return InstDef(
- hdr.override,
- hdr.register,
+ hdr.annotations,
hdr.kind,
hdr.name,
hdr.inputs,
@contextual
def inst_header(self) -> InstHeader | None:
- # [override] inst(NAME)
- # | [override] [register] inst(NAME, (inputs -- outputs))
- # | [override] [register] op(NAME, (inputs -- outputs))
- # TODO: Make INST a keyword in the lexer.
- override = bool(self.expect(lx.OVERRIDE))
- register = bool(self.expect(lx.REGISTER))
- if (tkn := self.expect(lx.IDENTIFIER)) and tkn.text in ("inst", "op"):
+ # annotation* inst(NAME, (inputs -- outputs))
+ # | annotation* op(NAME, (inputs -- outputs))
+ annotations = []
+ while anno := self.expect(lx.ANNOTATION):
+ annotations.append(anno.text)
+ tkn = self.expect(lx.INST)
+ if not tkn:
+ tkn = self.expect(lx.OP)
+ if tkn:
kind = cast(Literal["inst", "op"], tkn.text)
if self.expect(lx.LPAREN) and (tkn := self.expect(lx.IDENTIFIER)):
name = tkn.text
inp, outp = self.io_effect()
if self.expect(lx.RPAREN):
if (tkn := self.peek()) and tkn.kind == lx.LBRACE:
- return InstHeader(override, register, kind, name, inp, outp)
+ return InstHeader(annotations, kind, name, inp, outp)
return None
def io_effect(self) -> tuple[list[InputEffect], list[OutputEffect]]:
@contextual
def macro_def(self) -> Macro | None:
- if (tkn := self.expect(lx.IDENTIFIER)) and tkn.text == "macro":
+ if tkn := self.expect(lx.MACRO):
if self.expect(lx.LPAREN):
if tkn := self.expect(lx.IDENTIFIER):
if self.expect(lx.RPAREN):