Newtype oparg align methods (#7403)

* Align methods for newtype opargs
This commit is contained in:
Shahar Naveh authored and GitHub committed 2026-03-12 23:41:21 +09:00
1 parent c578ac0b21
commit bb36783206
6 files changed
+180 -245

No files matched your search

+4 -4
View File
@@ -342,7 +342,7 @@ impl CodeInfo {
}
}
let mut block_to_offset = vec![Label(0); blocks.len()];
let mut block_to_offset = vec![Label::new(0); blocks.len()];
// block_to_index: maps block idx to instruction index (for exception table)
// This is the index into the final instructions array, including EXTENDED_ARG and CACHE
let mut block_to_index = vec![0u32; blocks.len()];
@@ -351,7 +351,7 @@ impl CodeInfo {
loop {
let mut num_instructions = 0;
for (idx, block) in iter_blocks(&blocks) {
block_to_offset[idx.idx()] = Label(num_instructions as u32);
block_to_offset[idx.idx()] = Label::new(num_instructions as u32);
// block_to_index uses the same value as block_to_offset but as u32
// because lasti in frame.rs is the index into instructions array
// and instructions array index == byte offset (each instruction is 1 CodeUnit)
@@ -369,7 +369,7 @@ impl CodeInfo {
while next_block != BlockIdx::NULL {
let block = &mut blocks[next_block];
// Track current instruction offset for jump direction resolution
let mut current_offset = block_to_offset[next_block.idx()].0;
let mut current_offset = block_to_offset[next_block.idx()].as_u32();
for info in &mut block.instructions {
let target = info.target;
let mut op = info.instr.expect_real();
@@ -380,7 +380,7 @@ impl CodeInfo {
let offset_after = current_offset + old_arg_size as u32 + old_cache_entries;
if target != BlockIdx::NULL {
let target_offset = block_to_offset[target.idx()].0;
let target_offset = block_to_offset[target.idx()].as_u32();
// Direction must be based on concrete instruction offsets.
// Empty blocks can share offsets, so block-order-based resolution
// may classify some jumps incorrectly.
+1 -1
View File
@@ -1024,7 +1024,7 @@ impl<C: Constant> CodeObject<C> {
}
// arrow and offset
let arrow = if label_targets.contains(&Label(offset as u32)) {
let arrow = if label_targets.contains(&Label::new(offset as u32)) {
">>"
} else {
" "
@@ -1105,13 +1105,7 @@ impl InstructionMetadata for Instruction {
};
($variant:ident, $map:ident = $arg_marker:expr) => {{
let arg = $arg_marker.get(arg);
write!(
f,
"{:pad$}({}, {})",
stringify!($variant),
u32::from(arg),
$map(arg)
)
write!(f, "{:pad$}({}, {})", stringify!($variant), arg, $map(arg))
}};
($variant:ident, $arg_marker:expr) => {
write!(f, "{:pad$}({})", stringify!($variant), $arg_marker.get(arg))
+149 -208
View File
@@ -293,77 +293,6 @@ pub type NameIdx = u32;
impl OpArgType for u32 {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[repr(transparent)]
pub struct Label(pub u32);
impl Label {
pub const fn new(value: u32) -> Self {
Self(value)
}
}
impl From<u32> for Label {
fn from(value: u32) -> Self {
Self::new(value)
}
}
impl From<Label> for u32 {
fn from(value: Label) -> Self {
value.0
}
}
impl OpArgType for Label {}
impl fmt::Display for Label {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[repr(transparent)]
pub struct StoreFastLoadFast(u32);
impl StoreFastLoadFast {
#[must_use]
pub const fn new(value: u32) -> Self {
Self(value)
}
#[must_use]
pub const fn store_idx(self) -> NameIdx {
self.0 >> 4
}
#[must_use]
pub const fn load_idx(self) -> NameIdx {
self.0 & 15
}
}
impl From<u32> for StoreFastLoadFast {
fn from(value: u32) -> Self {
Self::new(value)
}
}
impl From<StoreFastLoadFast> for u32 {
fn from(value: StoreFastLoadFast) -> Self {
value.0
}
}
impl OpArgType for StoreFastLoadFast {}
impl fmt::Display for StoreFastLoadFast {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
oparg_enum!(
/// The kind of Raise that occurred.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -724,15 +653,159 @@ impl fmt::Display for UnpackExArgs {
}
}
#[derive(Clone, Copy)]
pub struct LoadSuperAttr(u32);
macro_rules! newtype_oparg {
(
$(#[$oparg_meta:meta])*
$vis:vis struct $name:ident(u32)
) => {
$(#[$oparg_meta])*
$vis struct $name(u32);
impl LoadSuperAttr {
impl $name {
/// Creates a new [`$name`] instance.
#[must_use]
pub const fn new(value: u32) -> Self {
Self(value)
}
/// Alias to [`$name::new`].
#[must_use]
pub const fn from_u32(value: u32) -> Self {
Self::new(value)
}
/// Returns the oparg as a `u32` value.
#[must_use]
pub const fn as_u32(self) -> u32 {
self.0
}
/// Returns the oparg as a `usize` value.
#[must_use]
pub const fn as_usize(self) -> usize {
self.0 as usize
}
}
impl From<u32> for $name {
fn from(value: u32) -> Self {
Self::from_u32(value)
}
}
impl From<$name> for u32 {
fn from(value: $name) -> Self {
value.as_u32()
}
}
impl From<$name> for usize {
fn from(value: $name) -> Self {
value.as_usize()
}
}
impl ::core::fmt::Display for $name {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
self.0.fmt(f)
}
}
impl OpArgType for $name {}
}
}
newtype_oparg!(
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct ConstIdx(u32)
);
newtype_oparg!(
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct VarNum(u32)
);
newtype_oparg!(
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct LoadAttr(u32)
);
newtype_oparg!(
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct LoadSuperAttr(u32)
);
newtype_oparg!(
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[repr(transparent)]
pub struct Label(u32)
);
newtype_oparg!(
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[repr(transparent)]
pub struct StoreFastLoadFast(u32)
);
impl StoreFastLoadFast {
#[must_use]
pub const fn new(value: u32) -> Self {
Self(value)
pub const fn store_idx(self) -> NameIdx {
self.0 >> 4
}
#[must_use]
pub const fn load_idx(self) -> NameIdx {
self.0 & 15
}
}
impl LoadAttr {
#[must_use]
pub fn builder() -> LoadAttrBuilder {
LoadAttrBuilder::default()
}
#[must_use]
pub const fn name_idx(self) -> u32 {
self.0 >> 1
}
#[must_use]
pub const fn is_method(self) -> bool {
(self.0 & 1) == 1
}
}
#[derive(Clone, Copy, Default)]
pub struct LoadAttrBuilder {
name_idx: u32,
is_method: bool,
}
impl LoadAttrBuilder {
#[must_use]
pub const fn build(self) -> LoadAttr {
let value = (self.name_idx << 1) | (self.is_method as u32);
LoadAttr::new(value)
}
#[must_use]
pub const fn name_idx(mut self, value: u32) -> Self {
self.name_idx = value;
self
}
#[must_use]
pub const fn is_method(mut self, value: bool) -> Self {
self.is_method = value;
self
}
}
impl LoadSuperAttr {
#[must_use]
pub fn builder() -> LoadSuperAttrBuilder {
LoadSuperAttrBuilder::default()
@@ -754,20 +827,6 @@ impl LoadSuperAttr {
}
}
impl OpArgType for LoadSuperAttr {}
impl From<u32> for LoadSuperAttr {
fn from(value: u32) -> Self {
Self::new(value)
}
}
impl From<LoadSuperAttr> for u32 {
fn from(value: LoadSuperAttr) -> Self {
value.0
}
}
#[derive(Clone, Copy, Default)]
pub struct LoadSuperAttrBuilder {
name_idx: u32,
@@ -807,121 +866,3 @@ impl From<LoadSuperAttrBuilder> for LoadSuperAttr {
builder.build()
}
}
#[derive(Clone, Copy)]
pub struct LoadAttr(u32);
impl LoadAttr {
#[must_use]
pub const fn new(value: u32) -> Self {
Self(value)
}
#[must_use]
pub fn builder() -> LoadAttrBuilder {
LoadAttrBuilder::default()
}
#[must_use]
pub const fn name_idx(self) -> u32 {
self.0 >> 1
}
#[must_use]
pub const fn is_method(self) -> bool {
(self.0 & 1) == 1
}
}
impl OpArgType for LoadAttr {}
impl From<u32> for LoadAttr {
fn from(value: u32) -> Self {
Self::new(value)
}
}
impl From<LoadAttr> for u32 {
fn from(value: LoadAttr) -> Self {
value.0
}
}
#[derive(Clone, Copy, Default)]
pub struct LoadAttrBuilder {
name_idx: u32,
is_method: bool,
}
impl LoadAttrBuilder {
#[must_use]
pub const fn build(self) -> LoadAttr {
let value = (self.name_idx << 1) | (self.is_method as u32);
LoadAttr::new(value)
}
#[must_use]
pub const fn name_idx(mut self, value: u32) -> Self {
self.name_idx = value;
self
}
#[must_use]
pub const fn is_method(mut self, value: bool) -> Self {
self.is_method = value;
self
}
}
macro_rules! newtype_oparg {
(
$(#[$oparg_meta:meta])*
$vis:vis struct $name:ident(u32)
) => {
$(#[$oparg_meta])*
$vis struct $name(u32);
impl $name {
#[must_use]
pub const fn from_u32(value: u32) -> Self {
Self(value)
}
/// Returns the oparg as a `u32` value.
#[must_use]
pub const fn as_u32(self) -> u32 {
self.0
}
/// Returns the oparg as a `usize` value.
#[must_use]
pub const fn as_usize(self) -> usize {
self.0 as usize
}
}
impl From<u32> for $name {
fn from(value: u32) -> Self {
Self::from_u32(value)
}
}
impl From<$name> for u32 {
fn from(value: $name) -> Self {
value.as_u32()
}
}
impl OpArgType for $name {}
}
}
newtype_oparg!(
#[derive(Clone, Copy)]
pub struct ConstIdx(u32)
);
newtype_oparg!(
#[derive(Clone, Copy)]
pub struct VarNum(u32)
);
+3 -3
View File
@@ -162,7 +162,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
let target = after
.checked_add(u32::from(arg))
.ok_or(JitCompileError::BadBytecode)?;
Ok(Label(target))
Ok(Label::new(target))
}
fn jump_target_backward(
@@ -177,7 +177,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
let target = after
.checked_sub(u32::from(arg))
.ok_or(JitCompileError::BadBytecode)?;
Ok(Label(target))
Ok(Label::new(target))
}
fn instruction_target(
@@ -232,7 +232,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
let mut in_unreachable_code = false;
for (offset, &raw_instr) in clean_instructions.iter().enumerate() {
let label = Label(offset as u32);
let label = Label::new(offset as u32);
let (instruction, arg) = arg_state.get(raw_instr);
// If this is a label that some earlier jump can target,
+22 -22
View File
@@ -2311,7 +2311,7 @@ impl ExecutingFrame<'_> {
}
Instruction::ForIter { .. } => {
// Relative forward jump: target = lasti + caches + delta
let target = bytecode::Label(self.lasti() + 1 + u32::from(arg));
let target = bytecode::Label::new(self.lasti() + 1 + u32::from(arg));
self.adaptive(|s, ii, cb| s.specialize_for_iter(vm, u32::from(arg), ii, cb));
self.execute_for_iter(vm, target)?;
Ok(None)
@@ -3440,7 +3440,7 @@ impl ExecutingFrame<'_> {
Instruction::Send { .. } => {
// (receiver, v -- receiver, retval)
self.adaptive(|s, ii, cb| s.specialize_send(vm, ii, cb));
let exit_label = bytecode::Label(self.lasti() + 1 + u32::from(arg));
let exit_label = bytecode::Label::new(self.lasti() + 1 + u32::from(arg));
let receiver = self.nth_value(1);
let can_fast_send = !self.specialization_eval_frame_active(vm)
&& (receiver.downcast_ref_if_exact::<PyGenerator>(vm).is_some()
@@ -3478,7 +3478,7 @@ impl ExecutingFrame<'_> {
}
}
Instruction::SendGen => {
let exit_label = bytecode::Label(self.lasti() + 1 + u32::from(arg));
let exit_label = bytecode::Label::new(self.lasti() + 1 + u32::from(arg));
// Stack: [receiver, val] — peek receiver before popping
let receiver = self.nth_value(1);
let can_fast_send = !self.specialization_eval_frame_active(vm)
@@ -5450,7 +5450,7 @@ impl ExecutingFrame<'_> {
self.unpack_sequence(size as u32, vm)
}
Instruction::ForIterRange => {
let target = bytecode::Label(self.lasti() + 1 + u32::from(arg));
let target = bytecode::Label::new(self.lasti() + 1 + u32::from(arg));
let iter = self.top_value();
if let Some(range_iter) = iter.downcast_ref_if_exact::<PyRangeIterator>(vm) {
if let Some(value) = range_iter.fast_next() {
@@ -5465,7 +5465,7 @@ impl ExecutingFrame<'_> {
}
}
Instruction::ForIterList => {
let target = bytecode::Label(self.lasti() + 1 + u32::from(arg));
let target = bytecode::Label::new(self.lasti() + 1 + u32::from(arg));
let iter = self.top_value();
if let Some(list_iter) = iter.downcast_ref_if_exact::<PyListIterator>(vm) {
if let Some(value) = list_iter.fast_next() {
@@ -5480,7 +5480,7 @@ impl ExecutingFrame<'_> {
}
}
Instruction::ForIterTuple => {
let target = bytecode::Label(self.lasti() + 1 + u32::from(arg));
let target = bytecode::Label::new(self.lasti() + 1 + u32::from(arg));
let iter = self.top_value();
if let Some(tuple_iter) = iter.downcast_ref_if_exact::<PyTupleIterator>(vm) {
if let Some(value) = tuple_iter.fast_next() {
@@ -5495,7 +5495,7 @@ impl ExecutingFrame<'_> {
}
}
Instruction::ForIterGen => {
let target = bytecode::Label(self.lasti() + 1 + u32::from(arg));
let target = bytecode::Label::new(self.lasti() + 1 + u32::from(arg));
let iter = self.top_value();
if self.specialization_eval_frame_active(vm) {
self.execute_for_iter(vm, target)?;
@@ -5737,26 +5737,26 @@ impl ExecutingFrame<'_> {
Instruction::InstrumentedJumpForward => {
let src_offset = (self.lasti() - 1) * 2;
let target_idx = self.lasti() + u32::from(arg);
let target = bytecode::Label(target_idx);
let target = bytecode::Label::new(target_idx);
self.jump(target);
if self.monitoring_mask & monitoring::EVENT_JUMP != 0 {
monitoring::fire_jump(vm, self.code, src_offset, target.0 * 2)?;
monitoring::fire_jump(vm, self.code, src_offset, target.as_u32() * 2)?;
}
Ok(None)
}
Instruction::InstrumentedJumpBackward => {
let src_offset = (self.lasti() - 1) * 2;
let target_idx = self.lasti() + 1 - u32::from(arg);
let target = bytecode::Label(target_idx);
let target = bytecode::Label::new(target_idx);
self.jump(target);
if self.monitoring_mask & monitoring::EVENT_JUMP != 0 {
monitoring::fire_jump(vm, self.code, src_offset, target.0 * 2)?;
monitoring::fire_jump(vm, self.code, src_offset, target.as_u32() * 2)?;
}
Ok(None)
}
Instruction::InstrumentedForIter => {
let src_offset = (self.lasti() - 1) * 2;
let target = bytecode::Label(self.lasti() + 1 + u32::from(arg));
let target = bytecode::Label::new(self.lasti() + 1 + u32::from(arg));
let continued = self.execute_for_iter(vm, target)?;
if continued {
if self.monitoring_mask & monitoring::EVENT_BRANCH_LEFT != 0 {
@@ -5806,7 +5806,7 @@ impl ExecutingFrame<'_> {
let obj = self.pop_value();
let value = obj.try_to_bool(vm)?;
if value {
self.jump(bytecode::Label(target_idx));
self.jump(bytecode::Label::new(target_idx));
if self.monitoring_mask & monitoring::EVENT_BRANCH_RIGHT != 0 {
monitoring::fire_branch_right(vm, self.code, src_offset, target_idx * 2)?;
}
@@ -5819,7 +5819,7 @@ impl ExecutingFrame<'_> {
let obj = self.pop_value();
let value = obj.try_to_bool(vm)?;
if !value {
self.jump(bytecode::Label(target_idx));
self.jump(bytecode::Label::new(target_idx));
if self.monitoring_mask & monitoring::EVENT_BRANCH_RIGHT != 0 {
monitoring::fire_branch_right(vm, self.code, src_offset, target_idx * 2)?;
}
@@ -5831,7 +5831,7 @@ impl ExecutingFrame<'_> {
let target_idx = self.lasti() + 1 + u32::from(arg);
let value = self.pop_value();
if vm.is_none(&value) {
self.jump(bytecode::Label(target_idx));
self.jump(bytecode::Label::new(target_idx));
if self.monitoring_mask & monitoring::EVENT_BRANCH_RIGHT != 0 {
monitoring::fire_branch_right(vm, self.code, src_offset, target_idx * 2)?;
}
@@ -5843,7 +5843,7 @@ impl ExecutingFrame<'_> {
let target_idx = self.lasti() + 1 + u32::from(arg);
let value = self.pop_value();
if !vm.is_none(&value) {
self.jump(bytecode::Label(target_idx));
self.jump(bytecode::Label::new(target_idx));
if self.monitoring_mask & monitoring::EVENT_BRANCH_RIGHT != 0 {
monitoring::fire_branch_right(vm, self.code, src_offset, target_idx * 2)?;
}
@@ -6235,7 +6235,7 @@ impl ExecutingFrame<'_> {
self.push_value(exception.into());
// 4. Jump to handler
self.jump(bytecode::Label(entry.target));
self.jump(bytecode::Label::new(entry.target));
Ok(None)
} else {
@@ -6741,7 +6741,7 @@ impl ExecutingFrame<'_> {
#[inline]
fn jump(&mut self, label: bytecode::Label) {
let target_pc = label.0;
let target_pc = label.as_u32();
vm_trace!("jump from {:?} to {:?}", self.lasti(), target_pc);
self.update_lasti(|i| *i = target_pc);
}
@@ -6833,14 +6833,14 @@ impl ExecutingFrame<'_> {
/// Compute the jump target for FOR_ITER exhaustion: skip END_FOR and jump to POP_ITER.
fn for_iter_jump_target(&self, target: bytecode::Label) -> bytecode::Label {
let target_idx = target.0 as usize;
let target_idx = target.as_usize();
if let Some(unit) = self.code.instructions.get(target_idx)
&& matches!(
unit.op,
bytecode::Instruction::EndFor | bytecode::Instruction::InstrumentedEndFor
)
{
return bytecode::Label(target.0 + 1);
return bytecode::Label::new(target.as_u32() + 1);
}
target
}
@@ -8815,13 +8815,13 @@ impl ExecutingFrame<'_> {
/// Handle iterator exhaustion in specialized FOR_ITER handlers.
/// Skips END_FOR if present at target and jumps.
fn for_iter_jump_on_exhausted(&mut self, target: bytecode::Label) {
let target_idx = target.0 as usize;
let target_idx = target.as_usize();
let jump_target = if let Some(unit) = self.code.instructions.get(target_idx) {
if matches!(
unit.op,
bytecode::Instruction::EndFor | bytecode::Instruction::InstrumentedEndFor
) {
bytecode::Label(target.0 + 1)
bytecode::Label::new(target.as_u32() + 1)
} else {
target
}