Skip to content

Commit 4107217

Browse files
authored
Oparg resume depth (#7515)
* Base resume context * Fixes for api change * Align codegen * Align `frame.rs` to the api changes * fix jit * Use new oparg * Fix doc * let `ir` to decide exception depth
1 parent e3ac1bf commit 4107217

File tree

7 files changed

+168
-111
lines changed

7 files changed

+168
-111
lines changed

crates/codegen/src/compile.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,7 +1277,7 @@ impl Compiler {
12771277
context: OpArgMarker::marker(),
12781278
}
12791279
.into(),
1280-
arg: OpArg::new(u32::from(bytecode::ResumeType::AtFuncStart)),
1280+
arg: OpArg::new(oparg::ResumeLocation::AtFuncStart.into()),
12811281
target: BlockIdx::NULL,
12821282
location,
12831283
end_location,
@@ -7200,9 +7200,9 @@ impl Compiler {
72007200
self,
72017201
Instruction::Resume {
72027202
context: if is_await {
7203-
bytecode::ResumeType::AfterAwait
7203+
oparg::ResumeContext::from(oparg::ResumeLocation::AfterAwait)
72047204
} else {
7205-
bytecode::ResumeType::AfterYieldFrom
7205+
oparg::ResumeContext::from(oparg::ResumeLocation::AfterYieldFrom)
72067206
}
72077207
}
72087208
);
@@ -7374,7 +7374,7 @@ impl Compiler {
73747374
emit!(
73757375
self,
73767376
Instruction::Resume {
7377-
context: bytecode::ResumeType::AfterYield
7377+
context: oparg::ResumeContext::from(oparg::ResumeLocation::AfterYield)
73787378
}
73797379
);
73807380
}
@@ -7596,7 +7596,9 @@ impl Compiler {
75967596
emit!(
75977597
compiler,
75987598
Instruction::Resume {
7599-
context: bytecode::ResumeType::AfterYield
7599+
context: oparg::ResumeContext::from(
7600+
oparg::ResumeLocation::AfterYield
7601+
)
76007602
}
76017603
);
76027604
emit!(compiler, Instruction::PopTop);

crates/codegen/src/ir.rs

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ impl CodeInfo {
363363
}
364364
}
365365

366-
let mut block_to_offset = vec![Label::new(0); blocks.len()];
366+
let mut block_to_offset = vec![Label::from_u32(0); blocks.len()];
367367
// block_to_index: maps block idx to instruction index (for exception table)
368368
// This is the index into the final instructions array, including EXTENDED_ARG and CACHE
369369
let mut block_to_index = vec![0u32; blocks.len()];
@@ -372,7 +372,7 @@ impl CodeInfo {
372372
loop {
373373
let mut num_instructions = 0;
374374
for (idx, block) in iter_blocks(&blocks) {
375-
block_to_offset[idx.idx()] = Label::new(num_instructions as u32);
375+
block_to_offset[idx.idx()] = Label::from_u32(num_instructions as u32);
376376
// block_to_index uses the same value as block_to_offset but as u32
377377
// because lasti in frame.rs is the index into instructions array
378378
// and instructions array index == byte offset (each instruction is 1 CodeUnit)
@@ -2188,20 +2188,19 @@ pub(crate) fn label_exception_targets(blocks: &mut [Block]) {
21882188
}
21892189

21902190
// Set RESUME DEPTH1 flag based on last yield's except depth
2191-
if matches!(
2192-
blocks[bi].instructions[i].instr.real(),
2193-
Some(Instruction::Resume { .. })
2194-
) {
2195-
const RESUME_AT_FUNC_START: u32 = 0;
2196-
const RESUME_OPARG_LOCATION_MASK: u32 = 0x3;
2197-
const RESUME_OPARG_DEPTH1_MASK: u32 = 0x4;
2198-
2199-
if (u32::from(arg) & RESUME_OPARG_LOCATION_MASK) != RESUME_AT_FUNC_START {
2200-
if last_yield_except_depth == 1 {
2201-
blocks[bi].instructions[i].arg =
2202-
OpArg::new(u32::from(arg) | RESUME_OPARG_DEPTH1_MASK);
2191+
if let Some(Instruction::Resume { context }) =
2192+
blocks[bi].instructions[i].instr.real()
2193+
{
2194+
let location = context.get(arg).location();
2195+
match location {
2196+
oparg::ResumeLocation::AtFuncStart => {}
2197+
_ => {
2198+
if last_yield_except_depth == 1 {
2199+
blocks[bi].instructions[i].arg =
2200+
OpArg::new(oparg::ResumeContext::new(location, true).as_u32());
2201+
}
2202+
last_yield_except_depth = -1;
22032203
}
2204-
last_yield_except_depth = -1;
22052204
}
22062205
}
22072206

crates/compiler-core/src/bytecode.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub use crate::bytecode::{
2727
BinaryOperator, BuildSliceArgCount, CommonConstant, ComparisonOperator, ConvertValueOparg,
2828
IntrinsicFunction1, IntrinsicFunction2, Invert, Label, LoadAttr, LoadSuperAttr,
2929
MakeFunctionFlag, MakeFunctionFlags, NameIdx, OpArg, OpArgByte, OpArgState, OpArgType,
30-
RaiseKind, ResumeType, SpecialMethod, UnpackExArgs,
30+
RaiseKind, SpecialMethod, UnpackExArgs,
3131
},
3232
};
3333

@@ -1041,7 +1041,7 @@ impl<C: Constant> CodeObject<C> {
10411041
}
10421042

10431043
// arrow and offset
1044-
let arrow = if label_targets.contains(&Label::new(offset as u32)) {
1044+
let arrow = if label_targets.contains(&Label::from_u32(offset as u32)) {
10451045
">>"
10461046
} else {
10471047
" "

crates/compiler-core/src/bytecode/instruction.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ pub enum Instruction {
304304
} = 120,
305305
// CPython 3.14 RESUME (128)
306306
Resume {
307-
context: Arg<oparg::ResumeType>,
307+
context: Arg<oparg::ResumeContext>,
308308
} = 128,
309309
// CPython 3.14 specialized opcodes (129-211)
310310
BinaryOpAddFloat = 129, // Placeholder

crates/compiler-core/src/bytecode/oparg.rs

Lines changed: 109 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -276,48 +276,6 @@ impl fmt::Display for ConvertValueOparg {
276276
}
277277
}
278278

279-
/// Resume type for the RESUME instruction
280-
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
281-
pub enum ResumeType {
282-
AtFuncStart,
283-
AfterYield,
284-
AfterYieldFrom,
285-
AfterAwait,
286-
Other(u32),
287-
}
288-
289-
impl From<u32> for ResumeType {
290-
fn from(value: u32) -> Self {
291-
match value {
292-
0 => Self::AtFuncStart,
293-
5 => Self::AfterYield,
294-
2 => Self::AfterYieldFrom,
295-
3 => Self::AfterAwait,
296-
_ => Self::Other(value),
297-
}
298-
}
299-
}
300-
301-
impl From<ResumeType> for u32 {
302-
fn from(typ: ResumeType) -> Self {
303-
match typ {
304-
ResumeType::AtFuncStart => 0,
305-
ResumeType::AfterYield => 5,
306-
ResumeType::AfterYieldFrom => 2,
307-
ResumeType::AfterAwait => 3,
308-
ResumeType::Other(v) => v,
309-
}
310-
}
311-
}
312-
313-
impl core::fmt::Display for ResumeType {
314-
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
315-
u32::from(*self).fmt(f)
316-
}
317-
}
318-
319-
impl OpArgType for ResumeType {}
320-
321279
pub type NameIdx = u32;
322280

323281
impl OpArgType for u32 {}
@@ -756,14 +714,8 @@ macro_rules! newtype_oparg {
756714
impl $name {
757715
/// Creates a new [`$name`] instance.
758716
#[must_use]
759-
pub const fn new(value: u32) -> Self {
760-
Self(value)
761-
}
762-
763-
/// Alias to [`$name::new`].
764-
#[must_use]
765717
pub const fn from_u32(value: u32) -> Self {
766-
Self::new(value)
718+
Self(value)
767719
}
768720

769721
/// Returns the oparg as a `u32` value.
@@ -843,15 +795,119 @@ newtype_oparg!(
843795
pub struct Label(u32)
844796
);
845797

798+
newtype_oparg!(
799+
/// Context for [`Instruction::Resume`].
800+
///
801+
/// The oparg consists of two parts:
802+
/// 1. [`ResumeContext::location`]: Indicates where the instruction occurs.
803+
/// 2. [`ResumeContext::is_exception_depth1`]: Is the instruction is at except-depth 1.
804+
#[derive(Clone, Copy)]
805+
#[repr(transparent)]
806+
pub struct ResumeContext(u32)
807+
);
808+
809+
impl ResumeContext {
810+
/// [CPython `RESUME_OPARG_LOCATION_MASK`](https://github.com/python/cpython/blob/v3.14.3/Include/internal/pycore_opcode_utils.h#L84)
811+
pub const LOCATION_MASK: u32 = 0x3;
812+
813+
/// [CPython `RESUME_OPARG_DEPTH1_MASK`](https://github.com/python/cpython/blob/v3.14.3/Include/internal/pycore_opcode_utils.h#L85)
814+
pub const DEPTH1_MASK: u32 = 0x4;
815+
816+
#[must_use]
817+
pub const fn new(location: ResumeLocation, is_exception_depth1: bool) -> Self {
818+
let value = if is_exception_depth1 {
819+
Self::DEPTH1_MASK
820+
} else {
821+
0
822+
};
823+
824+
Self::from_u32(location.as_u32() | value)
825+
}
826+
827+
/// Resume location is determined by [`Self::LOCATION_MASK`].
828+
#[must_use]
829+
pub fn location(&self) -> ResumeLocation {
830+
// SAFETY: The mask should return a value that is in range.
831+
unsafe { ResumeLocation::try_from(self.as_u32() & Self::LOCATION_MASK).unwrap_unchecked() }
832+
}
833+
834+
/// True if the bit at [`Self::DEPTH1_MASK`] is on.
835+
#[must_use]
836+
pub const fn is_exception_depth1(&self) -> bool {
837+
(self.as_u32() & Self::DEPTH1_MASK) != 0
838+
}
839+
}
840+
841+
#[derive(Copy, Clone)]
842+
pub enum ResumeLocation {
843+
/// At the start of a function, which is neither a generator, coroutine nor an async generator.
844+
AtFuncStart,
845+
/// After a `yield` expression.
846+
AfterYield,
847+
/// After a `yield from` expression.
848+
AfterYieldFrom,
849+
/// After an `await` expression.
850+
AfterAwait,
851+
}
852+
853+
impl From<ResumeLocation> for ResumeContext {
854+
fn from(location: ResumeLocation) -> Self {
855+
Self::new(location, false)
856+
}
857+
}
858+
859+
impl TryFrom<u32> for ResumeLocation {
860+
type Error = MarshalError;
861+
862+
fn try_from(value: u32) -> Result<Self, Self::Error> {
863+
Ok(match value {
864+
0 => Self::AtFuncStart,
865+
1 => Self::AfterYield,
866+
2 => Self::AfterYieldFrom,
867+
3 => Self::AfterAwait,
868+
_ => return Err(Self::Error::InvalidBytecode),
869+
})
870+
}
871+
}
872+
873+
impl ResumeLocation {
874+
#[must_use]
875+
pub const fn as_u8(&self) -> u8 {
876+
match self {
877+
Self::AtFuncStart => 0,
878+
Self::AfterYield => 1,
879+
Self::AfterYieldFrom => 2,
880+
Self::AfterAwait => 3,
881+
}
882+
}
883+
884+
#[must_use]
885+
pub const fn as_u32(&self) -> u32 {
886+
self.as_u8() as u32
887+
}
888+
}
889+
890+
impl From<ResumeLocation> for u8 {
891+
fn from(location: ResumeLocation) -> Self {
892+
location.as_u8()
893+
}
894+
}
895+
896+
impl From<ResumeLocation> for u32 {
897+
fn from(location: ResumeLocation) -> Self {
898+
location.as_u32()
899+
}
900+
}
901+
846902
impl VarNums {
847903
#[must_use]
848904
pub const fn idx_1(self) -> VarNum {
849-
VarNum::new(self.0 >> 4)
905+
VarNum::from_u32(self.0 >> 4)
850906
}
851907

852908
#[must_use]
853909
pub const fn idx_2(self) -> VarNum {
854-
VarNum::new(self.0 & 15)
910+
VarNum::from_u32(self.0 & 15)
855911
}
856912

857913
#[must_use]
@@ -887,7 +943,7 @@ impl LoadAttrBuilder {
887943
#[must_use]
888944
pub const fn build(self) -> LoadAttr {
889945
let value = (self.name_idx << 1) | (self.is_method as u32);
890-
LoadAttr::new(value)
946+
LoadAttr::from_u32(value)
891947
}
892948

893949
#[must_use]
@@ -937,7 +993,7 @@ impl LoadSuperAttrBuilder {
937993
pub const fn build(self) -> LoadSuperAttr {
938994
let value =
939995
(self.name_idx << 2) | ((self.has_class as u32) << 1) | (self.is_load_method as u32);
940-
LoadSuperAttr::new(value)
996+
LoadSuperAttr::from_u32(value)
941997
}
942998

943999
#[must_use]

crates/jit/src/instructions.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
162162
let target = after
163163
.checked_add(u32::from(arg))
164164
.ok_or(JitCompileError::BadBytecode)?;
165-
Ok(Label::new(target))
165+
Ok(Label::from_u32(target))
166166
}
167167

168168
fn jump_target_backward(
@@ -177,7 +177,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
177177
let target = after
178178
.checked_sub(u32::from(arg))
179179
.ok_or(JitCompileError::BadBytecode)?;
180-
Ok(Label::new(target))
180+
Ok(Label::from_u32(target))
181181
}
182182

183183
fn instruction_target(
@@ -232,7 +232,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
232232
let mut in_unreachable_code = false;
233233

234234
for (offset, &raw_instr) in clean_instructions.iter().enumerate() {
235-
let label = Label::new(offset as u32);
235+
let label = Label::from_u32(offset as u32);
236236
let (instruction, arg) = arg_state.get(raw_instr);
237237

238238
// If this is a label that some earlier jump can target,

0 commit comments

Comments
 (0)