Skip to content

Conversation

@youknowone
Copy link
Member

@youknowone youknowone commented Jan 19, 2026

Summary by CodeRabbit

  • Chores
    • Updated bytecode instruction set to align with CPython 3.14 compatibility standards.
    • Simplified instruction handling by removing specialized multi-tuple and map-building variants.
    • Updated compiled Python code format version to reflect internal changes.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 19, 2026

📝 Walkthrough

Walkthrough

Aligns RustPython's bytecode instruction set and opcodes with CPython 3.14, replacing CPython 3.13 definitions, introducing new opcodes (BinarySlice, BuildTemplate, GetLen, etc.), removing deprecated instruction variants (BuildListFromTuples, BuildMapForCall, etc.), and updating the pyc magic number.

Changes

Cohort / File(s) Summary
CPython 3.14 Opcode Alignment
crates/compiler-core/src/bytecode/instruction.rs
Restructured Instruction enum to align with CPython 3.14 opcodes (44–120 range); added new variants (BinarySlice, BuildTemplate, GetANext, GetIter, GetLen, GetYieldFromIter, LoadCommonConstant, LoadSmallInt, LoadSpecial, LoadFastBorrow, DictMerge, ListExtend, SetUpdate, BuildInterpolation, EndAsyncFor, etc.); added RustPython-specific and instrumented opcode variants; updated TryFrom mappings and opcode ranges; refreshed InstructionMetadata stack effect calculations and formatting/disassembly logic for new/renamed variants.
Frame Execution Cleanup
crates/vm/src/frame.rs
Removed deprecated instruction handling paths: BuildListFromTuples, BuildMapForCall, BuildSetFromTuples, BuildTupleFromIter, BuildTupleFromTuples, and associated helper functions (flatten_tuples, execute_build_map_for_call).
Version/Magic Number Update
crates/vm/src/version.rs
Updated PYC_MAGIC_NUMBER from 2997 to 2996 to reflect CPython 3.14 compatibility.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Replace custom opcodes with CPython standard sequences #6794 — Modifies the same instruction set and execution code, adding DictMerge, ListExtend, SetUpdate, and removing Build\*FromTuples/BuildMapForCall variants in parallel.
  • Bytecode pseudo opcodes #6715 — Updates bytecode instruction definitions and metadata (Instruction enum, TryFrom\<u8\> mappings, InstructionMetadata) to refactor opcode layout and introduce new helper functions.
  • RustPython version to 3.14 #6718 — Aligns RustPython to CPython 3.14 across multiple files (opcode/Instruction updates, frame execution changes, version/magic-number updates).

Suggested reviewers

  • ShaharNaveh

Poem

🐰 A rabbit hops through bytecode bright,
Aligning opcodes to CPython's light,
Old BuildTuples fade away,
CPython 3.14 is here to stay,
Magic numbers dance in pairs,
New instructions everywhere!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Instruction 3.14' directly references the main change: updating the instruction set to align with CPython 3.14 instead of 3.13, which is the primary modification across all three affected files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@youknowone youknowone marked this pull request as ready for review January 19, 2026 18:37
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/compiler-core/src/bytecode/instruction.rs (1)

416-463: RustPython cannot deserialize CPython 3.14 bytecode using opcodes 121‑127 or 226‑233.

CPython 3.14 assigns opcodes throughout the entire range 0–255. Specifically, values 121–127 are assigned to STORE_FAST_LOAD_FAST through FAST_FUNCTION, and values 226–233 are assigned to BUILD_MAP__RETURN_VALUE through MATCH_CLASS__RETURN_VALUE. The current TryFrom<u8> implementation rejects these ranges, causing valid CPython 3.14 bytecode to fail deserialization. These gaps must be filled with the corresponding CPython 3.14 opcodes to maintain compatibility.

