8000
  • Make use of `Unary` opcodes by ShaharNaveh · Pull Request #6647 · RustPython/RustPython · GitHub
    [go: up one dir, main page]

    Skip to content

    Make use of Unary opcodes#6647

    Merged
    youknowone merged 10 commits intoRustPython:mainfrom
    ShaharNaveh:bytecode-split-unary
    Jan 5, 2026
    Merged

    Make use of Unary opcodes#6647
    youknowone merged 10 commits intoRustPython:mainfrom
    ShaharNaveh:bytecode-split-unary

    Conversation

    @ShaharNaveh
    Copy link
    Contributor
    @ShaharNaveh ShaharNaveh commented Jan 4, 2026

    Summary by CodeRabbit

    • Refactor

      • Unary operations (plus, minus, invert, not) are now separate, explicit instructions rather than a single generic unary instruction, improving execution consistency across runtimes.
      • Unary plus is routed through a dedicated intrinsic path for uniform behavior.
    • Documentation / Tests

      • Tests and docs updated to reflect the explicit unary variants and revised runtime behavior.

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

    @coderabbitai
    Copy link
    Contributor
    coderabbitai bot commented Jan 4, 2026
    📝 Walkthrough

    Walkthrough

    Replaces the previous generic unary dispatch with explicit per-variant bytecode and intrinsic instructions across codegen, bytecode, VM, and JIT: UnaryPositive, UnaryNegative, UnaryInvert, and UnaryNot. Not now emits ToBool then UnaryNot.

    Changes

    Cohort / File(s) Summary
    Code generation for expressions
    crates/codegen/src/compile.rs
    Emit explicit ops instead of a generic unary: UAddCallIntrinsic1(UnaryPositive), USubUnaryNegative, InvertUnaryInvert, NotToBool + UnaryNot.
    Bytecode instruction definitions
    crates/compiler-core/src/bytecode.rs
    Add UnaryPositive to IntrinsicFunction1; remove UnaryOperator/UnaryOperation; introduce explicit UnaryInvert/UnaryNegative/UnaryNot instruction variants; update encoding/decoding, stack effects, display, reserved padding, tests/docs.
    VM instruction execution
    crates/vm/src/frame.rs
    Remove execute_unary_op dispatch; add dedicated UnaryInvert/UnaryNegative/UnaryNot arms; route unary plus via CallIntrinsic1(UnaryPositive)vm._pos(&arg); adjust push/pop and intrinsic call paths.
    JIT instruction handling
    crates/jit/src/instructions.rs
    Replace UnaryOperation handling with ToBool, UnaryNot, and UnaryNegative branches; implement boolean-negation and int-negation/invert paths; support CallIntrinsic1(UnaryPositive); update imports to use IntrinsicFunction1.

    Sequence Diagram(s)

    sequenceDiagram
        autonumber
        participant CG as Codegen
        participant BC as Bytecode
        participant VM as VM
        participant JIT as JIT
    
        rect rgb(235,245,255)
        Note over CG,BC: Codegen emits explicit unary opcodes/intrinsics
        end
    
        CG->>BC: Emit CallIntrinsic1(UnaryPositive) / UnaryNegative / UnaryInvert / ToBool + UnaryNot
        BC->>VM: Bytecode stream (explicit unary opcodes)
        VM->>JIT: Request compiled handler or fallback to interpreter
    
        alt UnaryPositive (UAdd)
            VM->>VM: CallIntrinsic1 handler → vm._pos(arg)
            VM->>VM: Push result
        else UnaryNegative
            VM->>VM: Pop operand → apply negation → Push result
        else UnaryInvert
            VM->>VM: Pop operand → apply bitwise invert (int) or NotSupported → Push result
        else Not (ToBool + UnaryNot)
            VM->>VM: Execute ToBool → UnaryNot (bool flip) → Push result
        end
    
        rect rgb(245,245,245)
        Note over VM,JIT: Generic UnaryOperation dispatch removed
        end
    
    Loading

    Estimated code review effort

    🎯 4 (Complex) | ⏱️ ~45 minutes

    Possibly related PRs

    Suggested reviewers

    • youknowone

    Poem

    🐰 I hopped through opcodes, swift and spry,

    Split the unaries, one by one they fly,

    • calls pos, - and ~ find their lane,
      not first converts then flips again,
      🥕 tiny hops, the bytecode sings.

    Pre-merge checks and finishing touches

    ✅ Passed checks (3 passed)
    Check name Status Explanation
    Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
    Title check ✅ Passed The title 'Make use of Unary opcodes' directly and accurately reflects the main change: replacing a generic unary operation dispatch with explicit unary opcodes (UnaryPositive, UnaryNegative, UnaryNot, UnaryInvert) across the codebase.
    Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
    ✨ Finishing touches
    • 📝 Generate docstrings

    📜 Recent review details

    Configuration used: Path: .coderabbit.yml

    Review profile: CHILL

    Plan: Pro

    📥 Commits

    Reviewing files that changed from the base of the PR and between 50763ce and 0696124.

    📒 Files selected for processing (1)
    • crates/jit/src/instructions.rs
    🧰 Additional context used
    📓 Path-based instructions (1)
    **/*.rs

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    **/*.rs: Follow the default rustfmt code style by running cargo fmt to format Rust code
    Always run clippy to lint code (cargo clippy) before completing tasks and fix any warnings or lints introduced by changes
    Follow Rust best practices for error handling and memory management
    Use the macro system (pyclass, pymodule, pyfunction, etc.) when implementing Python functionality in Rust

    Files:

    • crates/jit/src/instructions.rs
    ⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
    • GitHub Check: Run snippets and cpython tests (windows-latest)
    • GitHub Check: Ensure compilation on various targets
    • GitHub Check: Run rust tests (windows-latest)
    • GitHub Check: Run snippets and cpython tests (macos-latest)
    • GitHub Check: Run rust tests (macos-latest)
    • GitHub Check: Run rust tests (ubuntu-latest)
    • GitHub Check: Run snippets and cpython tests on wasm-wasi
    • GitHub Check: Check the WASM package and demo
    • GitHub Check: Check Rust code with clippy
    • GitHub Check: Run snippets and cpython tests (ubuntu-latest)
    • GitHub Check: Run tests under miri
    🔇 Additional comments (3)
    crates/jit/src/instructions.rs (3)

    6-9: LGTM!

    The import of IntrinsicFunction1 is correctly added to support the new CallIntrinsic1 instruction handling.


    638-643: LGTM!

    The ToBool instruction correctly delegates to boolean_val, which already handles Int, Float, Bool, and None types appropriately.


    644-652: Fix confirmed: Type mismatch resolved.

    The previous critical issue has been addressed. The code now correctly extracts the Cranelift Value from JitValue::Bool(val) before passing it to bxor_imm. The use of BadBytecode for non-Bool types is appropriate since ToBool is expected to precede UnaryNot in the bytecode sequence.


    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.

    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: 1

    🧹 Nitpick comments (2)
    crates/codegen/src/compile.rs (1)

    5659-5672: Unary op emission matches new opcode/intrinsic split

    The new mapping looks consistent with the refactor: UAdd goes through CallIntrinsic1::UnaryPositive, while USub, Not, and Invert target the dedicated UnaryNegative / UnaryNot / UnaryInvert instructions. Stack usage is correct (operand compiled first, each op consuming/producing a single value), and not is now clearly desugared to ToBool + UnaryNot, matching truthiness semantics and the separate compile_jump_if handling for control-flow cases.

    I don’t see any changes needed here; just make sure there are bytecode/VM tests covering +x, -x, ~x, and not x to catch regressions across the new opcodes/intrinsic.

    crates/vm/src/frame.rs (1)

    2630-2630: UnaryPositive intrinsic implementation looks correct.

    The call to vm._pos(&arg) properly handles the unary plus operation.

    However, note the architectural inconsistency: UnaryInvert, UnaryNegative, and UnaryNot are direct instructions, while UnaryPositive is handled via intrinsic. This may be intentional (e.g., if unary plus is less common or has special semantics), but it creates a mixed approach to unary operations.

    💡 Optional: Consider architectural consistency

    For consistency, consider whether all unary operations should follow the same pattern (either all direct instructions or all intrinsics). The current mixed approach is:

    • Direct instructions: UnaryInvert, UnaryNegative, UnaryNot
    • Intrinsic: UnaryPositive

    If there's a specific reason for this split (e.g., unary plus is rarely used), it might be worth documenting in a code comment.

    📜 Review details

    Configuration used: Path: .coderabbit.yml

    Review profile: CHILL

    Plan: Pro

    📥 Commits

    Reviewing files that changed from the base of the PR and between 6cc103b and eece3bf.

    📒 Files selected for processing (3)
    • crates/codegen/src/compile.rs
    • crates/compiler-core/src/bytecode.rs
    • crates/vm/src/frame.rs
    🧰 Additional context used
    📓 Path-based instructions (1)
    **/*.rs

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    **/*.rs: Follow the default rustfmt code style by running cargo fmt to format Rust code
    Always run clippy to lint code (cargo clippy) before completing tasks and fix any warnings or lints introduced by changes
    Follow Rust best practices for error handling and memory management
    Use the macro system (pyclass, pymodule, pyfunction, etc.) when implementing Python functionality in Rust

    Files:

    • crates/codegen/src/compile.rs
    • crates/vm/src/frame.rs
    • crates/compiler-core/src/bytecode.rs
    ⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
    • GitHub Check: Run snippets and cpython tests (macos-latest)
    • GitHub Check: Run snippets and cpython tests (windows-latest)
    • GitHub Check: Run snippets and cpython tests on wasm-wasi
    • GitHub Check: Run rust tests (windows-latest)
    • GitHub Check: Check Rust code with clippy
    • GitHub Check: Ensure compilation on various targets
    • GitHub Check: Run tests under miri
    • GitHub Check: Check the WASM package and demo
    🔇 Additional comments (4)
    crates/vm/src/frame.rs (1)

    1764-1781: LGTM! Clean implementation of explicit unary operations.

    The three unary instruction implementations are correct and straightforward:

    • Each properly pops the operand, applies the operation, and pushes the result
    • Error handling is appropriate
    • UnaryNot correctly calls try_to_bool before negating, which matches Python semantics
    crates/compiler-core/src/bytecode.rs (3)

    643-643: LGTM! UnaryPositive intrinsic variant added correctly.

    The UnaryPositive = 5 variant is properly added to the IntrinsicFunction1 enum with the correct sequential value.


    761-766: LGTM! Explicit unary instruction variants correctly added.

    The three unary instructions (UnaryInvert, UnaryNegative, UnaryNot) are:

    • Correctly positioned as no-argument instructions (opcodes 41-43, below the 44 threshold)
    • Properly documented with their opcode numbers
    • Consistent with the CPython 3.13 opcode structure

    2068-2070: LGTM! Stack effects and display formatting are correct.

    • Stack effects (lines 2068-2070): All three unary operations correctly show a stack effect of 0 (pop 1 operand, push 1 result)
    • Display formatting (lines 2155-2157): Proper use of the w! macro for instruction display, consistent with other instructions
    • The unary operations are correctly excluded from the placeholder/dummy instruction list

    Also applies to: 2155-2157

    UnaryOperation {
    op: Arg<UnaryOperator>,
    },
    // 141-148: Reserved (padding to keep RESUME at 149)
    Copy link
    Member

    Choose a reason for hiding this comment

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

    Let’s keep this line by replacing it with Reserved140 instead of removing it.

    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: 1

    Fix all issues with AI Agents 🤖
    In @crates/jit/src/instructions.rs:
    - Around line 630-641: The UnaryInvert arm incorrectly implements bitwise NOT by
    computing 0 - val (negation); change the logic in Instruction::UnaryInvert
    handling for JitValue::Int so it produces bitwise complement: either emit a bnot
    on the integer value via the builder (if Cranelift supports a bnot op) or
    compute -val - 1 by calling compile_sub twice (first compute neg =
    compile_sub(zero, val) then compute out = compile_sub(neg, one)) where one is an
    iconst types::I64 value of 1; push JitValue::Int(out) and keep the NotSupported
    error path unchanged.
    
    📜 Review details

    Configuration used: Path: .coderabbit.yml

    Review profile: CHILL

    Plan: Pro

    📥 Commits

    Reviewing files that changed from the base of the PR and between eece3bf and c54b881.

    📒 Files selected for processing (1)
    • crates/jit/src/instructions.rs
    🧰 Additional context used
    📓 Path-based instructions (1)
    **/*.rs

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    **/*.rs: Follow the default rustfmt code style by running cargo fmt to format Rust code
    Always run clippy to lint code (cargo clippy) before completing tasks and fix any warnings or lints introduced by changes
    Follow Rust best practices for error handling and memory management
    Use the macro system (pyclass, pymodule, pyfunction, etc.) when implementing Python functionality in Rust

    Files:

    • crates/jit/src/instructions.rs
    🧠 Learnings (1)
    📚 Learning: 2025-12-27T14:03:49.034Z
    Learnt from: CR
    Repo: RustPython/RustPython PR: 0
    File: .github/copilot-instructions.md:0-0
    Timestamp: 2025-12-27T14:03:49.034Z
    Learning: RustPython is a Python 3 interpreter written in Rust implementing Python 3.13.0+ compatibility with complete Python environment in Rust (not CPython bindings), clean implementation without compatibility hacks, and cross-platform support including WebAssembly compilation
    

    Applied to files:

    • crates/jit/src/instructions.rs
    ⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
    • GitHub Check: Run snippets and cpython tests on wasm-wasi
    • GitHub Check: Run rust tests (ubuntu-latest)
    • GitHub Check: Run rust tests (macos-latest)
    • GitHub Check: Run rust tests (windows-latest)
    • GitHub Check: Run tests under miri
    • GitHub Check: Run snippets and cpython tests (macos-latest)
    • GitHub Check: Run snippets and cpython tests (windows-latest)
    • GitHub Check: Run snippets and cpython tests (ubuntu-latest)
    • GitHub Check: Ensure compilation on various targets
    • GitHub Check: Check Rust code with clippy
    • GitHub Check: Check the WASM package and demo
    🔇 Additional comments (2)
    crates/jit/src/instructions.rs (2)

    8-8: LGTM: Import updates align with the refactor.

    The removal of UnaryOperator from imports is correct and consistent with the broader refactoring that replaces the composite unary operation pattern with explicit per-variant instructions.


    623-629: LGTM: Correct implementation of logical NOT.

    The implementation correctly converts the operand to a boolean value and negates it using XOR with 1. This properly implements Python's not operator semantics.

    Comment on lines 630 to 641
    Instruction::UnaryInvert => {
    match self.stack.pop().ok_or(JitCompileError::BadBytecode)? {
    JitValue::Int(val) => {
    // Compile minus as 0 - val.
    let zero = self.builder.ins().iconst(types::I64, 0);
    let out = self.compile_sub(zero, val);
    self.stack.push(JitValue::Int(out));
    Ok(())
    }
    (UnaryOperator::Plus, JitValue::Int(val)) => {
    // Nothing to do
    self.stack.push(JitValue::Int(val));
    Ok(())
    }
    (UnaryOperator::Not, a) => {
    let boolean = self.boolean_val(a)?;
    let not_boolean = self.builder.ins().bxor_imm(boolean, 1);
    self.stack.push(JitValue::Bool(not_boolean));
    Ok(())
    }
    _ => Err(JitCompileError::NotSupported),
    }
    }
    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

    Critical: Incorrect implementation of bitwise invert operator.

    The UnaryInvert instruction implements Python's ~ operator (bitwise NOT), which should compute -x - 1 for integers, not -x. The current implementation computes 0 - val, which is negation, producing incorrect results.

    Examples of the bug:

    • ~5 should return -6, but this returns -5
    • ~0 should return -1, but this returns 0
    • ~(-3) should return 2, but this returns 3
    🔎 Proposed fix for bitwise invert
     Instruction::UnaryInvert => {
         match self.stack.pop().ok_or(JitCompileError::BadBytecode)? {
             JitValue::Int(val) => {
    -            // Compile minus as 0 - val.
    +            // Bitwise invert: ~x = -x - 1
                 let zero = self.builder.ins().iconst(types::I64, 0);
    -            let out = self.compile_sub(zero, val);
    +            let neg_val = self.compile_sub(zero, val);
    +            let one = self.builder.ins().iconst(types::I64, 1);
    +            let out = self.compile_sub(neg_val, one);
                 self.stack.push(JitValue::Int(out));
                 Ok(())
             }
             _ => Err(JitCompileError::NotSupported),
         }
     }

    Alternatively, if Cranelift supports bitwise NOT directly (e.g., bnot), that would be more efficient and clearer:

     Instruction::UnaryInvert => {
         match self.stack.pop().ok_or(JitCompileError::BadBytecode)? {
             JitValue::Int(val) => {
    -            // Compile minus as 0 - val.
    -            let zero = self.builder.ins().iconst(types::I64, 0);
    -            let out = self.compile_sub(zero, val);
    +            // Bitwise invert using NOT instruction
    +            let out = self.builder.ins().bnot(val);
                 self.stack.push(JitValue::Int(out));
                 Ok(())
             }
             _ => Err(JitCompileError::NotSupported),
         }
     }
    📝 Committable suggestion

    ‼️ IMPORTANT
    Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    Suggested change
    Instruction::UnaryInvert => {
    match self.stack.pop().ok_or(JitCompileError::BadBytecode)? {
    JitValue::Int(val) => {
    // Compile minus as 0 - val.
    let zero = self.builder.ins().iconst(types::I64, 0);
    let out = self.compile_sub(zero, val);
    self.stack.push(JitValue::Int(out));
    Ok(())
    }
    (UnaryOperator::Plus, JitValue::Int(val)) => {
    // Nothing to do
    self.stack.push(JitValue::Int(val));
    Ok(())
    }
    (UnaryOperator::Not, a) => {
    let boolean = self.boolean_val(a)?;
    let not_boolean = self.builder.ins().bxor_imm(boolean, 1);
    self.stack.push(JitValue::Bool(not_boolean));
    Ok(())
    }
    _ => Err(JitCompileError::NotSupported),
    }
    }
    Instruction::UnaryInvert => {
    match self.stack.pop().ok_or(JitCompileError::BadBytecode)? {
    JitValue::Int(val) => {
    // Bitwise invert: ~x = -x - 1
    let zero = self.builder.ins().iconst(types::I64, 0);
    let neg_val = self.compile_sub(zero, val);
    let one = self.builder.ins().iconst(types::I64, 1);
    let out = self.compile_sub(neg_val, one);
    self.stack.push(JitValue::Int(out));
    Ok(())
    }
    _ => Err(JitCompileError::NotSupported),
    }
    }
    🤖 Prompt for AI Agents
    In @crates/jit/src/instructions.rs around lines 630-641, The UnaryInvert arm
    incorrectly implements bitwise NOT by computing 0 - val (negation); change the
    logic in Instruction::UnaryInvert handling for JitValue::Int so it produces
    bitwise complement: either emit a bnot on the integer value via the builder (if
    Cranelift supports a bnot op) or compute -val - 1 by calling compile_sub twice
    (first compute neg = compile_sub(zero, val) then compute out = compile_sub(neg,
    one)) where one is an iconst types::I64 value of 1; push JitValue::Int(out) and
    keep the NotSupported error path unchanged.
    

    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: 3

    Fix all issues with AI Agents 🤖
    In @crates/jit/src/instructions.rs:
    - Around line 644-649: The UnaryNot branch uses a JitValue enum directly in
    bxor_imm; update Instruction::UnaryNot to pop the value, match it to
    JitValue::Bool(var) to extract the underlying Cranelift Value (or return
    JitCompileError::BadBytecode if it isn't Bool), call self.builder.ins().bxor_imm
    on that extracted Value, then push JitValue::Bool(result) as before; ensure you
    do not pass the enum itself to bxor_imm and preserve existing error handling
    around self.stack.pop().
    - Around line 650-661: The UnaryNegative handler currently only handles
    JitValue::Int by compiling 0 - val; add a branch for JitValue::Float in the
    match inside Instruction::UnaryNegative to call self.builder.ins().fneg on the
    popped float value and push JitValue::Float(out) onto self.stack, returning
    Ok(()); keep the existing Int branch (using compile_sub) and the fallback error
    branch unchanged.
    - Around line 477-491: The unary intrinsic handling in
    Instruction::CallIntrinsic1 { func } currently only supports JitValue::Int for
    IntrinsicFunction1::UnaryPositive; update the match arms so both
    IntrinsicFunction1::UnaryPositive and IntrinsicFunction1::UnaryNegative handle
    JitValue::Float as well (for UnaryPositive push the same JitValue::Float back,
    for UnaryNegative push a JitValue::Float with the negated value), mirroring the
    existing Int semantics around the
    self.stack.pop().ok_or(JitCompileError::BadBytecode)? pattern and returning the
    same error branches for unsupported types.
    
    📜 Review details

    Configuration used: Path: .coderabbit.yml

    Review profile: CHILL

    Plan: Pro

    📥 Commits

    Reviewing files that changed from the base of the PR and between f12d2f3 and 50763ce.

    📒 Files selected for processing (1)
    • crates/jit/src/instructions.rs
    🧰 Additional context used
    📓 Path-based instructions (1)
    **/*.rs

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    **/*.rs: Follow the default rustfmt code style by running cargo fmt to format Rust code
    Always run clippy to lint code (cargo clippy) before completing tasks and fix any warnings or lints introduced by changes
    Follow Rust best practices for error handling and memory management
    Use the macro system (pyclass, pymodule, pyfunction, etc.) when implementing Python functionality in Rust

    Files:

    • crates/jit/src/instructions.rs
    ⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
    • GitHub Check: Ensure compilation on various targets
    • GitHub Check: Run snippets and cpython tests (windows-latest)
    • GitHub Check: Run rust tests (macos-latest)
    • GitHub Check: Run rust tests (windows-latest)
    • GitHub Check: Check the WASM package and demo
    • GitHub Check: Check Rust code with clippy
    • GitHub Check: Run snippets and cpython tests (macos-latest)
    • GitHub Check: Run tests under miri
    • GitHub Check: Run snippets and cpython tests on wasm-wasi
    🔇 Additional comments (2)
    crates/jit/src/instructions.rs (2)

    7-8: LGTM: Import changes align with new instruction variants.

    The addition of IntrinsicFunction1 and restructured imports correctly support the new CallIntrinsic1 instruction handler and explicit unary operation variants.


    638-643: LGTM: ToBool conversion is correctly implemented.

    The handler properly converts any JitValue to a boolean using the existing boolean_val helper and pushes the result as JitValue::Bool.

    Comment on lines +477 to +491
    Instruction::CallIntrinsic1 { func } => {
    match func.get(arg) {
    IntrinsicFunction1::UnaryPositive => {
    match self.stack.pop().ok_or(JitCompileError::BadBytecode)? {
    JitValue::Int(val) => {
    // Nothing to do
    self.stack.push(JitValue::Int(val));
    Ok(())
    }
    _ => Err(JitCompileError::NotSupported),
    }
    }
    _ => Err(JitCompileError::NotSupported),
    }
    }
    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 | 🟡 Minor

    🧩 Analysis chain

    🏁 Script executed:

    # Search for UnaryPositive references and Float handling patterns
    rg -n 'UnaryPositive' crates/jit/src/ --type rust -C 2

    Repository: RustPython/RustPython

    Length of output: 528


    🏁 Script executed:

    # Check if Float is handled for other unary operations
    rg -n 'JitValue::Float' crates/jit/src/instructions.rs -C 3 | head -100

    Repository: RustPython/RustPython

    Length of output: 5237


    🏁 Script executed:

    # Search for IntrinsicFunction1 definition to understand what operations exist
    rg -n 'enum IntrinsicFunction1|IntrinsicFunction1::' crates/jit/src/ --type rust -B2 -A5

    Repository: RustPython/RustPython

    Length of output: 790


    🏁 Script executed:

    # Check if there are any Float support patterns in the codebase
    rg -n 'Float.*=>|JitValue::Float' crates/jit/src/instructions.rs --type rust -B2 -A2 | head -50

    Repository: RustPython/RustPython

    Length of output: 2409


    🏁 Script executed:

    # Search for other unary operations to see the pattern
    rg -n 'UnaryNegative|UnaryInvert|UnaryNot' crates/jit/src/instructions.rs --type rust -B2 -A8

    Repository: RustPython/RustPython

    Length of output: 940


    🏁 Script executed:

    # Get more context for UnaryNegative to see if Float is handled
    rg -n 'Instruction::UnaryNegative' crates/jit/src/instructions.rs --type rust -A20

    Repository: RustPython/RustPython

    Length of output: 1102


    Add Float support for unary operations.

    The implementation correctly handles UnaryPositive for Int, but Float support is missing. This is inconsistent with binary operations throughout the file (Add, Subtract, Multiply, TrueDivide, Power all support Float). Both UnaryPositive and UnaryNegative should handle JitValue::Float with the same semantics as Int (identity for +, negate for -).

    🤖 Prompt for AI Agents
    In @crates/jit/src/instructions.rs around lines 477-491, The unary intrinsic
    handling in Instruction::CallIntrinsic1 { func } currently only supports
    JitValue::Int for IntrinsicFunction1::UnaryPositive; update the match arms so
    both IntrinsicFunction1::UnaryPositive and IntrinsicFunction1::UnaryNegative
    handle JitValue::Float as well (for UnaryPositive push the same JitValue::Float
    back, for UnaryNegative push a JitValue::Float with the negated value),
    mirroring the existing Int semantics around the
    self.stack.pop().ok_or(JitCompileError::BadBytecode)? pattern and returning the
    same error branches for unsupported types.
    

    Comment on lines +650 to 661
    Instruction::UnaryNegative => {
    match self.stack.pop().ok_or(JitCompileError::BadBytecode)? {
    JitValue::Int(val) => {
    // Compile minus as 0 - val.
    let zero = self.builder.ins().iconst(types::I64, 0);
    let out = self.compile_sub(zero, val);
    self.stack.push(JitValue::Int(out));
    Ok(())
    }
    (UnaryOperator::Plus, JitValue::Int(val)) => {
    // Nothing to do
    self.stack.push(JitValue::Int(val));
    Ok(())
    }
    (UnaryOperator::Not, a) => {
    let boolean = self.boolean_val(a)?;
    let not_boolean = self.builder.ins().bxor_imm(boolean, 1);
    self.stack.push(JitValue::Bool(not_boolean));
    Ok(())
    }
    _ => Err(JitCompileError::NotSupported),
    }
    }
    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 | 🟠 Major

    🧩 Analysis chain

    🏁 Script executed:

    #!/bin/bash
    # Search for fneg and Float handling in unary operations
    rg -nP --type=rust -C3 'fneg|UnaryNegative.*Float|Float.*UnaryNegative' crates/jit/src/

    Repository: RustPython/RustPython

    Length of output: 609


    🏁 Script executed:

    #!/bin/bash
    # Check the full context around UnaryNegative and look for Float handling patterns
    rg -nP --type=rust -B5 -A10 'Instruction::UnaryNegative' crates/jit/src/

    Repository: RustPython/RustPython

    Length of output: 1435


    🏁 Script executed:

    #!/bin/bash
    # Search for UnaryPositive to see if it has similar patterns
    rg -nP --type=rust -B2 -A8 'Instruction::UnaryPositive' crates/jit/src/

    Repository: RustPython/RustPython

    Length of output: 47


    🏁 Script executed:

    #!/bin/bash
    # Look for Float handling in other unary or binary operations for reference
    rg -nP --type=rust 'JitValue::Float.*=>' crates/jit/src/ | head -20

    Repository: RustPython/RustPython

    Length of output: 556


    Add Float support to UnaryNegative handler.

    The implementation handles Int correctly but should also support JitValue::Float. Since the codebase uses fneg for float negation and Float is handled in other binary operations (e.g., line 529), add a case for JitValue::Float:

    JitValue::Float(val) => {
        let out = self.builder.ins().fneg(val);
        self.stack.push(JitValue::Float(out));
        Ok(())
    }
    🤖 Prompt for AI Agents
    In @crates/jit/src/instructions.rs around lines 650-661, The UnaryNegative
    handler currently only handles JitValue::Int by compiling 0 - val; add a branch
    for JitValue::Float in the match inside Instruction::UnaryNegative to call
    self.builder.ins().fneg on the popped float value and push JitValue::Float(out)
    onto self.stack, returning Ok(()); keep the existing Int branch (using
    compile_sub) and the fallback error branch unchanged.
    

    Copy link
    Member
    @youknowone youknowone left a comment

    Choose a reason for hiding this comment

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

    👍

    @youknowone youknowone merged commit ead7e0c into RustPython:main Jan 5, 2026
    13 checks passed
    terryluan12 pushed a commit to terryluan12/RustPython that referenced this pull request Jan 5, 2026
    * Make use of `Unary` opcodes
    
    * Add `Reserved140` opcode
    
    * re-add support for UnaryPositive in JIT
    
    * JIT support for `ToBool` instruction
    @coderabbitai coderabbitai bot mentioned this pull request Jan 6, 2026
    terryluan12 pushed a commit to terryluan12/RustPython that referenced this pull request Jan 15, 2026
    * Make use of `Unary` opcodes
    
    * Add `Reserved140` opcode
    
    * re-add support for UnaryPositive in JIT
    
    * JIT support for `ToBool` instruction
    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.

    2 participants

    0