Project private constant specializations in public adaptive bytes

Traverse immutable canonical boundaries and preserve arguments and cache cells. Retain public specialization IDs and reject the unsupported legacy iterator encoding. Add ordinary warmed and monitoring regressions.

Assisted-by: OpenAI Codex:model identifier unavailable
This commit is contained in:
dot committed 2026-10-12 08:58:14 +09:00
1 parent cafc576332
commit b33adafe86
4 files changed
+158 -10

No files matched your search

+7
View File
@@ -807,6 +807,13 @@ impl CodeUnits {
}
}
/// Number of code units, without borrowing the live mutable instruction slice.
/// Constructors and Clone keep the out-of-line counter array at this length.
#[must_use]
pub fn unit_count(&self) -> usize {
self.adaptive_counters.len()
}
/// Replace the opcode at `index` in-place without changing the arg byte.
/// Uses atomic Release store to ensure prior cache writes are visible
/// to threads that subsequently read the new opcode with Acquire.
+40 -8
View File
@@ -1198,15 +1198,47 @@ impl Py<PyCode> {
}
#[pygetset]
pub fn _co_code_adaptive(&self, vm: &VirtualMachine) -> crate::builtins::PyBytesRef {
// Return current (possibly quickened/specialized) bytecode
let bytes = unsafe {
core::slice::from_raw_parts(
self.code.instructions.as_ptr() as *const u8,
self.code.instructions.len() * 2,
)
pub fn _co_code_adaptive(&self, vm: &VirtualMachine) -> PyResult<PyBytesRef> {
let incompatible = || vm.new_value_error("incompatible adaptive bytecode layout");
let legacy =
|| vm.new_value_error("legacy private GET_YIELD_FROM_ITER has no public 3.15 encoding");
let (canonical, []) = self.canonical_code.as_chunks::<2>() else {
return Err(incompatible());
};
vm.ctx.new_bytes(bytes.to_vec())
let instructions = &self.code.instructions;
if canonical.len() != instructions.unit_count() {
return Err(incompatible());
}
let mut bytes = Vec::with_capacity(self.canonical_code.len());
let mut index = 0;
while index < canonical.len() {
let base = Instruction::try_from(canonical[index][0]).map_err(|_| incompatible())?;
if matches!(base, Instruction::GetYieldFromIter) {
return Err(legacy());
}
let next = index
.checked_add(1 + base.cache_entries())
.filter(|&next| next <= canonical.len())
.ok_or_else(incompatible)?;
// Boundaries come only from this code's immutable canonical backing.
// Never decode a cache payload or use a live monitoring wrapper's width.
let op = match instructions.read_op(index) {
op @ (Instruction::LoadConstImmortal | Instruction::LoadConstMortal) => {
op.deoptimize()
}
Instruction::GetYieldFromIter => return Err(legacy()),
op => op,
};
bytes.push(u8::from(op));
bytes.push(u8::from(instructions.read_arg(index)));
for cache in index + 1..next {
bytes.extend_from_slice(&instructions.read_cache_u16(cache).to_ne_bytes());
}
index = next;
}
// Per-cell atomic observation, not a coherent cross-thread snapshot.
// This does not repair the interpreter's other live-storage access paths.
Ok(vm.ctx.new_bytes(bytes))
}
#[pygetset]
@@ -0,0 +1,101 @@
"""Public adaptive bytes preserve layout without exposing private constant opcodes."""
import dis
import marshal
import opcode
import sys
FILENAME = "adaptive_public_export.py"
CONSTANT_SOURCE = (
'def warm_const():\n return ("ordinary constant payload", 123456789)\n'
)
LOOP_SOURCE = "def total(n):\n result = 0\n for value in range(n):\n result += value\n return result\n"
PUBLIC = set(opcode.opmap.values()) | set(opcode._specialized_opmap.values())
def make(source, name):
namespace = {}
exec(compile(source, FILENAME, "exec"), namespace)
return namespace[name]
def check_layout(code, canonical, offsets):
adaptive = code._co_code_adaptive
assert len(adaptive) == len(canonical)
for offset in offsets:
assert adaptive[offset] in PUBLIC, (offset, adaptive[offset])
assert adaptive[offset] not in (219, 220, 221)
assert adaptive[offset + 1] == canonical[offset + 1]
assert code.co_code == canonical
return adaptive
function = make(CONSTANT_SOURCE, "warm_const")
code = function.__code__
canonical = code.co_code
offsets = tuple(i.offset for i in dis.get_instructions(code))
cold = check_layout(code, canonical, offsets)
constant_offset = next(
i.offset for i in dis.get_instructions(code) if i.opname == "LOAD_CONST"
)
assert cold[constant_offset] == opcode.opmap["LOAD_CONST"]
before = marshal.dumps(code, 6)
for _ in range(1000):
assert function() == ("ordinary constant payload", 123456789)
assert marshal.dumps(code, 6) == before
adaptive = check_layout(code, canonical, offsets)
constants = [
i for i in dis.get_instructions(code, adaptive=True) if i.opname == "LOAD_CONST"
]
assert len(constants) == 1
assert constants[0].argval == ("ordinary constant payload", 123456789)
assert adaptive[constants[0].offset] == opcode.opmap["LOAD_CONST"]
loop = make(LOOP_SOURCE, "total")
code = loop.__code__
canonical = code.co_code
offsets = tuple(i.offset for i in dis.get_instructions(code))
for _ in range(1000):
assert loop(10) == 45
first = check_layout(code, canonical, offsets)
second = check_layout(code, canonical, offsets)
assert first == second
assert any(i.cache_info for i in dis.get_instructions(code))
rows = tuple(dis.get_instructions(code, adaptive=True))
assert tuple(i.offset for i in rows) == offsets
assert any(i.opcode in opcode._specialized_opmap.values() for i in rows)
assert all(first[i.offset] == i.opcode for i in rows)
monitoring = sys.monitoring
available = [tool for tool in range(6) if monitoring.get_tool(tool) is None]
assert available
TOOL = available[0]
monitoring.use_tool_id(TOOL, "adaptive-public-export")
try:
for event in (monitoring.events.LINE, monitoring.events.INSTRUCTION):
function = make(LOOP_SOURCE, "total")
code = function.__code__
canonical = code.co_code
offsets = tuple(i.offset for i in dis.get_instructions(code))
observed = []
def inspect(actual_code, location):
assert actual_code is code
if not observed:
# First adaptive export for this code happens under active wrappers.
observed.append(check_layout(actual_code, canonical, offsets))
assert marshal.loads(marshal.dumps(actual_code, 6)).co_code == canonical
monitoring.register_callback(TOOL, event, inspect)
monitoring.set_local_events(TOOL, code, event)
try:
assert function(10) == 45
assert len(observed) == 1
finally:
monitoring.set_local_events(TOOL, code, 0)
monitoring.register_callback(TOOL, event, None)
check_layout(code, canonical, offsets)
assert function(5) == 10
finally:
monitoring.set_events(TOOL, 0)
monitoring.free_tool_id(TOOL)
@@ -53,8 +53,16 @@ they do not alter the three strict comparison domains.
Canonical `co_code` uses immutable prepublication bytes and a stable bytes owner;
the common marshal writer uses that actual field rather than live monitoring
instructions. The adaptive getter remains a separate gap: private Rust
`LOAD_CONST` specializations can appear as unknown IDs in canonical `dis`.
instructions. The adaptive getter projects private Rust `LOAD_CONST` IDs220/221
to public `LOAD_CONST` at immutable canonical instruction boundaries, preserving
arguments, public specialization IDs and cache payload bytes. Legacy private
ID219 has no equivalent one-unit public encoding and raises `ValueError` on
adaptive export. Length and truncated canonical cache spans are checked; arbitrary
same-length postpublication embedding mutation remains outside the finalized-code
contract. Per-cell atomic reads do not establish a coherent concurrent snapshot
or repair inherited shared-storage accessor and mixed-width dispatch concerns.
The projection neither mutates executable instructions nor changes canonical or
marshal bytes. Runtime specialization choices need not match CPython.
Retained private `GET_YIELD_FROM_ITER` must never be emitted by the compiler.
The current local tree includes the separately reviewed builtin `frozendict`
prerequisite and exact final `Lib/opcode.py`, `Lib/_opcode_metadata.py`, and