🤖 Fix all issues with AI agents
In `@crates/compiler-core/src/bytecode/instruction.rs`:
- Around line 17-199: The GetAwaitable opcode in the Instruction enum is missing
the required oparg ("where") per CPython 3.14; update the
Instruction::GetAwaitable variant to include an oparg field (e.g. GetAwaitable {
oparg: Arg<u32> } or a small enum type Arg<GetAwaitableWhere>) so
decoding/encoding and semantics can read 0/1/2 contexts correctly; search for
Instruction::GetAwaitable and adjust any places that construct, pattern-match,
encode/decode, or format Instruction to handle the new oparg field (and add a
small GetAwaitableWhere type if you prefer typed values instead of raw u32).

In `@crates/vm/src/version.rs`:
- Around line 131-132: The constant PYC_MAGIC_NUMBER is set incorrectly to 2996;
update the value of pub const PYC_MAGIC_NUMBER in version.rs to 3627 so it
matches CPython 3.14's magic number (as defined in CPython's
pycore_magic_number) to ensure .pyc files from CPython 3.14 are read correctly.
🧹 Nitpick comments (1)
crates/compiler-core/src/bytecode/instruction.rs (1)

872-1011: Add disassembly formatting for newly added opcodes to avoid placeholder output.

Several new variants (e.g., LoadCommonConstant, LoadSmallInt, LoadSpecial, LoadFastBorrow, BinarySlice, StoreSlice) currently fall through to RUSTPYTHON_PLACEHOLDER, making dis output harder to read. Consider adding explicit formatters.

♻️ Suggested disassembly cases
@@
-            Self::BinarySubscr => w!(BINARY_SUBSCR),
+            Self::BinarySubscr => w!(BINARY_SUBSCR),
+            Self::BinarySlice => w!(BINARY_SLICE),
@@
-            Self::LoadBuildClass => w!(LOAD_BUILD_CLASS),
-            Self::LoadFromDictOrDeref(i) => w!(LOAD_FROM_DICT_OR_DEREF, cell_name = i),
-            Self::LoadConst { idx } => fmt_const("LOAD_CONST", arg, f, idx),
+            Self::LoadBuildClass => w!(LOAD_BUILD_CLASS),
+            Self::LoadCommonConstant { idx } => w!(LOAD_COMMON_CONSTANT, idx),
+            Self::LoadSmallInt { idx } => w!(LOAD_SMALL_INT, idx),
+            Self::LoadSpecial { arg } => w!(LOAD_SPECIAL, arg),
+            Self::LoadFromDictOrDeref(i) => w!(LOAD_FROM_DICT_OR_DEREF, cell_name = i),
+            Self::LoadConst { idx } => fmt_const("LOAD_CONST", arg, f, idx),
             Self::LoadDeref(idx) => w!(LOAD_DEREF, cell_name = idx),
             Self::LoadFast(idx) => w!(LOAD_FAST, varname = idx),
             Self::LoadFastAndClear(idx) => w!(LOAD_FAST_AND_CLEAR, varname = idx),
+            Self::LoadFastBorrow(idx) => w!(LOAD_FAST_BORROW, varname = idx),
@@
-            Self::StoreSubscr => w!(STORE_SUBSCR),
+            Self::StoreSubscr => w!(STORE_SUBSCR),
+            Self::StoreSlice => w!(STORE_SLICE),

