-
Notifications
You must be signed in to change notification settings - Fork 19
builtins: fold sum() over the live iterator, and carry a wide int through the compensated fast paths #1036
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
builtins: fold sum() over the live iterator, and carry a wide int through the compensated fast paths #1036
Changes from all commits
a0c018c
709aef7
341ea33
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -15196,86 +15196,210 @@ fn builtin_sum(args: &[PyObjectRef]) -> Result<PyObjectRef, crate::PyError> { | |||||||||||||
| "sum() can't sum bytearray [use b''.join(seq) instead]", | ||||||||||||||
| )); | ||||||||||||||
| } | ||||||||||||||
| // `_regular_sum`: `last = last + x` over the generic iterator protocol | ||||||||||||||
| // (so generators, ranges, sets, dict views, ... all work). Very | ||||||||||||||
| // intentionally `last + x`, not `+=` — preserving a mutable `start` | ||||||||||||||
| // (e.g. a list) matches PyPy's app-level definition. | ||||||||||||||
| let items = crate::builtins::collect_iterable(iterable)?; | ||||||||||||||
| let mut last = start; | ||||||||||||||
| let mut i = 0; | ||||||||||||||
| // `_regular_sum`: `for x in sequence: last = last + x` over the generic | ||||||||||||||
| // iterator protocol (so generators, ranges, sets, dict views, ... all | ||||||||||||||
| // work). Very intentionally `last + x`, not `+=` — preserving a mutable | ||||||||||||||
| // `start` (e.g. a list) matches PyPy's app-level definition. | ||||||||||||||
| // | ||||||||||||||
| // That fold runs over the *live* iterator, so `next` and the addition | ||||||||||||||
| // interleave. Materialising the iterable up front reorders those side | ||||||||||||||
| // effects and turns an unbounded iterable into an unbounded allocation, so | ||||||||||||||
| // pump the iterator here instead and keep the running total in a | ||||||||||||||
| // shadow-stack slot: every `next` and every `__add__` can run allocating | ||||||||||||||
| // Python code and move it. | ||||||||||||||
| // Every slot here is read and written once per item, so the whole fold | ||||||||||||||
| // goes through the scope's cached root-stack cell rather than the free | ||||||||||||||
| // `shadow_stack_*` functions, which re-resolve the thread local on each | ||||||||||||||
| // call. | ||||||||||||||
| let roots = pyre_object::gc_roots::push_roots(); | ||||||||||||||
| // `iter(iterable)` runs arbitrary allocating code before the fold begins, | ||||||||||||||
| // so both operands are already live across a collection point here: root | ||||||||||||||
| // them first and read every later use back out of the slot, never from the | ||||||||||||||
| // local a foreign collection has left stale. | ||||||||||||||
| let last_slot = roots.base(); | ||||||||||||||
| roots.pin_root(start); | ||||||||||||||
| let iterable_slot = last_slot + 1; | ||||||||||||||
| roots.pin_root(iterable); | ||||||||||||||
|
Comment on lines
+15220
to
+15222
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If another thread starts a moving collection while the first Useful? React with 👍 / 👎. |
||||||||||||||
| let it_slot = iterable_slot + 1; | ||||||||||||||
| roots.pin_root(crate::baseobjspace::iter(roots.get(iterable_slot))?); | ||||||||||||||
| // Seeded with `start` purely to own a slot; every read is guarded by | ||||||||||||||
| // `pending`, which is only set once a real item has been written here. | ||||||||||||||
| let item_slot = it_slot + 1; | ||||||||||||||
| roots.pin_root(roots.get(last_slot)); | ||||||||||||||
| // A dict-view iterator is an off-GC carrier whose own walker does not cover | ||||||||||||||
| // this native loop; retain its source dict for the duration, as | ||||||||||||||
| // `collect_iterator` does. | ||||||||||||||
| if unsafe { pyre_object::dictmultiobject::is_dict_view_iterator(roots.get(it_slot)) } { | ||||||||||||||
| let w_dict = unsafe { | ||||||||||||||
| pyre_object::dictmultiobject::w_dict_view_iterator_get_dict(roots.get(it_slot)) | ||||||||||||||
| }; | ||||||||||||||
| roots.pin_root(w_dict); | ||||||||||||||
| } | ||||||||||||||
| let mut pending = false; | ||||||||||||||
| let mut exhausted = false; | ||||||||||||||
| let mut ensure_item = | ||||||||||||||
| |pending: &mut bool, exhausted: &mut bool| -> Result<(), crate::PyError> { | ||||||||||||||
| if *pending || *exhausted { | ||||||||||||||
| return Ok(()); | ||||||||||||||
| } | ||||||||||||||
| // The iterator may have moved during a prior step; reload it from its | ||||||||||||||
| // (post-relocation) slot before each call. | ||||||||||||||
| match crate::baseobjspace::next(roots.get(it_slot)) { | ||||||||||||||
| Ok(v) => { | ||||||||||||||
| roots.set(item_slot, v); | ||||||||||||||
| *pending = true; | ||||||||||||||
| } | ||||||||||||||
| Err(e) if e.kind == crate::PyErrorKind::StopIteration => *exhausted = true, | ||||||||||||||
| Err(e) => return Err(e), | ||||||||||||||
| } | ||||||||||||||
| Ok(()) | ||||||||||||||
| }; | ||||||||||||||
| // `builtin_sum_impl` runs an exact-int accumulator until the running | ||||||||||||||
| // total turns into an exact float, then a float accumulator that carries | ||||||||||||||
| // the improved Kahan-Babuška (Neumaier) compensation term — so | ||||||||||||||
| // `sum([0.1] * 10)` is exactly `1.0` rather than the naive partial sum | ||||||||||||||
| // `functional.py:_sum` produces. The int phase is the generic `last + x` | ||||||||||||||
| // loop, which already keeps exact ints exact and promotes to a float on | ||||||||||||||
| // the first float item, so only the float phase needs its own arithmetic. | ||||||||||||||
| while i < items.len() | ||||||||||||||
| && unsafe { is_exact_int_operand(last) } | ||||||||||||||
| && !unsafe { is_exact_float_operand(last) } | ||||||||||||||
| { | ||||||||||||||
| last = crate::baseobjspace::add(last, items[i])?; | ||||||||||||||
| i += 1; | ||||||||||||||
| // | ||||||||||||||
| // The C accumulator is a machine word, and `PyLong_AsLongAndOverflow` | ||||||||||||||
| // signalling overflow ends the int fast path for good: the value that | ||||||||||||||
| // leaves it is still an `int`, so neither compensated phase below can | ||||||||||||||
| // claim it and the rest of the fold is generic. Leaving on a wide total | ||||||||||||||
| // reproduces that — `sum([2**63, 0.1, 1, -(2**63)])` is `0.0`, not the | ||||||||||||||
| // `1.0` a compensated float phase would produce. | ||||||||||||||
| loop { | ||||||||||||||
| ensure_item(&mut pending, &mut exhausted)?; | ||||||||||||||
| if !pending { | ||||||||||||||
| break; | ||||||||||||||
| } | ||||||||||||||
| // Nothing between this read and the `add` can collect, so the running | ||||||||||||||
| // total is read once per item rather than re-read for the call. | ||||||||||||||
| let last = roots.get(last_slot); | ||||||||||||||
| if !unsafe { is_machine_word_int_operand(last) } { | ||||||||||||||
| break; | ||||||||||||||
| } | ||||||||||||||
| pending = false; | ||||||||||||||
| let total = crate::baseobjspace::add(last, roots.get(item_slot))?; | ||||||||||||||
| roots.set(last_slot, total); | ||||||||||||||
| } | ||||||||||||||
| if unsafe { is_exact_float_operand(last) } { | ||||||||||||||
| let mut real_sum = | ||||||||||||||
| compensated_sum_from_double(unsafe { pyre_object::w_float_get_value(last) }); | ||||||||||||||
| while i < items.len() { | ||||||||||||||
| let item = items[i]; | ||||||||||||||
| if unsafe { is_exact_float_operand(roots.get(last_slot)) } { | ||||||||||||||
| let mut real_sum = compensated_sum_from_double(unsafe { | ||||||||||||||
| pyre_object::w_float_get_value(roots.get(last_slot)) | ||||||||||||||
| }); | ||||||||||||||
| loop { | ||||||||||||||
| ensure_item(&mut pending, &mut exhausted)?; | ||||||||||||||
| if !pending { | ||||||||||||||
| break; | ||||||||||||||
| } | ||||||||||||||
| let item = roots.get(item_slot); | ||||||||||||||
| // A float *subclass* leaves the fast path — its `__add__` may be | ||||||||||||||
| // overridden — while `bool` and an `int` subclass stay in it, | ||||||||||||||
| // matching the `PyFloat_CheckExact` / `PyLong_Check` asymmetry of | ||||||||||||||
| // the C loop. An int too wide for a machine word leaves it too, | ||||||||||||||
| // the way `PyLong_AsLongAndOverflow` signals overflow. | ||||||||||||||
| // the C loop. An item that leaves it stays `pending` for the | ||||||||||||||
| // generic fold below. | ||||||||||||||
| let x = unsafe { | ||||||||||||||
| if pyre_object::is_float(item) && pyre_object::is_exact_builtin_instance(item) { | ||||||||||||||
| pyre_object::w_float_get_value(item) | ||||||||||||||
| } else if pyre_object::pyobject::is_int(item) { | ||||||||||||||
| pyre_object::w_int_get_value(item) as f64 | ||||||||||||||
| } else if pyre_object::pyobject::is_int_or_long(item) { | ||||||||||||||
| let v = sum_int_as_double(item); | ||||||||||||||
| if !v.is_finite() { | ||||||||||||||
| return Err(crate::PyError::overflow_error( | ||||||||||||||
| "int too large to convert to float", | ||||||||||||||
| )); | ||||||||||||||
| } | ||||||||||||||
| v | ||||||||||||||
| } else { | ||||||||||||||
| break; | ||||||||||||||
| } | ||||||||||||||
| }; | ||||||||||||||
| pending = false; | ||||||||||||||
| real_sum = compensated_sum_add(real_sum, x); | ||||||||||||||
| i += 1; | ||||||||||||||
| } | ||||||||||||||
| last = pyre_object::w_float_new(compensated_sum_to_double(real_sum)); | ||||||||||||||
| roots.set( | ||||||||||||||
| last_slot, | ||||||||||||||
| pyre_object::w_float_new(compensated_sum_to_double(real_sum)), | ||||||||||||||
| ); | ||||||||||||||
| } | ||||||||||||||
| // CPython 3.14's complex fast path carries independent compensated real | ||||||||||||||
| // and imaginary accumulators. Exact complex items, all ints, and all | ||||||||||||||
| // floats stay on the path; an arbitrary numeric object falls back to the | ||||||||||||||
| // generic left-fold below. | ||||||||||||||
| if unsafe { is_exact_complex_operand(last) } { | ||||||||||||||
| if unsafe { is_exact_complex_operand(roots.get(last_slot)) } { | ||||||||||||||
|
Comment on lines
+15319
to
+15328
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🌐 Web query:
💡 Result: In CPython, the Citations:
🏁 Script executed: #!/bin/sh
set -eu
# Locate the relevant builtin implementation and inspect the sum loop around the cited region.
printf 'Files named builtins.rs:\n'
fd -a 'builtins\.rs$' . | sed 's#^\./##'
printf '\nLine count:\n'
wc -l pyre/pyre-interpreter/src/builtins.rs
printf '\nRelevant region (15260-15365):\n'
sed -n '15260,15365p' pyre/pyre-interpreter/src/builtins.rs | nl -ba -v15260
printf '\nSearch for exact_complex_operand and sum implementation context:\n'
rg -n "is_exact_complex_operand|PyComplex_CheckExact|compensated_sum_to_double|generic left-fold|sum\\(" pyre/pyre-interpreter/src/builtins.rsRepository: youknowone/pyre Length of output: 365 🏁 Script executed: #!/bin/sh
set -eu
printf 'Relevant region (15260-15365):\n'
awk '{printf "%8d\t%s\n", NR, $0}' pyre/pyre-interpreter/src/builtins.rs | sed -n '15260,15365p'
printf '\nSearch for exact_complex_operand and sum implementation context:\n'
rg -n "is_exact_complex_operand|PyComplex_CheckExact|compensated_sum_to_double|generic left-fold|sum\\(" pyre/pyre-interpreter/src/builtins.rsRepository: youknowone/pyre Length of output: 7115 🌐 Web query:
💡 Result: In CPython's Citations:
🌐 Web query:
💡 Result: Based on the search results, I found information about the code you're looking for in CPython's In the Specifically, the implementation checks if items are exact complex types using The code utilizes a specialized Citations: 🌐 Web query:
💡 Result: In the CPython source code, PyComplex_CheckExact is a macro defined in Include/complexobject.h that checks if a given PyObject is exactly an instance of the complex type, explicitly excluding its subclasses [1][2]. It is defined as: Citations:
🏁 Script executed: #!/bin/sh
set -eu
python3 - <<'PY'
try:
import math
from _pyre_interpreter import builtin_sum
except Exception as e:
print("IMPORT_ERROR", repr(e))
import sys
sys.exit(0)
cases = [
("float_float_complex", [0.1] + [[complex(1.0)])],
("float_complex", [0.1 + 0.0, 1+2j],
("complex_float_float", [1+0j] + [0.1, 0.2, 0.3]),
]
missing = []
for name, args in cases:
try:
print(name, "REPR", repr(builtin_sum(list(args))))
except Exception as e:
print(name, "EXCEPTION", type(e).__name__, repr(e))
print("PYTHON_FLOAT_FLOAT_COMPLEX", repr(sum([0.1, complex(1.0, 2.0)])))
print("PYTHON_FLOAT_COMPLEX", repr(sum([0.1+0j, 1+2j])))
print("PYTHON_COMPLEX_FLOAT_FLOAT", repr(sum([1+0j, 0.1, 0.2, 0.3])))
PYRepository: youknowone/pyre Length of output: 363 🏁 Script executed: #!/bin/sh
set -eu
python3 - <<'PY'
try:
import math
from _pyre_interpreter import builtin_sum
except Exception as e:
print("IMPORT_ERROR", repr(e))
import sys
sys.exit(0)
cases = [
("float_float_complex", [0.1, complex(1.0, 2.0)]),
("float_complex", [0.1+0j, 1+2j]),
("complex_float_float", [1+0j, 0.1, 0.2, 0.3]),
]
for name, args in cases:
try:
print(name, "REPR", repr(builtin_sum(list(args))))
except Exception as e:
print(name, "EXCEPTION", type(e).__name__, repr(e))
for args in [
[0.1, complex(1.0, 2.0)],
[0.1+0j, 1+2j],
[1+0j, 0.1, 0.2, 0.3],
]:
print("python", repr(args), "=>", repr(sum(args)))
PYRepository: youknowone/pyre Length of output: 225 🏁 Script executed: #!/bin/sh
set -eu
# Read the next phase to understand how the pending complex falls into the generic fold and any post-fold checks.
printf 'Generic fold and end checks (15361-15415):\n'
awk '{printf "%8d\t%s\n", NR, $0}' pyre/pyre-interpreter/src/builtins.rs | sed -n '15361,15415p'
printf '\nOperand predicates and helpers:\n'
rg -n "is_exact_float_operand|is_exact_int_operand|is_exact_complex_operand|w_complex_get_real|w_complex_get_imag" pyre/pyre-interpreter/src -A8 -B4
printf '\nBuiltins tests mentioning sum or complex:\n'
rg -n "sum\\(|complex|isnan|float\\+complex|float.*complex|complex.*float" pyre -g '*.py' -g '*.rs' -A4 -B4Repository: youknowone/pyre Length of output: 50372 🏁 Script executed: #!/bin/sh
set -eu
printf 'Generic fold and post-general checks (15361-15445):\n'
awk '{printf "%8d\t%s\n", NR, $0}' pyre/pyre-interpreter/src/builtins.rs | sed -n '15361,15445p'
printf '\nOperand predicates (15404-15430):\n'
awk '{printf "%8d\t%s\n", NR, $0}' pyre/pyre-interpreter/src/builtins.rs | sed -n '15404,15430p'Repository: youknowone/pyre Length of output: 5501 🌐 Web query:
💡 Result: The functions PyComplex_CheckExact and PyObject_Add are both part of the CPython C API, but they serve different purposes and are used in different contexts within the CPython source code [1]. PyComplex_CheckExact(op) is a macro defined in the CPython C API that returns true if its argument is a PyComplexObject but not a subtype of PyComplexObject [1]. In the context of Python/bltinmodule.c, it is used to perform type checks on objects, often to allow for specialized, faster handling of complex numbers without invoking general, slower methods [2]. For example, development discussions have included proposing the use of PyComplex_CheckExact within the builtin sum function to specialize summation for complex numbers [2]. PyObject_Add(o1, o2) is a C API function that adds two objects together, essentially performing the equivalent of the Python expression o1 + o2. It is a general-purpose function that handles various object types by invoking their type-specific number methods (e.g., nb_add) [1]. While both may appear in Python/bltinmodule.c—which implements many of Python's built-in functions—they are not typically used together as a pair [3][4][2]. PyComplex_CheckExact is used to verify an object's exact type, while PyObject_Add is used to perform arithmetic operations [2][1]. Their proximity in search results or code discussions generally reflects distinct operations being performed on objects within built-in functions, rather than them being linked as a single mechanism. Citations:
🌐 Web query:
💡 Result: The terms and code snippet provided originate from two distinct and unrelated domains: the Python C API and the CSparse library. There is no native function named cs_add in Python's bltinmodule.c or any other core CPython source file that uses PyComplex_CheckExact or PyNumber_Add. 1. Python C API (PyComplex_CheckExact, PyNumber_Add) These are standard functions within the CPython source code [1][2]. - PyComplex_CheckExact(result) is a C API function used to determine if an object is an exact instance of the built-in complex type, excluding subclasses [1][3]. - PyNumber_Add(result, item) is a generic C API function that performs addition between two Python objects, equivalent to the expression result + item in Python [2][4]. 2. CSparse Library (cs_add) The symbol cs_add belongs to the CSparse library, a suite of sparse matrix algorithms [5][6]. The function signature is typically cs *cs_add (const cs A, const cs B, double alpha, double beta) [5][6]. It performs sparse matrix addition (C = alphaA + betaB) [5][6] and is implemented in C, completely unrelated to Python's complex number or object addition APIs [5][7]. The snippet likely represents a user-defined function or a misunderstanding of merging two separate codebases. No such integration exists in the standard CPython distribution [8][9]. Citations:
🌐 Web query:
💡 Result: PyComplex_CheckExact(item) is a C macro in the CPython API used to determine if a given PyObject* is specifically an instance of the complex type (PyComplex_Type), excluding subclasses [1][2][3]. It is defined in the Python header files, typically within Include/complexobject.h, as a check against the object's type pointer [1][4]. The macro essentially performs the following logic: Citations:
🌐 Web query:
💡 Result: The functions PyComplex_CheckExact and PyNumber_Add are part of the Python/C API and are utilized within the implementation of the built-in sum function located in Python/bltinmodule.c [1][2]. The sum function employs fast-path optimizations for specific types (such as integers, floats, and complex numbers) to improve performance [3][2]. 1. PyComplex_CheckExact: This function is used to verify if an object is an exact instance of a complex number, excluding subclasses [4]. In the context of builtin_sum_impl within bltinmodule.c, it is used to guard or enter specialized loops that perform high-performance, in-place summation for complex numbers [3]. 2. PyNumber_Add: When the inputs do not meet the criteria for specialized fast-path handling (e.g., they are not exact instances of the expected types or a fallback is required), the implementation falls back to the generic PyNumber_Add function [3][2]. This ensures that the summation remains compatible with arbitrary Python objects that support the addition protocol [3]. Recent developments in the CPython source code, such as PR Citations:
🌐 Web query:
💡 Result: The identifiers Citations:
🌐 Web query:
💡 Result: The terms you are referencing appear to be related to the internal implementation of Python's C API, specifically concerning numeric operations and object checking. PyComplex_CheckExact(p) is a macro in the Python/C API that returns a non-zero value if the provided object p is exactly a standard Python complex number object, and zero if it is a subtype or a different type [1][2]. It is commonly used in CPython's internal code to optimize operations by ensuring the object has the expected standard memory layout, avoiding the overhead or complexity associated with subclasses [2][3]. The terms "cs_add" and "fs" (often seen as part of fsum or similar accuracy-improving logic) are related to internal helper functions used within CPython to improve the numerical precision of summation operations [4][5]. Specifically: - cs_add: This is an internal helper function (Compensated Summation) used in recent CPython implementations (notably within the builtin sum function) to maintain higher precision when adding numbers [4][5]. It uses a compensated summation algorithm to track high-order and low-order bits, mitigating precision loss during floating-point or complex number accumulation [4]. - Context: These are separate components. PyComplex_CheckExact is for type verification, while cs_add is an internal algorithm for accurate numerical summation [4][3]. They appear together in CPython's codebase when the sum function handles complex or float inputs, ensuring that the inputs are correctly identified and processed using these high-precision helpers [5]. Citations:
Fix the mixed float-then-complex
🤖 Prompt for AI Agents |
||||||||||||||
| let last = roots.get(last_slot); | ||||||||||||||
| let mut real_sum = | ||||||||||||||
| compensated_sum_from_double(unsafe { pyre_object::w_complex_get_real(last) }); | ||||||||||||||
| let mut imag_sum = | ||||||||||||||
| compensated_sum_from_double(unsafe { pyre_object::w_complex_get_imag(last) }); | ||||||||||||||
| while i < items.len() { | ||||||||||||||
| let item = items[i]; | ||||||||||||||
| loop { | ||||||||||||||
| ensure_item(&mut pending, &mut exhausted)?; | ||||||||||||||
| if !pending { | ||||||||||||||
| break; | ||||||||||||||
| } | ||||||||||||||
| let item = roots.get(item_slot); | ||||||||||||||
| unsafe { | ||||||||||||||
| if is_exact_complex_operand(item) { | ||||||||||||||
| real_sum = compensated_sum_add(real_sum, pyre_object::w_complex_get_real(item)); | ||||||||||||||
| imag_sum = compensated_sum_add(imag_sum, pyre_object::w_complex_get_imag(item)); | ||||||||||||||
| } else if pyre_object::pyobject::is_int(item) { | ||||||||||||||
| real_sum = | ||||||||||||||
| compensated_sum_add(real_sum, pyre_object::w_int_get_value(item) as f64); | ||||||||||||||
| } else if pyre_object::pyobject::is_int_or_long(item) { | ||||||||||||||
| let v = sum_int_as_double(item); | ||||||||||||||
| if !v.is_finite() { | ||||||||||||||
| return Err(crate::PyError::overflow_error( | ||||||||||||||
| "int too large to convert to float", | ||||||||||||||
| )); | ||||||||||||||
| } | ||||||||||||||
| real_sum = compensated_sum_add(real_sum, v); | ||||||||||||||
| } else if pyre_object::is_float(item) { | ||||||||||||||
| real_sum = compensated_sum_add(real_sum, pyre_object::w_float_get_value(item)); | ||||||||||||||
|
Comment on lines
15352
to
15353
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Require an exact Line 15345 tests A 🐛 Proposed fix- } else if pyre_object::is_float(item) {
+ } else if pyre_object::is_float(item)
+ && pyre_object::is_exact_builtin_instance(item)
+ {
real_sum = compensated_sum_add(real_sum, pyre_object::w_float_get_value(item));📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
| } else { | ||||||||||||||
| break; | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| i += 1; | ||||||||||||||
| pending = false; | ||||||||||||||
| } | ||||||||||||||
| last = pyre_object::w_complex_new( | ||||||||||||||
| compensated_sum_to_double(real_sum), | ||||||||||||||
| compensated_sum_to_double(imag_sum), | ||||||||||||||
| roots.set( | ||||||||||||||
| last_slot, | ||||||||||||||
| pyre_object::w_complex_new( | ||||||||||||||
| compensated_sum_to_double(real_sum), | ||||||||||||||
| compensated_sum_to_double(imag_sum), | ||||||||||||||
| ), | ||||||||||||||
| ); | ||||||||||||||
| } | ||||||||||||||
| for &item in &items[i..] { | ||||||||||||||
| last = crate::baseobjspace::add(last, item)?; | ||||||||||||||
| loop { | ||||||||||||||
| ensure_item(&mut pending, &mut exhausted)?; | ||||||||||||||
| if !pending { | ||||||||||||||
| break; | ||||||||||||||
| } | ||||||||||||||
| pending = false; | ||||||||||||||
| let total = crate::baseobjspace::add(roots.get(last_slot), roots.get(item_slot))?; | ||||||||||||||
| roots.set(last_slot, total); | ||||||||||||||
| } | ||||||||||||||
| Ok(last) | ||||||||||||||
| Ok(roots.get(last_slot)) | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| /// `PyLong_AsDouble` for `builtin_sum`'s compensated fast paths: an `int` of | ||||||||||||||
| /// any width as a double. A Python `int` is always finite, so a non-finite | ||||||||||||||
| /// result means the magnitude exceeds f64 range and the caller raises. | ||||||||||||||
| /// | ||||||||||||||
| /// Signalling over-range through the value rather than a `Result` keeps a | ||||||||||||||
| /// float-payload `Result` out of the JIT codewriter, which cannot flatten a | ||||||||||||||
| /// payload mixing `Float` (`Ok`) and `Ref` (`Err`) into one register kind. | ||||||||||||||
| unsafe fn sum_int_as_double(obj: PyObjectRef) -> f64 { | ||||||||||||||
| unsafe { | ||||||||||||||
| if pyre_object::pyobject::is_int(obj) { | ||||||||||||||
| pyre_object::w_int_get_value(obj) as f64 | ||||||||||||||
| } else { | ||||||||||||||
| pyre_object::jit_bigint_to_f64_or_nan(pyre_object::w_long_get_value(obj)) | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| /// An exact `int` the C fast path's `long` accumulator can hold — the | ||||||||||||||
| /// `PyLong_AsLongAndOverflow` precondition guarding `builtin_sum`'s integer | ||||||||||||||
| /// loop. pyre splits a wide `int` into its own `LONG_TYPE`, so overflow is | ||||||||||||||
| /// the type test rather than a range check. | ||||||||||||||
| unsafe fn is_machine_word_int_operand(obj: PyObjectRef) -> bool { | ||||||||||||||
| unsafe { is_exact_int_operand(obj) && !pyre_object::is_long(obj) } | ||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When AGENTS.md reference: AGENTS.md:L231-L233 Useful? React with 👍 / 👎. |
||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| /// `PyLong_CheckExact` — an exact `int`, excluding `bool` and any subclass. | ||||||||||||||
|
|
||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.