Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2025, Oracle and/or its affiliates.
* Copyright (c) 2017, 2026, Oracle and/or its affiliates.
* Copyright (c) 2013, Regents of the University of California
*
* All rights reserved.
Expand Down Expand Up @@ -498,19 +498,21 @@ static boolean doObject(VirtualFrame frame, Object object,
@Cached PyIterNextNode nextNode,
@Cached PyObjectIsTrueNode isTrueNode) {
Object iterator = getIter.execute(frame, inliningTarget, object);
int nbrIter = 0;
int loopCount = 0;

while (true) {
try {
Object next = nextNode.execute(frame, inliningTarget, iterator);
nbrIter++;
if (CompilerDirectives.hasNextTier() && loopCount < Integer.MAX_VALUE) {
loopCount++;
}
if (!isTrueNode.execute(frame, next)) {
return false;
}
} catch (IteratorExhausted e) {
break;
} finally {
LoopNode.reportLoopCount(inliningTarget, nbrIter);
LoopNode.reportLoopCount(inliningTarget, loopCount);
}
}

Expand Down Expand Up @@ -549,19 +551,21 @@ static boolean doObject(VirtualFrame frame, Object object,
@Cached PyIterNextNode nextNode,
@Cached PyObjectIsTrueNode isTrueNode) {
Object iterator = getIter.execute(frame, inliningTarget, object);
int nbrIter = 0;
int loopCount = 0;

while (true) {
try {
Object next = nextNode.execute(frame, inliningTarget, iterator);
nbrIter++;
if (CompilerDirectives.hasNextTier() && loopCount < Integer.MAX_VALUE) {
loopCount++;
}
if (isTrueNode.execute(frame, next)) {
return true;
}
} catch (IteratorExhausted e) {
break;
} finally {
LoopNode.reportLoopCount(inliningTarget, nbrIter);
LoopNode.reportLoopCount(inliningTarget, loopCount);
}
}

Expand Down Expand Up @@ -1563,11 +1567,13 @@ static Object minmaxSequenceWithKey(VirtualFrame frame, Node inliningTarget, Obj
currentKey = nextKey;
currentValue = nextValue;
}
loopCount++;
if (CompilerDirectives.hasNextTier() && loopCount < Integer.MAX_VALUE) {
loopCount++;
}
} catch (IteratorExhausted e) {
break;
} finally {
LoopNode.reportLoopCount(inliningTarget, loopCount < 0 ? Integer.MAX_VALUE : loopCount);
LoopNode.reportLoopCount(inliningTarget, loopCount);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2025, Oracle and/or its affiliates.
* Copyright (c) 2017, 2026, Oracle and/or its affiliates.
* Copyright (c) 2014, Regents of the University of California
*
* All rights reserved.
Expand Down Expand Up @@ -34,7 +34,6 @@
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.util.Arrays;
import java.util.List;

import com.oracle.graal.python.PythonLanguage;
Expand All @@ -50,6 +49,9 @@
import com.oracle.graal.python.builtins.objects.ints.IntBuiltins;
import com.oracle.graal.python.builtins.objects.ints.PInt;
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
import com.oracle.graal.python.builtins.objects.type.TpSlots;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotIterNext;
import com.oracle.graal.python.lib.IteratorExhausted;
import com.oracle.graal.python.lib.PyBoolCheckNode;
import com.oracle.graal.python.lib.PyFloatAsDoubleNode;
Expand Down Expand Up @@ -91,6 +93,7 @@
import com.oracle.graal.python.runtime.exception.PException;
import com.oracle.graal.python.runtime.object.PFactory;
import com.oracle.graal.python.util.OverflowException;
import com.oracle.graal.python.util.XSum;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
Expand All @@ -108,6 +111,7 @@
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.dsl.TypeSystemReference;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.LoopNode;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.InlinedConditionProfile;
import com.oracle.truffle.api.profiles.InlinedLoopConditionProfile;
Expand Down Expand Up @@ -869,127 +873,65 @@ protected ArgumentClinicProvider getArgumentClinic() {
@GenerateNodeFactory
public abstract static class FsumNode extends PythonUnaryBuiltinNode {

/**
* Note: this specialization uses an inlined version of {@link PyIterNextNode} with the
* tp_iternext slot moved out of the loop.
*/
@Specialization
static double doIt(VirtualFrame frame, Object iterable,
@Bind Node inliningTarget,
@Cached PyObjectGetIter getIter,
@Cached PyIterNextNode nextNode,
@Cached GetClassNode nextNodeGetClassNode,
@Cached TpSlots.GetCachedTpSlotsNode nextNodeGetSlots,
@Cached TpSlotIterNext.CallSlotTpIterNextNode nextNodeCallNext,
@Cached IsBuiltinObjectProfile nextNodeStopIterationProfile,
@Cached PyFloatAsDoubleNode asDoubleNode,
@Cached InlinedLoopConditionProfile loopProfile,
@Cached PRaiseNode raiseNode) {
/*
* This implementation is taken from CPython. The performance is not good. Should be
* faster. It can be easily replace with much simpler code based on BigDecimal:
*
* BigDecimal result = BigDecimal.ZERO;
*
* in cycle just: result = result.add(BigDecimal.valueof(x); ... The current
* implementation is little bit faster. The testFSum in test_math.py takes in different
* implementations: CPython ~0.6s CurrentImpl: ~14.3s Using BigDecimal: ~15.1
*/
Object iterator = getIter.execute(frame, inliningTarget, iterable);
double x, y, t, hi, lo = 0, yr, inf_sum = 0, special_sum = 0, sum;
double xsave;
int i, j, n = 0, arayLength = 32;
double[] p = new double[arayLength];
boolean exhausted = false;
while (loopProfile.profile(inliningTarget, !exhausted)) {
try {
Object next = nextNode.execute(frame, inliningTarget, iterator);
x = asDoubleNode.execute(frame, inliningTarget, next);
xsave = x;
for (i = j = 0; j < n; j++) { /* for y in partials */
y = p[j];
if (Math.abs(x) < Math.abs(y)) {
t = x;
x = y;
y = t;
}
hi = x + y;
yr = hi - x;
lo = y - yr;
if (lo != 0.0) {
p[i++] = lo;
}
x = hi;
}

n = i;
if (x != 0.0) {
if (!Double.isFinite(x)) {
/*
* a nonfinite x could arise either as a result of intermediate
* overflow, or as a result of a nan or inf in the summands
*/
if (Double.isFinite(xsave)) {
throw raiseNode.raise(inliningTarget, OverflowError, ErrorMessages.INTERMEDIATE_OVERFLOW_IN, "fsum");
}
if (Double.isInfinite(xsave)) {
inf_sum += xsave;
}
special_sum += xsave;
/* reset partials */
n = 0;
} else if (n >= arayLength) {
arayLength += arayLength;
p = Arrays.copyOf(p, arayLength);
} else {
p[n++] = x;
}
TpSlot tpIternext = nextNodeGetSlots.execute(inliningTarget, nextNodeGetClassNode.execute(inliningTarget, iterator)).tp_iternext();
assert tpIternext != null;

var acc = new XSum.SmallAccumulator();
int loopCount = 0;
while (true) {
try {
Object next = nextNodeCallNext.execute(frame, inliningTarget, tpIternext, iterator);
if (CompilerDirectives.hasNextTier() && loopCount < Integer.MAX_VALUE) {
loopCount++;
}
acc.add(asDoubleNode.execute(frame, inliningTarget, next));
} catch (IteratorExhausted e) {
exhausted = true;
break;
} catch (PException e) {
e.expectStopIteration(inliningTarget, nextNodeStopIterationProfile);
break;
} finally {
LoopNode.reportLoopCount(inliningTarget, loopCount);
}
}

if (special_sum != 0.0) {
if (Double.isNaN(inf_sum)) {
if (acc.isNaNResult()) {
return Double.NaN;
}

if (acc.isInfiniteResult()) {
double result = acc.getInfiniteResult();
if (Double.isNaN(result)) {
throw raiseNode.raise(inliningTarget, ValueError, ErrorMessages.NEG_INF_PLUS_INF_IN);
} else {
sum = special_sum;
return sum;
assert Double.isInfinite(result);
return result;
}
}

hi = 0.0;
if (n > 0) {
hi = p[--n];
/*
* sum_exact(ps, hi) from the top, stop when the sum becomes inexact.
*/
while (n > 0) {
x = hi;
y = p[--n];
assert (Math.abs(y) < Math.abs(x));
hi = x + y;
yr = hi - x;
lo = y - yr;
if (lo != 0.0) {
break;
}
}
/*
* Make half-even rounding work across multiple partials. Needed so that sum([1e-16,
* 1, 1e16]) will round-up the last digit to two instead of down to zero (the 1e-16
* makes the 1 slightly closer to two). With a potential 1 ULP rounding error
* fixed-up, math.fsum() can guarantee commutativity.
*/
if (n > 0 && ((lo < 0.0 && p[n - 1] < 0.0) ||
(lo > 0.0 && p[n - 1] > 0.0))) {
y = lo * 2.0;
x = hi + y;
yr = x - hi;
if (compareAsBigDecimal(y, yr) == 0) {
hi = x;
}
}
double result = acc.round();
// +Inf or -Inf if exponent has overflowed
if (Double.isInfinite(result)) {
throw raiseNode.raise(inliningTarget, OverflowError, ErrorMessages.INTERMEDIATE_OVERFLOW_IN, "fsum");
} else {
return result;
}
return hi;
}

@TruffleBoundary
private static int compareAsBigDecimal(double y, double yr) {
return BigDecimal.valueOf(y).compareTo(BigDecimal.valueOf(yr));
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
Expand Down Expand Up @@ -45,7 +45,6 @@
import static com.oracle.graal.python.nodes.BuiltinNames.T_FUNCTOOLS;
import static com.oracle.graal.python.nodes.ErrorMessages.REDUCE_EMPTY_SEQ;
import static com.oracle.graal.python.nodes.ErrorMessages.S_ARG_N_MUST_SUPPORT_ITERATION;
import static com.oracle.truffle.api.nodes.LoopNode.reportLoopCount;

import java.util.List;

Expand Down Expand Up @@ -75,6 +74,7 @@
import com.oracle.truffle.api.dsl.NodeFactory;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.LoopNode;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.InlinedConditionProfile;

Expand Down Expand Up @@ -136,7 +136,7 @@ Object doReduce(VirtualFrame frame, Object function, Object sequence, Object ini

Object[] args = new Object[2];

int count = 0;
int loopCount = 0;
while (true) {
Object op2;
try {
Expand All @@ -152,11 +152,11 @@ Object doReduce(VirtualFrame frame, Object function, Object sequence, Object ini
args[1] = op2;
result = callNode.execute(frame, function, args);
}
if (CompilerDirectives.hasNextTier()) {
count++;
if (CompilerDirectives.hasNextTier() && loopCount < Integer.MAX_VALUE) {
loopCount++;
}
}
reportLoopCount(this, count >= 0 ? count : Integer.MAX_VALUE);
LoopNode.reportLoopCount(this, loopCount);

if (result == null) {
throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, REDUCE_EMPTY_SEQ);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
Expand Down Expand Up @@ -1216,12 +1216,12 @@ static boolean doIt(Frame frame, Node inliningTarget, HashingStorage aStorage, H
if (lenANode.execute(inliningTarget, aStorage) != lenBNode.execute(inliningTarget, bStorage)) {
return false;
}
int index = 0;
int loopCount = 0;
try {
HashingStorageIterator aIter = getAIter.execute(inliningTarget, aStorage);
while (loopProfile.profile(inliningTarget, aIterNext.execute(inliningTarget, aStorage, aIter))) {
if (CompilerDirectives.hasNextTier()) {
index++;
if (CompilerDirectives.hasNextTier() && loopCount < Integer.MAX_VALUE) {
loopCount++;
}

Object aKey = aIterKey.execute(inliningTarget, aStorage, aIter);
Expand All @@ -1236,8 +1236,8 @@ static boolean doIt(Frame frame, Node inliningTarget, HashingStorage aStorage, H
return false;
}
} finally {
if (index != 0) {
LoopNode.reportLoopCount(inliningTarget, index);
if (loopCount != 0) {
LoopNode.reportLoopCount(inliningTarget, loopCount);
}
}
return true;
Expand Down Expand Up @@ -1287,19 +1287,19 @@ static Object doIt(Frame frame, Node callbackInliningTarget, HashingStorage stor
@Cached HashingStorageGetIterator getIter,
@Cached HashingStorageIteratorNext iterNext,
@Cached InlinedLoopConditionProfile loopProfile) {
int index = 0;
int loopCount = 0;
Object accumulator = accumulatorIn;
try {
HashingStorageIterator aIter = getIter.execute(inliningTarget, storage);
while (loopProfile.profile(inliningTarget, iterNext.execute(inliningTarget, storage, aIter))) {
if (CompilerDirectives.hasNextTier()) {
index++;
if (CompilerDirectives.hasNextTier() && loopCount < Integer.MAX_VALUE) {
loopCount++;
}
accumulator = callback.execute(frame, callbackInliningTarget, storage, aIter, accumulator);
}
} finally {
if (index != 0) {
LoopNode.reportLoopCount(getIter, index);
if (loopCount != 0) {
LoopNode.reportLoopCount(getIter, loopCount);
}
}
return accumulator;
Expand Down
Loading
Loading