Comment on lines +17 to +199
/// Currently aligned with CPython 3.14.
///
/// ## See also
/// - [CPython opcode IDs](https://github.com/python/cpython/blob/627894459a84be3488a1789919679c997056a03c/Include/opcode_ids.h)
/// - [CPython opcode IDs](https://github.com/python/cpython/blob/v3.14.2/Include/opcode_ids.h)
#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub enum Instruction {
// ==================== No-argument instructions (opcode < 44) ====================
Cache = 0, // Placeholder
BeforeAsyncWith = 1,
BeforeWith = 2,
// No-argument instructions (opcode < HAVE_ARGUMENT=44)
Cache = 0, // Placeholder
BinarySlice = 1, // Placeholder
BuildTemplate = 2,
BinaryOpInplaceAddUnicode = 3, // Placeholder
BinarySlice = 4, // Placeholder
BinarySubscr = 5,
CheckEgMatch = 6,
CheckExcMatch = 7,
CleanupThrow = 8,
DeleteSubscr = 9,
EndAsyncFor = 10,
EndFor = 11, // Placeholder
EndSend = 12,
ExitInitCheck = 13, // Placeholder
FormatSimple = 14,
FormatWithSpec = 15,
GetAIter = 16,
CallFunctionEx = 4,
CheckEgMatch = 5,
CheckExcMatch = 6,
CleanupThrow = 7,
DeleteSubscr = 8,
EndFor = 9, // Placeholder
EndSend = 10,
ExitInitCheck = 11, // Placeholder
FormatSimple = 12,
FormatWithSpec = 13,
GetAIter = 14,
GetANext = 15,
GetIter = 16,
Reserved = 17,
GetANext = 18,
GetIter = 19,
GetLen = 20,
GetYieldFromIter = 21,
InterpreterExit = 22, // Placeholder
LoadAssertionError = 23, // Placeholder
LoadBuildClass = 24,
LoadLocals = 25, // Placeholder
MakeFunction = 26,
MatchKeys = 27,
MatchMapping = 28,
MatchSequence = 29,
Nop = 30,
PopExcept = 31,
PopTop = 32,
PushExcInfo = 33,
PushNull = 34,
ReturnGenerator = 35, // Placeholder
ReturnValue = 36,
SetupAnnotations = 37,
StoreSlice = 38, // Placeholder
StoreSubscr = 39,
ToBool = 40,
UnaryInvert = 41,
UnaryNegative = 42,
UnaryNot = 43,
WithExceptStart = 44,
// ==================== With-argument instructions (opcode > 44) ====================
GetLen = 18,
GetYieldFromIter = 19,
InterpreterExit = 20, // Placeholder
LoadBuildClass = 21,
LoadLocals = 22, // Placeholder
MakeFunction = 23,
MatchKeys = 24,
MatchMapping = 25,
MatchSequence = 26,
Nop = 27,
NotTaken = 28, // Placeholder
PopExcept = 29,
PopIter = 30, // Placeholder
PopTop = 31,
PushExcInfo = 32,
PushNull = 33,
ReturnGenerator = 34, // Placeholder
ReturnValue = 35,
SetupAnnotations = 36,
StoreSlice = 37, // Placeholder
StoreSubscr = 38,
ToBool = 39,
UnaryInvert = 40,
UnaryNegative = 41,
UnaryNot = 42,
WithExceptStart = 43,
// CPython 3.14 opcodes with arguments (44-120)
BinaryOp {
op: Arg<BinaryOperator>,
} = 44,
/// Build an Interpolation from value, expression string, and optional format_spec on stack.
///
/// oparg encoding: (conversion << 2) | has_format_spec
/// - has_format_spec (bit 0): if 1, format_spec is on stack
/// - conversion (bits 2+): 0=None, 1=Str, 2=Repr, 3=Ascii
///
/// Stack: [value, expression_str, format_spec?] -> [interpolation]
BuildInterpolation {
oparg: Arg<u32>,
} = 45,
BuildConstKeyMap {
size: Arg<u32>,
} = 46, // Placeholder
BuildList {
size: Arg<u32>,
} = 47,
} = 46,
BuildMap {
size: Arg<u32>,
} = 48,
} = 47,
BuildSet {
size: Arg<u32>,
} = 49,
} = 48,
BuildSlice {
argc: Arg<BuildSliceArgCount>,
} = 50,
} = 49,
BuildString {
size: Arg<u32>,
} = 51,
} = 50,
BuildTuple {
size: Arg<u32>,
} = 52,
} = 51,
Call {
nargs: Arg<u32>,
} = 53,
CallFunctionEx = 54,
} = 52,
CallIntrinsic1 {
func: Arg<IntrinsicFunction1>,
} = 55,
} = 53,
CallIntrinsic2 {
func: Arg<IntrinsicFunction2>,
} = 56,
} = 54,
CallKw {
nargs: Arg<u32>,
} = 57,
} = 55,
CompareOp {
op: Arg<ComparisonOperator>,
} = 58,
ContainsOp(Arg<Invert>) = 59,
} = 56,
ContainsOp(Arg<Invert>) = 57,
ConvertValue {
oparg: Arg<ConvertValueOparg>,
} = 60,
} = 58,
Copy {
index: Arg<u32>,
} = 61,
} = 59,
CopyFreeVars {
count: Arg<u32>,
} = 62, // Placeholder
} = 60, // Placeholder
DeleteAttr {
idx: Arg<NameIdx>,
} = 63,
DeleteDeref(Arg<NameIdx>) = 64,
DeleteFast(Arg<NameIdx>) = 65,
DeleteGlobal(Arg<NameIdx>) = 66,
DeleteName(Arg<NameIdx>) = 67,
} = 61,
DeleteDeref(Arg<NameIdx>) = 62,
DeleteFast(Arg<NameIdx>) = 63,
DeleteGlobal(Arg<NameIdx>) = 64,
DeleteName(Arg<NameIdx>) = 65,
DictMerge {
index: Arg<u32>,
} = 68, // Placeholder
} = 66,
DictUpdate {
index: Arg<u32>,
} = 69,
EnterExecutor = 70, // Placeholder
ExtendedArg = 71,
} = 67,
EndAsyncFor = 68,
ExtendedArg = 69,
ForIter {
target: Arg<Label>,
} = 72,
GetAwaitable = 73, // TODO: Make this instruction to hold an oparg
} = 70,
GetAwaitable = 71, // TODO: Make this instruction to hold an oparg
ImportFrom {
idx: Arg<NameIdx>,
} = 74,
} = 72,
ImportName {
idx: Arg<NameIdx>,
} = 75,
IsOp(Arg<Invert>) = 76,
} = 73,
IsOp(Arg<Invert>) = 74,
JumpBackward {
target: Arg<Label>,
} = 77,
} = 75,
JumpBackwardNoInterrupt {
target: Arg<Label>,
} = 78, // Placeholder
} = 76, // Placeholder
JumpForward {
target: Arg<Label>,
} = 79,
} = 77,
ListAppend {
i: Arg<u32>,
} = 80,
} = 78,
ListExtend {
i: Arg<u32>,
} = 81, // Placeholder
} = 79,
LoadAttr {
idx: Arg<NameIdx>,
} = 82,
} = 80,
LoadCommonConstant {
idx: Arg<u32>,
} = 81, // Placeholder
LoadConst {
idx: Arg<u32>,
} = 83,
LoadDeref(Arg<NameIdx>) = 84,
LoadFast(Arg<NameIdx>) = 85,
LoadFastAndClear(Arg<NameIdx>) = 86,
LoadFastCheck(Arg<NameIdx>) = 87, // Placeholder
} = 82,
LoadDeref(Arg<NameIdx>) = 83,
LoadFast(Arg<NameIdx>) = 84,
LoadFastAndClear(Arg<NameIdx>) = 85,
LoadFastBorrow(Arg<NameIdx>) = 86, // Placeholder
LoadFastBorrowLoadFastBorrow {
arg: Arg<u32>,
} = 87, // Placeholder
LoadFastCheck(Arg<NameIdx>) = 88, // Placeholder
LoadFastLoadFast {
arg: Arg<u32>,
} = 88, // Placeholder
LoadFromDictOrDeref(Arg<NameIdx>) = 89,
LoadFromDictOrGlobals(Arg<NameIdx>) = 90, // Placeholder
LoadGlobal(Arg<NameIdx>) = 91,
LoadName(Arg<NameIdx>) = 92,
} = 89, // Placeholder
LoadFromDictOrDeref(Arg<NameIdx>) = 90,
LoadFromDictOrGlobals(Arg<NameIdx>) = 91, // Placeholder
LoadGlobal(Arg<NameIdx>) = 92,
LoadName(Arg<NameIdx>) = 93,
LoadSmallInt {
idx: Arg<u32>,
} = 94, // Placeholder
LoadSpecial {
arg: Arg<u32>,
} = 95, // Placeholder
LoadSuperAttr {
arg: Arg<u32>,
} = 93,
MakeCell(Arg<NameIdx>) = 94, // Placeholder
} = 96,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

