Align _opcode_metadata.py to 3.14.3 (#7456)

* Align `_opcode_metadata.py` to 3.14.3

* Unmark passing test

* Ensure python 3.14 runs on CI

* Update banner

* Fix `test__opcode.py`

* Adjust generate script

* Fix docs
This commit is contained in:
Shahar Naveh authored and GitHub committed 2026-03-18 22:09:00 +09:00
1 parent 31480243f0
commit 9a5de28b79
7 files changed
+447 -213

No files matched your search

+7
View File
@@ -13,6 +13,9 @@ concurrency:
group: format-check-${{ github.event.pull_request.number }}
cancel-in-progress: true
env:
PYTHON_VERSION: "3.14.3"
jobs:
format_check:
permissions:
@@ -45,6 +48,10 @@ jobs:
- name: Run ruff check import sorting
run: ruff check --select I --fix
- uses: actions/setup-python@v6.2.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Run generate_opcode_metadata.py
run: python scripts/generate_opcode_metadata.py
Generated Vendored
+213 -94
View File
@@ -1,16 +1,223 @@
# This file is generated by scripts/generate_opcode_metadata.py
# for RustPython bytecode format (CPython 3.13 compatible opcode numbers).
# for RustPython bytecode format (CPython 3.14 compatible opcode numbers).
# Do not edit!
_specializations = {}
_specializations = {
"RESUME": [
"RESUME_CHECK",
],
"LOAD_CONST": [
"LOAD_CONST_MORTAL",
"LOAD_CONST_IMMORTAL",
],
"TO_BOOL": [
"TO_BOOL_ALWAYS_TRUE",
"TO_BOOL_BOOL",
"TO_BOOL_INT",
"TO_BOOL_LIST",
"TO_BOOL_NONE",
"TO_BOOL_STR",
],
"BINARY_OP": [
"BINARY_OP_MULTIPLY_INT",
"BINARY_OP_ADD_INT",
"BINARY_OP_SUBTRACT_INT",
"BINARY_OP_MULTIPLY_FLOAT",
"BINARY_OP_ADD_FLOAT",
"BINARY_OP_SUBTRACT_FLOAT",
"BINARY_OP_ADD_UNICODE",
"BINARY_OP_SUBSCR_LIST_INT",
"BINARY_OP_SUBSCR_LIST_SLICE",
"BINARY_OP_SUBSCR_TUPLE_INT",
"BINARY_OP_SUBSCR_STR_INT",
"BINARY_OP_SUBSCR_DICT",
"BINARY_OP_SUBSCR_GETITEM",
"BINARY_OP_EXTEND",
"BINARY_OP_INPLACE_ADD_UNICODE",
],
"STORE_SUBSCR": [
"STORE_SUBSCR_DICT",
"STORE_SUBSCR_LIST_INT",
],
"SEND": [
"SEND_GEN",
],
"UNPACK_SEQUENCE": [
"UNPACK_SEQUENCE_TWO_TUPLE",
"UNPACK_SEQUENCE_TUPLE",
"UNPACK_SEQUENCE_LIST",
],
"STORE_ATTR": [
"STORE_ATTR_INSTANCE_VALUE",
"STORE_ATTR_SLOT",
"STORE_ATTR_WITH_HINT",
],
"LOAD_GLOBAL": [
"LOAD_GLOBAL_MODULE",
"LOAD_GLOBAL_BUILTIN",
],
"LOAD_SUPER_ATTR": [
"LOAD_SUPER_ATTR_ATTR",
"LOAD_SUPER_ATTR_METHOD",
],
"LOAD_ATTR": [
"LOAD_ATTR_INSTANCE_VALUE",
"LOAD_ATTR_MODULE",
"LOAD_ATTR_WITH_HINT",
"LOAD_ATTR_SLOT",
"LOAD_ATTR_CLASS",
"LOAD_ATTR_CLASS_WITH_METACLASS_CHECK",
"LOAD_ATTR_PROPERTY",
"LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN",
"LOAD_ATTR_METHOD_WITH_VALUES",
"LOAD_ATTR_METHOD_NO_DICT",
"LOAD_ATTR_METHOD_LAZY_DICT",
"LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES",
"LOAD_ATTR_NONDESCRIPTOR_NO_DICT",
],
"COMPARE_OP": [
"COMPARE_OP_FLOAT",
"COMPARE_OP_INT",
"COMPARE_OP_STR",
],
"CONTAINS_OP": [
"CONTAINS_OP_SET",
"CONTAINS_OP_DICT",
],
"JUMP_BACKWARD": [
"JUMP_BACKWARD_NO_JIT",
"JUMP_BACKWARD_JIT",
],
"FOR_ITER": [
"FOR_ITER_LIST",
"FOR_ITER_TUPLE",
"FOR_ITER_RANGE",
"FOR_ITER_GEN",
],
"CALL": [
"CALL_BOUND_METHOD_EXACT_ARGS",
"CALL_PY_EXACT_ARGS",
"CALL_TYPE_1",
"CALL_STR_1",
"CALL_TUPLE_1",
"CALL_BUILTIN_CLASS",
"CALL_BUILTIN_O",
"CALL_BUILTIN_FAST",
"CALL_BUILTIN_FAST_WITH_KEYWORDS",
"CALL_LEN",
"CALL_ISINSTANCE",
"CALL_LIST_APPEND",
"CALL_METHOD_DESCRIPTOR_O",
"CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS",
"CALL_METHOD_DESCRIPTOR_NOARGS",
"CALL_METHOD_DESCRIPTOR_FAST",
"CALL_ALLOC_AND_ENTER_INIT",
"CALL_PY_GENERAL",
"CALL_BOUND_METHOD_GENERAL",
"CALL_NON_PY_GENERAL",
],
"CALL_KW": [
"CALL_KW_BOUND_METHOD",
"CALL_KW_PY",
"CALL_KW_NON_PY",
],
}
_specialized_opmap = {}
_specialized_opmap = {
'BINARY_OP_ADD_FLOAT': 129,
'BINARY_OP_ADD_INT': 130,
'BINARY_OP_ADD_UNICODE': 131,
'BINARY_OP_EXTEND': 132,
'BINARY_OP_INPLACE_ADD_UNICODE': 3,
'BINARY_OP_MULTIPLY_FLOAT': 133,
'BINARY_OP_MULTIPLY_INT': 134,
'BINARY_OP_SUBSCR_DICT': 135,
'BINARY_OP_SUBSCR_GETITEM': 136,
'BINARY_OP_SUBSCR_LIST_INT': 137,
'BINARY_OP_SUBSCR_LIST_SLICE': 138,
'BINARY_OP_SUBSCR_STR_INT': 139,
'BINARY_OP_SUBSCR_TUPLE_INT': 140,
'BINARY_OP_SUBTRACT_FLOAT': 141,
'BINARY_OP_SUBTRACT_INT': 142,
'CALL_ALLOC_AND_ENTER_INIT': 143,
'CALL_BOUND_METHOD_EXACT_ARGS': 144,
'CALL_BOUND_METHOD_GENERAL': 145,
'CALL_BUILTIN_CLASS': 146,
'CALL_BUILTIN_FAST': 147,
'CALL_BUILTIN_FAST_WITH_KEYWORDS': 148,
'CALL_BUILTIN_O': 149,
'CALL_ISINSTANCE': 150,
'CALL_KW_BOUND_METHOD': 151,
'CALL_KW_NON_PY': 152,
'CALL_KW_PY': 153,
'CALL_LEN': 154,
'CALL_LIST_APPEND': 155,
'CALL_METHOD_DESCRIPTOR_FAST': 156,
'CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS': 157,
'CALL_METHOD_DESCRIPTOR_NOARGS': 158,
'CALL_METHOD_DESCRIPTOR_O': 159,
'CALL_NON_PY_GENERAL': 160,
'CALL_PY_EXACT_ARGS': 161,
'CALL_PY_GENERAL': 162,
'CALL_STR_1': 163,
'CALL_TUPLE_1': 164,
'CALL_TYPE_1': 165,
'COMPARE_OP_FLOAT': 166,
'COMPARE_OP_INT': 167,
'COMPARE_OP_STR': 168,
'CONTAINS_OP_DICT': 169,
'CONTAINS_OP_SET': 170,
'FOR_ITER_GEN': 171,
'FOR_ITER_LIST': 172,
'FOR_ITER_RANGE': 173,
'FOR_ITER_TUPLE': 174,
'JUMP_BACKWARD_JIT': 175,
'JUMP_BACKWARD_NO_JIT': 176,
'LOAD_ATTR_CLASS': 177,
'LOAD_ATTR_CLASS_WITH_METACLASS_CHECK': 178,
'LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN': 179,
'LOAD_ATTR_INSTANCE_VALUE': 180,
'LOAD_ATTR_METHOD_LAZY_DICT': 181,
'LOAD_ATTR_METHOD_NO_DICT': 182,
'LOAD_ATTR_METHOD_WITH_VALUES': 183,
'LOAD_ATTR_MODULE': 184,
'LOAD_ATTR_NONDESCRIPTOR_NO_DICT': 185,
'LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES': 186,
'LOAD_ATTR_PROPERTY': 187,
'LOAD_ATTR_SLOT': 188,
'LOAD_ATTR_WITH_HINT': 189,
'LOAD_CONST_IMMORTAL': 190,
'LOAD_CONST_MORTAL': 191,
'LOAD_GLOBAL_BUILTIN': 192,
'LOAD_GLOBAL_MODULE': 193,
'LOAD_SUPER_ATTR_ATTR': 194,
'LOAD_SUPER_ATTR_METHOD': 195,
'RESUME_CHECK': 196,
'SEND_GEN': 197,
'STORE_ATTR_INSTANCE_VALUE': 198,
'STORE_ATTR_SLOT': 199,
'STORE_ATTR_WITH_HINT': 200,
'STORE_SUBSCR_DICT': 201,
'STORE_SUBSCR_LIST_INT': 202,
'TO_BOOL_ALWAYS_TRUE': 203,
'TO_BOOL_BOOL': 204,
'TO_BOOL_INT': 205,
'TO_BOOL_LIST': 206,
'TO_BOOL_NONE': 207,
'TO_BOOL_STR': 208,
'UNPACK_SEQUENCE_LIST': 209,
'UNPACK_SEQUENCE_TUPLE': 210,
'UNPACK_SEQUENCE_TWO_TUPLE': 211,
}
opmap = {
'CACHE': 0,
'RESERVED': 17,
'RESUME': 128,
'INSTRUMENTED_LINE': 254,
'ENTER_EXECUTOR': 255,
'BINARY_SLICE': 1,
'BUILD_TEMPLATE': 2,
'BINARY_OP_INPLACE_ADD_UNICODE': 3,
'CALL_FUNCTION_EX': 4,
'CHECK_EG_MATCH': 5,
'CHECK_EXC_MATCH': 6,
@@ -24,7 +231,6 @@ opmap = {
'GET_AITER': 14,
'GET_ANEXT': 15,
'GET_ITER': 16,
'RESERVED': 17,
'GET_LEN': 18,
'GET_YIELD_FROM_ITER': 19,
'INTERPRETER_EXIT': 20,
@@ -128,90 +334,6 @@ opmap = {
'UNPACK_EX': 118,
'UNPACK_SEQUENCE': 119,
'YIELD_VALUE': 120,
'RESUME': 128,
'BINARY_OP_ADD_FLOAT': 129,
'BINARY_OP_ADD_INT': 130,
'BINARY_OP_ADD_UNICODE': 131,
'BINARY_OP_EXTEND': 132,
'BINARY_OP_MULTIPLY_FLOAT': 133,
'BINARY_OP_MULTIPLY_INT': 134,
'BINARY_OP_SUBSCR_DICT': 135,
'BINARY_OP_SUBSCR_GETITEM': 136,
'BINARY_OP_SUBSCR_LIST_INT': 137,
'BINARY_OP_SUBSCR_LIST_SLICE': 138,
'BINARY_OP_SUBSCR_STR_INT': 139,
'BINARY_OP_SUBSCR_TUPLE_INT': 140,
'BINARY_OP_SUBTRACT_FLOAT': 141,
'BINARY_OP_SUBTRACT_INT': 142,
'CALL_ALLOC_AND_ENTER_INIT': 143,
'CALL_BOUND_METHOD_EXACT_ARGS': 144,
'CALL_BOUND_METHOD_GENERAL': 145,
'CALL_BUILTIN_CLASS': 146,
'CALL_BUILTIN_FAST': 147,
'CALL_BUILTIN_FAST_WITH_KEYWORDS': 148,
'CALL_BUILTIN_O': 149,
'CALL_ISINSTANCE': 150,
'CALL_KW_BOUND_METHOD': 151,
'CALL_KW_NON_PY': 152,
'CALL_KW_PY': 153,
'CALL_LEN': 154,
'CALL_LIST_APPEND': 155,
'CALL_METHOD_DESCRIPTOR_FAST': 156,
'CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS': 157,
'CALL_METHOD_DESCRIPTOR_NOARGS': 158,
'CALL_METHOD_DESCRIPTOR_O': 159,
'CALL_NON_PY_GENERAL': 160,
'CALL_PY_EXACT_ARGS': 161,
'CALL_PY_GENERAL': 162,
'CALL_STR_1': 163,
'CALL_TUPLE_1': 164,
'CALL_TYPE_1': 165,
'COMPARE_OP_FLOAT': 166,
'COMPARE_OP_INT': 167,
'COMPARE_OP_STR': 168,
'CONTAINS_OP_DICT': 169,
'CONTAINS_OP_SET': 170,
'FOR_ITER_GEN': 171,
'FOR_ITER_LIST': 172,
'FOR_ITER_RANGE': 173,
'FOR_ITER_TUPLE': 174,
'JUMP_BACKWARD_JIT': 175,
'JUMP_BACKWARD_NO_JIT': 176,
'LOAD_ATTR_CLASS': 177,
'LOAD_ATTR_CLASS_WITH_METACLASS_CHECK': 178,
'LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN': 179,
'LOAD_ATTR_INSTANCE_VALUE': 180,
'LOAD_ATTR_METHOD_LAZY_DICT': 181,
'LOAD_ATTR_METHOD_NO_DICT': 182,
'LOAD_ATTR_METHOD_WITH_VALUES': 183,
'LOAD_ATTR_MODULE': 184,
'LOAD_ATTR_NONDESCRIPTOR_NO_DICT': 185,
'LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES': 186,
'LOAD_ATTR_PROPERTY': 187,
'LOAD_ATTR_SLOT': 188,
'LOAD_ATTR_WITH_HINT': 189,
'LOAD_CONST_IMMORTAL': 190,
'LOAD_CONST_MORTAL': 191,
'LOAD_GLOBAL_BUILTIN': 192,
'LOAD_GLOBAL_MODULE': 193,
'LOAD_SUPER_ATTR_ATTR': 194,
'LOAD_SUPER_ATTR_METHOD': 195,
'RESUME_CHECK': 196,
'SEND_GEN': 197,
'STORE_ATTR_INSTANCE_VALUE': 198,
'STORE_ATTR_SLOT': 199,
'STORE_ATTR_WITH_HINT': 200,
'STORE_SUBSCR_DICT': 201,
'STORE_SUBSCR_LIST_INT': 202,
'TO_BOOL_ALWAYS_TRUE': 203,
'TO_BOOL_BOOL': 204,
'TO_BOOL_INT': 205,
'TO_BOOL_LIST': 206,
'TO_BOOL_NONE': 207,
'TO_BOOL_STR': 208,
'UNPACK_SEQUENCE_LIST': 209,
'UNPACK_SEQUENCE_TUPLE': 210,
'UNPACK_SEQUENCE_TWO_TUPLE': 211,
'INSTRUMENTED_END_FOR': 234,
'INSTRUMENTED_POP_ITER': 235,
'INSTRUMENTED_END_SEND': 236,
@@ -232,8 +354,6 @@ opmap = {
'INSTRUMENTED_CALL_KW': 251,
'INSTRUMENTED_CALL_FUNCTION_EX': 252,
'INSTRUMENTED_JUMP_BACKWARD': 253,
'INSTRUMENTED_LINE': 254,
'ENTER_EXECUTOR': 255,
'ANNOTATIONS_PLACEHOLDER': 256,
'JUMP': 257,
'JUMP_IF_FALSE': 258,
@@ -247,6 +367,5 @@ opmap = {
'STORE_FAST_MAYBE_NULL': 266,
}
# CPython 3.13 compatible: opcodes < 44 have no argument
HAVE_ARGUMENT = 44
MIN_INSTRUMENTED_OPCODE = 236
HAVE_ARGUMENT = 43
MIN_INSTRUMENTED_OPCODE = 234
-2
View File
@@ -38,7 +38,6 @@ class OpListTests(unittest.TestCase):
opcodes = [dis.opmap[opname] for opname in names]
self.check_bool_function_result(_opcode.is_valid, opcodes, True)
@unittest.expectedFailure # TODO: RUSTPYTHON; KeyError: 'BINARY_OP_ADD_INT'
def test_opmaps(self):
def check_roundtrip(name, map):
return self.assertEqual(opcode.opname[map[name]], name)
@@ -116,7 +115,6 @@ class StackEffectTests(unittest.TestCase):
class SpecializationStatsTests(unittest.TestCase):
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 'load_attr' not found in []
def test_specialization_stats(self):
stat_names = ["success", "failure", "hit", "deferred", "miss", "deopt"]
specialized_opcodes = [
-1
View File
@@ -1005,7 +1005,6 @@ class DisTests(DisTestBase):
def test_boundaries(self):
self.assertEqual(dis.opmap["EXTENDED_ARG"], dis.EXTENDED_ARG)
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 29 not less than or equal to 20
def test_widths(self):
long_opcodes = set(['JUMP_BACKWARD_NO_INTERRUPT',
'LOAD_FAST_BORROW_LOAD_FAST_BORROW',
+109 -99
View File
@@ -462,7 +462,7 @@ impl TryFrom<u8> for Instruction {
impl Instruction {
/// Returns `true` if this is any instrumented opcode
/// (regular INSTRUMENTED_*, INSTRUMENTED_LINE, or INSTRUMENTED_INSTRUCTION).
pub fn is_instrumented(self) -> bool {
pub const fn is_instrumented(self) -> bool {
self.to_base().is_some()
|| matches!(self, Self::InstrumentedLine | Self::InstrumentedInstruction)
}
@@ -509,7 +509,7 @@ impl Instruction {
///
/// The returned base opcode uses `Arg::marker()` for typed fields —
/// only the opcode byte matters since `replace_op` preserves the arg byte.
pub fn to_base(self) -> Option<Self> {
pub const fn to_base(self) -> Option<Self> {
Some(match self {
Self::InstrumentedResume => Self::Resume {
context: Arg::marker(),
@@ -558,66 +558,15 @@ impl Instruction {
/// Map a specialized opcode back to its adaptive (base) variant.
/// `_PyOpcode_Deopt`
pub fn deoptimize(self) -> Self {
match self {
// LOAD_ATTR specializations
Self::LoadAttrClass
| Self::LoadAttrClassWithMetaclassCheck
| Self::LoadAttrGetattributeOverridden
| Self::LoadAttrInstanceValue
| Self::LoadAttrMethodLazyDict
| Self::LoadAttrMethodNoDict
| Self::LoadAttrMethodWithValues
| Self::LoadAttrModule
| Self::LoadAttrNondescriptorNoDict
| Self::LoadAttrNondescriptorWithValues
| Self::LoadAttrProperty
| Self::LoadAttrSlot
| Self::LoadAttrWithHint => Self::LoadAttr {
namei: Arg::marker(),
pub const fn deopt(self) -> Option<Self> {
Some(match self {
// RESUME specializations
Self::ResumeCheck => Self::Resume {
context: Arg::marker(),
},
// BINARY_OP specializations
Self::BinaryOpAddFloat
| Self::BinaryOpAddInt
| Self::BinaryOpAddUnicode
| Self::BinaryOpExtend
| Self::BinaryOpInplaceAddUnicode
| Self::BinaryOpMultiplyFloat
| Self::BinaryOpMultiplyInt
| Self::BinaryOpSubscrDict
| Self::BinaryOpSubscrGetitem
| Self::BinaryOpSubscrListInt
| Self::BinaryOpSubscrListSlice
| Self::BinaryOpSubscrStrInt
| Self::BinaryOpSubscrTupleInt
| Self::BinaryOpSubtractFloat
| Self::BinaryOpSubtractInt => Self::BinaryOp { op: Arg::marker() },
// CALL specializations
Self::CallAllocAndEnterInit
| Self::CallBoundMethodExactArgs
| Self::CallBoundMethodGeneral
| Self::CallBuiltinClass
| Self::CallBuiltinFast
| Self::CallBuiltinFastWithKeywords
| Self::CallBuiltinO
| Self::CallIsinstance
| Self::CallLen
| Self::CallListAppend
| Self::CallMethodDescriptorFast
| Self::CallMethodDescriptorFastWithKeywords
| Self::CallMethodDescriptorNoargs
| Self::CallMethodDescriptorO
| Self::CallNonPyGeneral
| Self::CallPyExactArgs
| Self::CallPyGeneral
| Self::CallStr1
| Self::CallTuple1
| Self::CallType1 => Self::Call {
argc: Arg::marker(),
},
// CALL_KW specializations
Self::CallKwBoundMethod | Self::CallKwNonPy | Self::CallKwPy => Self::CallKw {
argc: Arg::marker(),
// LOAD_CONST specializations
Self::LoadConstMortal | Self::LoadConstImmortal => Self::LoadConst {
consti: Arg::marker(),
},
// TO_BOOL specializations
Self::ToBoolAlwaysTrue
@@ -626,69 +575,130 @@ impl Instruction {
| Self::ToBoolList
| Self::ToBoolNone
| Self::ToBoolStr => Self::ToBool,
// COMPARE_OP specializations
Self::CompareOpFloat | Self::CompareOpInt | Self::CompareOpStr => Self::CompareOp {
opname: Arg::marker(),
// BINARY_OP specializations
Self::BinaryOpMultiplyInt
| Self::BinaryOpAddInt
| Self::BinaryOpSubtractInt
| Self::BinaryOpMultiplyFloat
| Self::BinaryOpAddFloat
| Self::BinaryOpSubtractFloat
| Self::BinaryOpAddUnicode
| Self::BinaryOpSubscrListInt
| Self::BinaryOpSubscrListSlice
| Self::BinaryOpSubscrTupleInt
| Self::BinaryOpSubscrStrInt
| Self::BinaryOpSubscrDict
| Self::BinaryOpSubscrGetitem
| Self::BinaryOpExtend
| Self::BinaryOpInplaceAddUnicode => Self::BinaryOp { op: Arg::marker() },
// STORE_SUBSCR specializations
Self::StoreSubscrDict | Self::StoreSubscrListInt => Self::StoreSubscr,
// SEND specializations
Self::SendGen => Self::Send {
delta: Arg::marker(),
},
// CONTAINS_OP specializations
Self::ContainsOpDict | Self::ContainsOpSet => Self::ContainsOp {
invert: Arg::marker(),
},
// FOR_ITER specializations
Self::ForIterGen | Self::ForIterList | Self::ForIterRange | Self::ForIterTuple => {
Self::ForIter {
delta: Arg::marker(),
// UNPACK_SEQUENCE specializations
Self::UnpackSequenceTwoTuple | Self::UnpackSequenceTuple | Self::UnpackSequenceList => {
Self::UnpackSequence {
count: Arg::marker(),
}
}
// LOAD_GLOBAL specializations
Self::LoadGlobalBuiltin | Self::LoadGlobalModule => Self::LoadGlobal {
namei: Arg::marker(),
},
// STORE_ATTR specializations
Self::StoreAttrInstanceValue | Self::StoreAttrSlot | Self::StoreAttrWithHint => {
Self::StoreAttr {
namei: Arg::marker(),
}
}
// LOAD_GLOBAL specializations
Self::LoadGlobalModule | Self::LoadGlobalBuiltin => Self::LoadGlobal {
namei: Arg::marker(),
},
// LOAD_SUPER_ATTR specializations
Self::LoadSuperAttrAttr | Self::LoadSuperAttrMethod => Self::LoadSuperAttr {
namei: Arg::marker(),
},
// STORE_SUBSCR specializations
Self::StoreSubscrDict | Self::StoreSubscrListInt => Self::StoreSubscr,
// UNPACK_SEQUENCE specializations
Self::UnpackSequenceList | Self::UnpackSequenceTuple | Self::UnpackSequenceTwoTuple => {
Self::UnpackSequence {
count: Arg::marker(),
}
}
// SEND specializations
Self::SendGen => Self::Send {
delta: Arg::marker(),
// LOAD_ATTR specializations
Self::LoadAttrInstanceValue
| Self::LoadAttrModule
| Self::LoadAttrWithHint
| Self::LoadAttrSlot
| Self::LoadAttrClass
| Self::LoadAttrClassWithMetaclassCheck
| Self::LoadAttrProperty
| Self::LoadAttrGetattributeOverridden
| Self::LoadAttrMethodWithValues
| Self::LoadAttrMethodNoDict
| Self::LoadAttrMethodLazyDict
| Self::LoadAttrNondescriptorWithValues
| Self::LoadAttrNondescriptorNoDict => Self::LoadAttr {
namei: Arg::marker(),
},
// LOAD_CONST specializations
Self::LoadConstImmortal | Self::LoadConstMortal => Self::LoadConst {
consti: Arg::marker(),
// COMPARE_OP specializations
Self::CompareOpFloat | Self::CompareOpInt | Self::CompareOpStr => Self::CompareOp {
opname: Arg::marker(),
},
// RESUME specializations
Self::ResumeCheck => Self::Resume {
context: Arg::marker(),
// CONTAINS_OP specializations
Self::ContainsOpSet | Self::ContainsOpDict => Self::ContainsOp {
invert: Arg::marker(),
},
// JUMP_BACKWARD specializations
Self::JumpBackwardJit | Self::JumpBackwardNoJit => Self::JumpBackward {
Self::JumpBackwardNoJit | Self::JumpBackwardJit => Self::JumpBackward {
delta: Arg::marker(),
},
// Instrumented opcodes map back to their base
_ => match self.to_base() {
Some(base) => base,
None => self,
// FOR_ITER specializations
Self::ForIterList | Self::ForIterTuple | Self::ForIterRange | Self::ForIterGen => {
Self::ForIter {
delta: Arg::marker(),
}
}
// CALL specializations
Self::CallBoundMethodExactArgs
| Self::CallPyExactArgs
| Self::CallType1
| Self::CallStr1
| Self::CallTuple1
| Self::CallBuiltinClass
| Self::CallBuiltinO
| Self::CallBuiltinFast
| Self::CallBuiltinFastWithKeywords
| Self::CallLen
| Self::CallIsinstance
| Self::CallListAppend
| Self::CallMethodDescriptorO
| Self::CallMethodDescriptorFastWithKeywords
| Self::CallMethodDescriptorNoargs
| Self::CallMethodDescriptorFast
| Self::CallAllocAndEnterInit
| Self::CallPyGeneral
| Self::CallBoundMethodGeneral
| Self::CallNonPyGeneral => Self::Call {
argc: Arg::marker(),
},
// CALL_KW specializations
Self::CallKwBoundMethod | Self::CallKwPy | Self::CallKwNonPy => Self::CallKw {
argc: Arg::marker(),
},
_ => return None,
})
}
/// Map a specialized or instrumented opcode back to its adaptive (base) variant.
pub const fn deoptimize(self) -> Self {
match self.deopt() {
Some(v) => v,
None => {
// Instrumented opcodes map back to their base
match self.to_base() {
Some(v) => v,
None => self,
}
}
}
}
/// Number of CACHE code units that follow this instruction.
/// _PyOpcode_Caches
pub fn cache_entries(self) -> usize {
pub const fn cache_entries(self) -> usize {
match self {
// LOAD_ATTR: 9 cache entries
Self::LoadAttr { .. }
+5
View File
@@ -197,6 +197,11 @@ mod _opcode {
let opcode = Opcode::try_from_pyint(args.opcode, vm)?;
// Raise ValueError if specialized.
if opcode.inner().real().is_some_and(|op| op.deopt().is_some()) {
return Err(vm.new_value_error("invalid opcode or oparg"));
}
let _ = jump; // Python API accepts jump but it's not used
Ok(opcode.stack_effect(oparg))
}
+113 -17
View File
@@ -16,25 +16,48 @@ BYTECODE_FILE = (
OPCODE_METADATA_FILE = ROOT / "Lib" / "_opcode_metadata.py"
# Opcodes that needs to be first, regardless of their opcode ID.
PRIORITY_OPMAP = {
"CACHE",
"RESERVED",
"RESUME",
"INSTRUMENTED_LINE",
"ENTER_EXECUTOR",
}
def to_pascal_case(s: str) -> str:
res = re.sub(r"(?<=[a-z0-9])([A-Z])", r"_\1", s)
return re.sub(r"(\D)(\d+)$", r"\1_\2", res).upper()
class Opcode(typing.NamedTuple):
rust_name: str
id: int
have_oparg: bool
@property
def cpython_name(self) -> str:
name = re.sub(r"(?<=[a-z0-9])([A-Z])", r"_\1", self.rust_name)
return re.sub(r"(\D)(\d+)$", r"\1_\2", name).upper()
return to_pascal_case(self.rust_name)
@property
def is_instrumented(self):
return self.cpython_name.startswith("INSTRUMENTED_")
@classmethod
def from_str(cls, body: str):
raw_variants = re.split(r"(\d+),", body.strip())
raw_variants.remove("")
for raw_name, raw_id in itertools.batched(raw_variants, 2):
for raw_name, raw_id in itertools.batched(raw_variants, 2, strict=True):
have_oparg = "Arg<" in raw_name # Hacky but works
name = re.findall(r"\b[A-Z][A-Za-z]*\d*\b(?=\s*[\({=])", raw_name)[0]
yield cls(rust_name=name.strip(), id=int(raw_id))
yield cls(rust_name=name.strip(), id=int(raw_id), have_oparg=have_oparg)
def __lt__(self, other: typing.Self) -> bool:
return self.id < other.id
sprio, oprio = (
opcode.cpython_name not in PRIORITY_OPMAP for opcode in (self, other)
)
return (sprio, self.id) < (oprio, other.id)
def extract_enum_body(contents: str, enum_name: str) -> str:
@@ -49,33 +72,106 @@ def extract_enum_body(contents: str, enum_name: str) -> str:
)
def build_deopts(contents: str) -> dict[str, list[str]]:
raw_body = re.search(
r"fn deopt\(self\) -> Option<Self>(.*)", contents, re.DOTALL
).group(1)
body = "\n".join(
itertools.takewhile(
lambda l: not l.startswith("_ =>"), # Take until reaching fallback
filter(
lambda l: (
not l.startswith(
("//", "Some(match")
) # Skip comments or start of match
),
map(str.strip, raw_body.splitlines()),
),
)
).removeprefix("{")
depth = 0
arms = []
buf = []
for char in body:
if char == "{":
depth += 1
elif char == "}":
depth -= 1
if depth == 0 and (char in ("}", ",")):
arm = "".join(buf).strip()
arms.append(arm)
buf = []
else:
buf.append(char)
# last arm
arms.append("".join(buf))
arms = [arm for arm in arms if arm]
deopts = {}
for arm in arms:
*specialized, deopt = map(to_pascal_case, re.findall(r"Self::(\w*)\b", arm))
deopts[deopt] = specialized
return deopts
contents = BYTECODE_FILE.read_text(encoding="utf-8")
deopts = build_deopts(contents)
enum_body = "\n".join(
extract_enum_body(contents, enum_name)
for enum_name in ("Instruction", "PseudoInstruction")
)
opcodes = list(Opcode.from_str(enum_body))
have_oparg = min(opcode.id for opcode in opcodes if opcode.have_oparg) - 1
min_instrumented = min(opcode.id for opcode in opcodes if opcode.is_instrumented)
# Generate the output file
output = """# This file is generated by scripts/generate_opcode_metadata.py
# for RustPython bytecode format (CPython 3.13 compatible opcode numbers).
# for RustPython bytecode format (CPython 3.14 compatible opcode numbers).
# Do not edit!
_specializations = {}
_specialized_opmap = {}
opmap = {
"""
output += "\n_specializations = {\n"
for key, lst in deopts.items():
output += f' "{key}": [\n'
for item in lst:
output += f' "{item}",\n'
output += " ],\n"
output += "}\n"
specialized = set(itertools.chain.from_iterable(deopts.values()))
output += "\n_specialized_opmap = {\n"
for opcode in sorted(opcodes, key=lambda op: op.cpython_name):
cpython_name = opcode.cpython_name
if cpython_name not in specialized:
continue
output += f" '{cpython_name}': {opcode.id},\n"
output += "}\n"
output += "\nopmap = {\n"
for opcode in sorted(opcodes):
output += f" '{opcode.cpython_name}': {opcode.id},\n"
cpython_name = opcode.cpython_name
if cpython_name in specialized:
continue
output += """}
output += f" '{cpython_name}': {opcode.id},\n"
# CPython 3.13 compatible: opcodes < 44 have no argument
HAVE_ARGUMENT = 44
MIN_INSTRUMENTED_OPCODE = 236
output += "}\n"
output += f"""
HAVE_ARGUMENT = {have_oparg}
MIN_INSTRUMENTED_OPCODE = {min_instrumented}
"""
OPCODE_METADATA_FILE.write_text(output, encoding="utf-8")