Nuitka
The Python compiler
Loading...
Searching...
No Matches
HelpersOperationBinaryAddUtils.c
1// Copyright 2025, Kay Hayen, mailto:kay.hayen@gmail.com find license text at end of file
2
3/* These slots are still manually coded and are used by the generated code.
4 *
5 * The plan should be to generate these as well, so e.g. we can have a slot
6 * SLOT_nb_add_LONG_INT that is optimal too.
7 */
8
9// This file is included from another C file, help IDEs to still parse it on
10// its own.
11#ifdef __IDE_ONLY__
12#include "nuitka/prelude.h"
13#endif
14
15static PyObject *LIST_CONCAT(PyThreadState *tstate, PyObject *operand1, PyObject *operand2) {
16 CHECK_OBJECT(operand1);
17 assert(PyList_CheckExact(operand1));
18 CHECK_OBJECT(operand2);
19 assert(PyList_CheckExact(operand2));
20
21 Py_ssize_t size = Py_SIZE(operand1) + Py_SIZE(operand2);
22
23 PyListObject *result = (PyListObject *)MAKE_LIST_EMPTY(tstate, size);
24 if (unlikely(result == NULL)) {
25 return NULL;
26 }
27
28 PyObject **src = ((PyListObject *)operand1)->ob_item;
29 PyObject **dest = result->ob_item;
30
31 for (Py_ssize_t i = 0; i < Py_SIZE(operand1); i++) {
32 PyObject *v = src[i];
33 Py_INCREF(v);
34 dest[i] = v;
35 }
36 src = ((PyListObject *)operand2)->ob_item;
37 dest = result->ob_item + Py_SIZE(operand1);
38 for (Py_ssize_t i = 0; i < Py_SIZE(operand2); i++) {
39 PyObject *v = src[i];
40 Py_INCREF(v);
41 dest[i] = v;
42 }
43 return (PyObject *)result;
44}
45
46// Needed for offsetof
47#include <stddef.h>
48
49#if PYTHON_VERSION < 0x3c0
50#define MAX_LONG_DIGITS ((PY_SSIZE_T_MAX - offsetof(PyLongObject, ob_digit)) / sizeof(digit))
51#define Nuitka_LongGetDigitPointer(value) (&(((PyLongObject *)value)->ob_digit[0]))
52#define Nuitka_LongGetDigitSize(value) (Py_ABS(Py_SIZE(value)))
53#define Nuitka_LongGetSignedDigitSize(value) (Py_SIZE(value))
54#define Nuitka_LongIsNegative(value) (Py_SIZE(value) < 0)
55#define Nuitka_LongSetSignNegative(value) Py_SET_SIZE(value, -Py_ABS(Py_SIZE(value)))
56#define Nuitka_LongSetSign(value, positive) Py_SET_SIZE(value, (((positive) ? 1 : -1) * Py_ABS(Py_SIZE(value))))
57#define Nuitka_LongFlipSign(value) Py_SET_SIZE(value, -Py_SIZE(value))
58#define Nuitka_LongSetDigitSizeAndNegative(value, count, negative) Py_SET_SIZE(value, negative ? -count : count)
59#else
60#define MAX_LONG_DIGITS ((PY_SSIZE_T_MAX - offsetof(PyLongObject, long_value.ob_digit)) / sizeof(digit))
61
62#define Nuitka_LongGetDigitPointer(value) (&(((PyLongObject *)value)->long_value.ob_digit[0]))
63#define Nuitka_LongGetDigitSize(value) (_PyLong_DigitCount((PyLongObject const *)(value)))
64#define Nuitka_LongGetSignedDigitSize(value) (_PyLong_SignedDigitCount((PyLongObject const *)(value)))
65#define Nuitka_LongIsNegative(value) (((PyLongObject *)value)->long_value.lv_tag & SIGN_NEGATIVE)
66#define Nuitka_LongSetSignNegative(value) \
67 ((PyLongObject *)value)->long_value.lv_tag = ((PyLongObject *)value)->long_value.lv_tag | SIGN_NEGATIVE;
68#define Nuitka_LongSetSignPositive(value) \
69 ((PyLongObject *)value)->long_value.lv_tag = ((PyLongObject *)value)->long_value.lv_tag & ~(SIGN_NEGATIVE);
70#define Nuitka_LongSetSign(value, positive) \
71 if (positive) { \
72 Nuitka_LongSetSignPositive(value); \
73 } else { \
74 Nuitka_LongSetSignNegative(value); \
75 }
76#define Nuitka_LongSetDigitSizeAndNegative(value, count, negative) \
77 _PyLong_SetSignAndDigitCount(value, negative ? -1 : 1, count)
78#define Nuitka_LongFlipSign(value) _PyLong_FlipSign(value)
79#endif
80
81// Our version of _PyLong_New(size);
82static PyLongObject *Nuitka_LongNew(Py_ssize_t size) {
83 // TODO: The assertion may be a bit to strong, could be <= for at least < 3.12
84 assert(size < (Py_ssize_t)MAX_LONG_DIGITS);
85 assert(size >= 0);
86
87#if PYTHON_VERSION >= 0x3c0
88 // The zero now is a single digit number.
89 Py_ssize_t ndigits = size ? size : 1;
90
91 PyLongObject *result =
92 (PyLongObject *)NuitkaObject_Malloc(offsetof(PyLongObject, long_value.ob_digit) + ndigits * sizeof(digit));
93 _PyLong_SetSignAndDigitCount(result, size != 0, size);
94 PyObject_INIT(result, &PyLong_Type);
95 result->long_value.ob_digit[0] = 0;
96 return result;
97#elif PYTHON_VERSION >= 0x300
98 PyLongObject *result = (PyLongObject *)NuitkaObject_Malloc(offsetof(PyLongObject, ob_digit) + size * sizeof(digit));
99 return (PyLongObject *)PyObject_INIT_VAR(result, &PyLong_Type, size);
100#else
101 return (PyLongObject *)PyObject_NEW_VAR(PyLongObject, &PyLong_Type, size);
102#endif
103}
104
105static PyObject *Nuitka_LongRealloc(PyObject *value, Py_ssize_t size) {
106 assert(size >= 0);
107
108 PyLongObject *result = Nuitka_LongNew(size);
109 Nuitka_LongSetDigitSizeAndNegative(result, size, false);
110 Py_DECREF(value);
111
112 return (PyObject *)result;
113}
114
115static PyObject *Nuitka_LongFromCLong(long ival) {
116#if PYTHON_VERSION < 0x300
117 if (ival == 0) {
118 PyLongObject *result = Nuitka_LongNew(0);
119
120 return (PyObject *)result;
121 }
122#else
123 if (ival >= NUITKA_STATIC_SMALLINT_VALUE_MIN && ival < NUITKA_STATIC_SMALLINT_VALUE_MAX) {
124 PyObject *result = Nuitka_Long_GetSmallValue(ival);
125 Py_INCREF(result);
126
127 return result;
128 }
129#endif
130
131 // We go via unsigned long to avoid overflows when shifting and we need
132 // the sign separate in the end anyway.
133 unsigned long abs_ival;
134 bool negative;
135
136 if (ival < 0) {
137 abs_ival = 0U - (unsigned long)ival;
138 negative = true;
139 } else {
140 abs_ival = (unsigned long)ival;
141 negative = false;
142 }
143
144 // Fast path for single digit values
145 if (!(abs_ival >> PyLong_SHIFT)) {
146 PyLongObject *result = Nuitka_LongNew(1);
147 assert(result != NULL);
148 if (negative) {
149 Nuitka_LongSetSignNegative(result);
150 }
151
152 digit *digits = Nuitka_LongGetDigitPointer(result);
153 digits[0] = (digit)abs_ival;
154
155 return (PyObject *)result;
156 }
157
158 // Fast path for two digit values on suitable platforms.
159#if PyLong_SHIFT == 15
160 if (!(abs_ival >> 2 * PyLong_SHIFT)) {
161 PyLongObject *result = Nuitka_LongNew(2);
162 assert(result != NULL);
163 if (negative) {
164 Nuitka_LongSetSignNegative(result);
165 }
166
167 digit *digits = Nuitka_LongGetDigitPointer(result);
168
169 digits[0] = (digit)(abs_ival & PyLong_MASK);
170 digits[1] = (digit)(abs_ival >> PyLong_SHIFT);
171
172 return (PyObject *)result;
173 }
174#endif
175
176 // Slow path for the rest.
177 unsigned long t = abs_ival;
178 Py_ssize_t ndigits = 0;
179
180 // First determine the number of digits needed.
181 while (t != 0) {
182 ++ndigits;
183 t >>= PyLong_SHIFT;
184 }
185
186 PyLongObject *result = _PyLong_New(ndigits);
187 assert(result != NULL);
188
189 Nuitka_LongSetDigitSizeAndNegative(result, ndigits, negative);
190
191 digit *d = Nuitka_LongGetDigitPointer(result);
192
193 // Now copy the digits
194 t = abs_ival;
195 while (t != 0) {
196 *d++ = (digit)(t & PyLong_MASK);
197 t >>= PyLong_SHIFT;
198 }
199
200 return (PyObject *)result;
201}
202
203// Our "PyLong_FromLong" replacement.
204PyObject *Nuitka_PyLong_FromLong(long ival) { return Nuitka_LongFromCLong(ival); }
205
206static void Nuitka_LongUpdateFromCLong(PyObject **value, long ival) {
207 assert(Py_REFCNT(*value) == 1);
208
209#if PYTHON_VERSION < 0x300
210 if (ival == 0) {
211 if (Py_SIZE(*value) == 0) {
212 return;
213 }
214
215 Py_DECREF(*value);
216 *value = (PyObject *)Nuitka_LongNew(0);
217
218 return;
219 }
220#else
221 if (ival >= NUITKA_STATIC_SMALLINT_VALUE_MIN && ival < NUITKA_STATIC_SMALLINT_VALUE_MAX) {
222 Py_DECREF(*value);
223
224 *value = Nuitka_Long_GetSmallValue(ival);
225 Py_INCREF(*value);
226
227 return;
228 }
229#endif
230
231 // We go via unsigned long to avoid overflows when shifting and we need
232 // the sign separate in the end anyway.
233 unsigned long abs_ival;
234 bool negative;
235
236 if (ival < 0) {
237 abs_ival = 0U - (unsigned long)ival;
238 negative = true;
239 } else {
240 abs_ival = (unsigned long)ival;
241 negative = false;
242 }
243
244 // Fast path for single digit values
245 if (!(abs_ival >> PyLong_SHIFT)) {
246 PyLongObject *result;
247
248#if PYTHON_VERSION < 0x3c0
249 if (unlikely(Py_SIZE(*value) == 0)) {
250 *value = Nuitka_LongRealloc(*value, 1);
251 CHECK_OBJECT(*value);
252
253 result = (PyLongObject *)*value;
254 } else
255#endif
256 {
257 result = (PyLongObject *)(*value);
258 }
259
260 Nuitka_LongSetSign(result, !negative);
261
262 digit *digits = Nuitka_LongGetDigitPointer(result);
263 digits[0] = (digit)abs_ival;
264
265 return;
266 }
267
268 // Fast path for two digit values on suitable platforms, e.g. armv7l
269#if PyLong_SHIFT == 15
270 if (!(abs_ival >> 2 * PyLong_SHIFT)) {
271 PyLongObject *result;
272 if (unlikely(Py_ABS(Py_SIZE(*value)) < 2)) {
273 *value = Nuitka_LongRealloc(*value, 2);
274 CHECK_OBJECT(*value);
275
276 result = (PyLongObject *)*value;
277 } else {
278 result = (PyLongObject *)(*value);
279 }
280
281 if (negative) {
282 Nuitka_LongSetSignNegative(result);
283 }
284
285 digit *digits = Nuitka_LongGetDigitPointer(result);
286
287 digits[0] = (digit)(abs_ival & PyLong_MASK);
288 digits[1] = (digit)(abs_ival >> PyLong_SHIFT);
289
290 return;
291 }
292#endif
293
294 // Slow path for the rest.
295 unsigned long t = abs_ival;
296 Py_ssize_t ndigits = 0;
297
298 // First determine the number of digits needed.
299 while (t != 0) {
300 ndigits++;
301 t >>= PyLong_SHIFT;
302 }
303
304 if (unlikely(Py_ABS(Py_SIZE(*value)) < ndigits)) {
305 *value = Nuitka_LongRealloc(*value, ndigits);
306 }
307
308 CHECK_OBJECT(*value);
309
310 Nuitka_LongSetDigitSizeAndNegative((PyLongObject *)*value, ndigits, negative);
311
312 digit *d = Nuitka_LongGetDigitPointer(*value);
313
314 // Now copy the digits
315 t = abs_ival;
316 while (t) {
317 *d++ = (digit)(t & PyLong_MASK);
318 t >>= PyLong_SHIFT;
319 }
320
321 return;
322}
323
324#if 0
325// Note: We are manually inlining this so far.
326static PyLongObject *Nuitka_LongStripZeros(PyLongObject *v) {
327 Py_ssize_t j = Py_ABS(Py_SIZE(v));
328
329 Py_ssize_t i = j;
330 while (i > 0 && v->ob_digit[i - 1] == 0) {
331 i -= 1;
332 }
333
334 if (i != j) {
335 Py_SIZE(v) = (Py_SIZE(v) < 0) ? -i : i;
336 }
337
338 return v;
339}
340#endif
341
342static PyLongObject *_Nuitka_LongAddDigits(digit const *a, Py_ssize_t size_a, digit const *b, Py_ssize_t size_b) {
343 // Make sure we know a is the longest value.
344 if (size_a < size_b) {
345 {
346 digit const *temp = a;
347 a = b;
348 b = temp;
349 }
350
351 {
352 Py_ssize_t temp = size_a;
353 size_a = size_b;
354 size_b = temp;
355 }
356 }
357
358 // We do not know ahead of time, if we need a new digit, lets just allocate it.
359 PyLongObject *result = Nuitka_LongNew(size_a + 1);
360 CHECK_OBJECT(result);
361
362 digit *r = Nuitka_LongGetDigitPointer(result);
363
364 digit carry = 0;
365
366 // First common digits.
367 Py_ssize_t i;
368 for (i = 0; i < size_b; i++) {
369 carry += a[i] + b[i];
370 r[i] = carry & PyLong_MASK;
371 carry >>= PyLong_SHIFT;
372 }
373 // Digits from longest one only.
374 for (; i < size_a; i++) {
375 carry += a[i];
376 r[i] = carry & PyLong_MASK;
377 carry >>= PyLong_SHIFT;
378 }
379
380 // Only the top digit can be zero, so we can strip this faster.
381 if (carry) {
382 r[i] = carry;
383 } else {
384 // Note: Beware, this looses the sign value.
385 Nuitka_LongSetDigitSizeAndNegative(result, Nuitka_LongGetDigitSize(result) - 1, false);
386 }
387
388 return result;
389}
390
391static PyObject *_Nuitka_LongAddInplaceDigits(PyObject *left, digit const *b, Py_ssize_t size_b) {
392 digit const *a = Nuitka_LongGetDigitPointer(left);
393 Py_ssize_t size_a = Nuitka_LongGetDigitSize(left);
394
395 digit const *aa = a;
396 digit const *bb = b;
397
398 // Make sure we know aa is the longest value by swapping a/b attributes.
399 if (size_a < size_b) {
400 {
401 aa = b;
402 bb = a;
403 }
404
405 {
406 Py_ssize_t temp = size_a;
407 size_a = size_b;
408 size_b = temp;
409 }
410 }
411
412 digit carry = 0;
413
414 // First common digits.
415 Py_ssize_t i;
416 for (i = 0; i < size_b; i++) {
417 carry += aa[i] + bb[i];
418 carry >>= PyLong_SHIFT;
419 }
420
421 // Digits from longest one only might cause a new digit through carry.
422 Py_ssize_t needed = size_a;
423
424 for (; i < size_a; i++) {
425 carry += aa[i];
426 carry >>= PyLong_SHIFT;
427
428 // No more carry, that means size cannot increase.
429 if (carry == 0) {
430 break;
431 }
432 }
433
434 // Final digit needs to be added.
435 if (carry) {
436 needed = i + 1;
437 }
438
439 // Need to keep the old value around, or else we commit use after free potentially.
440 PyObject *old = left;
441
442 if (needed > Nuitka_LongGetDigitSize(left)) {
443 left = (PyObject *)Nuitka_LongNew(needed);
444 } else {
445 Py_INCREF(old);
446 }
447
448 digit *r = Nuitka_LongGetDigitPointer(left);
449
450 // Now do the real thing, with actual storage to left digits.
451 carry = 0;
452
453 // First common digits.
454 for (i = 0; i < size_b; i++) {
455 carry += aa[i] + bb[i];
456 r[i] = carry & PyLong_MASK;
457 carry >>= PyLong_SHIFT;
458 }
459 // Digits from longest one only.
460 for (; i < size_a; i++) {
461 carry += aa[i];
462 r[i] = carry & PyLong_MASK;
463 carry >>= PyLong_SHIFT;
464 }
465
466 // Final digit from the carry.
467 if (carry != 0) {
468 r[i] = carry;
469
470 Nuitka_LongSetDigitSizeAndNegative((PyLongObject *)left, i + 1, false);
471 } else {
472 Nuitka_LongSetDigitSizeAndNegative((PyLongObject *)left, i, false);
473 }
474
475 // Release reference to old value
476 Py_DECREF(old);
477
478 return left;
479}
480
481static PyLongObject *_Nuitka_LongSubDigits(digit const *a, Py_ssize_t size_a, digit const *b, Py_ssize_t size_b) {
482 // Sign of the result.
483 int sign = 1;
484
485 // Make sure we know a is the largest value.
486 if (size_a < size_b) {
487 sign = -1;
488
489 {
490 digit const *temp = a;
491 a = b;
492 b = temp;
493 }
494
495 {
496 Py_ssize_t temp = size_a;
497 size_a = size_b;
498 size_b = temp;
499 }
500 } else if (size_a == size_b) {
501 // Find highest digit where a and b differ:
502 Py_ssize_t i = size_a;
503 while (--i >= 0 && a[i] == b[i]) {
504 }
505
506 if (i < 0) {
507#if PYTHON_VERSION < 0x300
508 return (PyLongObject *)Nuitka_LongFromCLong(0);
509#else
510 // For Python3, we have this prepared.
511 PyObject *result = Nuitka_Long_GetSmallValue(0);
512 Py_INCREF(result);
513 return (PyLongObject *)result;
514#endif
515 }
516
517 if (a[i] < b[i]) {
518 sign = -1;
519
520 {
521 digit const *temp = a;
522 a = b;
523 b = temp;
524 }
525 }
526
527 size_a = size_b = i + 1;
528 }
529
530 PyLongObject *result = Nuitka_LongNew(size_a);
531 CHECK_OBJECT(result);
532
533 digit *r = Nuitka_LongGetDigitPointer(result);
534
535 digit borrow = 0;
536
537 Py_ssize_t i;
538 // First common digits.
539 for (i = 0; i < size_b; i++) {
540 borrow = a[i] - b[i] - borrow;
541 r[i] = borrow & PyLong_MASK;
542 borrow >>= PyLong_SHIFT;
543 borrow &= 1;
544 }
545 // Digits from largest one only.
546 for (; i < size_a; i++) {
547 borrow = a[i] - borrow;
548 r[i] = borrow & PyLong_MASK;
549 borrow >>= PyLong_SHIFT;
550 borrow &= 1;
551 }
552 assert(borrow == 0);
553
554 // Strip leading zeros.
555 while (i > 0 && r[i - 1] == 0) {
556 i -= 1;
557 }
558
559 Nuitka_LongSetDigitSizeAndNegative(result, i, sign < 0);
560
561#if PYTHON_VERSION >= 0x300
562 // Normalize small integers.
563 if (i <= 1) {
564 medium_result_value_t ival = MEDIUM_VALUE(result);
565
566 if (ival >= NUITKA_STATIC_SMALLINT_VALUE_MIN && ival < NUITKA_STATIC_SMALLINT_VALUE_MAX) {
567 Py_DECREF(result);
568
569 result = (PyLongObject *)Nuitka_Long_GetSmallValue(ival);
570 Py_INCREF(result);
571 }
572 }
573#endif
574
575 return result;
576}
577
578static PyObject *_Nuitka_LongSubInplaceDigits(PyObject *left, digit const *b, Py_ssize_t size_b, int sign) {
579 digit const *a = Nuitka_LongGetDigitPointer(left);
580 Py_ssize_t size_a = Nuitka_LongGetDigitSize(left);
581
582 digit const *aa = a;
583 digit const *bb = b;
584
585 // Make sure we know a is the largest value.
586 if (size_a < size_b) {
587 // Invert the sign of the result by swapping the order.
588 sign *= -1;
589
590 {
591 aa = b;
592 bb = a;
593 }
594
595 {
596 Py_ssize_t temp = size_a;
597 size_a = size_b;
598 size_b = temp;
599 }
600 } else if (size_a == size_b) {
601 // Find highest digit where a and b differ:
602 Py_ssize_t i = size_a;
603 while (--i >= 0 && a[i] == b[i]) {
604 }
605
606 // TODO: This will benefit a lot by being in a template.
607 if (i < 0) {
608#if PYTHON_VERSION < 0x300
609 PyObject *r = const_long_0;
610#else
611 PyObject *r = Nuitka_Long_GetSmallValue(0);
612#endif
613 Py_INCREF(r);
614 Py_DECREF(left);
615
616 return r;
617 }
618
619 if (aa[i] < bb[i]) {
620 sign *= -1;
621
622 {
623 aa = b;
624 bb = a;
625 }
626 }
627
628 size_a = size_b = i + 1;
629 }
630
631 Py_ssize_t needed = size_a;
632
633 // Need to keep the old value around, or else we commit use after free potentially.
634 PyObject *old = left;
635
636 if (needed > Nuitka_LongGetDigitSize(left)) {
637 left = (PyObject *)Nuitka_LongNew(needed);
638 } else {
639 Py_INCREF(old);
640 }
641
642 digit *r = Nuitka_LongGetDigitPointer(left);
643
644 digit borrow = 0;
645
646 Py_ssize_t i;
647 // First common digits.
648 for (i = 0; i < size_b; i++) {
649 borrow = aa[i] - bb[i] - borrow;
650 r[i] = borrow & PyLong_MASK;
651 borrow >>= PyLong_SHIFT;
652 borrow &= 1;
653 }
654 // Digits from largest one only.
655 for (; i < size_a; i++) {
656 borrow = aa[i] - borrow;
657 r[i] = borrow & PyLong_MASK;
658 borrow >>= PyLong_SHIFT;
659 borrow &= 1;
660 }
661 assert(borrow == 0);
662
663 // Strip leading zeros.
664 while (i > 0 && r[i - 1] == 0) {
665 i -= 1;
666 }
667
668 Nuitka_LongSetDigitSizeAndNegative((PyLongObject *)left, i, (sign < 0));
669
670 // Release reference to old value
671 Py_DECREF(old);
672
673#if PYTHON_VERSION >= 0x300
674 // Normalize small integers.
675 if (i <= 1) {
676 medium_result_value_t ival = MEDIUM_VALUE(left);
677
678 if (ival >= NUITKA_STATIC_SMALLINT_VALUE_MIN && ival < NUITKA_STATIC_SMALLINT_VALUE_MAX) {
679 Py_DECREF(left);
680
681 left = Nuitka_Long_GetSmallValue(ival);
682 Py_INCREF(left);
683 }
684 }
685#endif
686
687 return left;
688}
689
690// Part of "Nuitka", an optimizing Python compiler that is compatible and
691// integrates with CPython, but also works on its own.
692//
693// Licensed under the Apache License, Version 2.0 (the "License");
694// you may not use this file except in compliance with the License.
695// You may obtain a copy of the License at
696//
697// http://www.apache.org/licenses/LICENSE-2.0
698//
699// Unless required by applicable law or agreed to in writing, software
700// distributed under the License is distributed on an "AS IS" BASIS,
701// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
702// See the License for the specific language governing permissions and
703// limitations under the License.