CPython 3.14 GET_AWAITABLE opcode oparg specification

💡 Result:

In CPython 3.14 the GET_AWAITABLE opcode takes an oparg "where" that indicates the context where the awaitable is produced:

  • 0: general (normal await).
  • 1: after a call to aenter (i.e. awaiting the result of aenter).
  • 2: after a call to aexit (i.e. awaiting the result of aexit).

(Documentation shows the same "where" operand meaning as in earlier versions.) [1][2]

Sources: Python dis documentation (3.14, 3.11). [1] https://docs.python.org/3.14/library/dis.html [2] https://docs.python.org/3.11/library/dis.html


Fix GetAwaitable to include oparg field for CPython 3.14 compatibility.

CPython 3.14's GET_AWAITABLE opcode requires an oparg ("where") that indicates the context (0=general, 1=after aenter, 2=after aexit). The current variant lacks this field, which will cause incorrect decoding and incomplete execution semantics. The TODO comment acknowledges this gap—implement the fix to match CPython's specification.

🤖 Prompt for AI Agents
In `@crates/compiler-core/src/bytecode/instruction.rs` around lines 17 - 199, The
GetAwaitable opcode in the Instruction enum is missing the required oparg
("where") per CPython 3.14; update the Instruction::GetAwaitable variant to
include an oparg field (e.g. GetAwaitable { oparg: Arg<u32> } or a small enum
type Arg<GetAwaitableWhere>) so decoding/encoding and semantics can read 0/1/2
contexts correctly; search for Instruction::GetAwaitable and adjust any places
that construct, pattern-match, encode/decode, or format Instruction to handle
the new oparg field (and add a small GetAwaitableWhere type if you prefer typed
values instead of raw u32).

Comment on lines 131 to +132
// Must be aligned to Lib/importlib/_bootstrap_external.py
pub const PYC_MAGIC_NUMBER: u16 = 2997;
pub const PYC_MAGIC_NUMBER: u16 = 2996;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

CPython 3.14 pyc magic number

💡 Result:

CPython 3.14 pyc magic number: 3627 (4-byte magic token = b'\x2b\x0e\x0d\x0a'). [1][2]

Sources:

  • Include/internal/pycore_magic_number.h (PYC_MAGIC_NUMBER = 3627). [1]
  • Explanation of pyc magic/token format (little-endian 2-byte number + CR LF). [2]

Change PYC_MAGIC_NUMBER to 3627 to match CPython 3.14.

Line 132 sets the value to 2996, but CPython 3.14's magic number is 3627 (defined in Include/internal/pycore_magic_number.h). Using the incorrect value will cause RustPython to misread .pyc files from CPython 3.14.

🤖 Prompt for AI Agents
In `@crates/vm/src/version.rs` around lines 131 - 132, The constant
PYC_MAGIC_NUMBER is set incorrectly to 2996; update the value of pub const
PYC_MAGIC_NUMBER in version.rs to 3627 so it matches CPython 3.14's magic number
(as defined in CPython's pycore_magic_number) to ensure .pyc files from CPython
3.14 are read correctly.

@youknowone youknowone merged commit 97167ab into RustPython:main Jan 19, 2026
13 checks passed
@youknowone youknowone deleted the instruction-3.14 branch January 19, 2026 18:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant