-
-
Notifications
You must be signed in to change notification settings - Fork 8.6k
Test REPR_B & fix test failures #17688
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
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #17688 +/- ##
==========================================
- Coverage 98.38% 98.37% -0.01%
==========================================
Files 171 171
Lines 22299 22306 +7
==========================================
+ Hits 21939 21944 +5
- Misses 360 362 +2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Code size report: |
|
Native & viper tests fail if --via-mpy even after accounting for small int size. I did some digging and I'm guessing it's because mpy-cross uses a fixed representation for integers in the code emitter, e.g., I chose to simply skip running any of the "native" tests, even though locally they succeed when not going via mpy. Because this was the easiest of the choices available. |
5a8c677 to
5dcf5fa
Compare
I'd prefer not to add this constant, if we can help it. Could the tests automatically detect the max int? Note there is already If we do need to add a constant, it might be better as |
9c67dca to
1bb4e0f
Compare
|
I reworked it to not require the added property. |
|
is this a known-to-be-flaky test? My changes shouldn't have touched it. |
Yes, it's known flaky. PR #17655 works around the issue. |
fb4d2d4 to
66a33e4
Compare
|
The one remaining failure appears to be |
|
@dpgeorge would it be easier if I split this up? The uctypes change to fit the integer width of REPR_B is the most important part for me in m68k-micropython. The float conversion bit is nice to have. The rest is "just" needed so that the tests of the added REPR_B unix build can pass. |
extmod/moductypes.c
Outdated
| // Here we need to set sign bit right | ||
| #define TYPE2SMALLINT(x, nbits) ((((int)x) << (32 - nbits)) >> 2) | ||
| #define GET_TYPE(x, nbits) (((x) >> (30 - nbits)) & ((1 << nbits) - 1)) | ||
| #define VALUE_MASK(type_nbits) (~((int)0xc0000000 >> type_nbits)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can any of these constants be computed using values from py/smallint.h (eg MP_SMALL_INT_POSITIVE_MASK) so that the definitions can be common for both object repr's?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In principle, yes. In practice, I spent some time trying to do so and failed. I will try some more tomorrow, because simplifying this would be good. Failing that, I will comment it better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I don't think it's possible. Because on 64-bit targets we still want this to use 32-bit small-int ranges. I think that's so the constants are the same across "all" targets.
Well, now with this change they won't be, repr B will have different constants. I guess we could in principle change all targets to use the same, smaller, sizing. But probably that's not a good idea.
So... how you have it looks like the best way to do it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did go ahead and do this in a way that widened the values to be whatever mp_int_t can accommodate. I guess this is the cause of the slight growth in the x86_64 build?
tests/basics/int_big_to_small.py
Outdated
| micropython.heap_lock() | ||
| try: | ||
| k = (1 << 29) | ||
| k = k + 20 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The compiler does a lot of constant folding, in particular the 1<<29 is done at compile time, not runtime. To make it clear that it's the add that may fail, I suggest:
k = 1 << 29
micropython.heap_lock()
try:
k += 1
except MemoryError:
print("SKIP")
raise SystemExit
finally:
micropython.heap_unlock()| @@ -0,0 +1,32 @@ | |||
| try: | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a comment that this test is specifically for repr B, where small ints are maximum (1<<29)-1 (and that it also works fine for other reprs).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add comment.
|
|
||
| # If the port has at least 32-bits then this test should pass. | ||
| print(test(29)) | ||
| print(test(28)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand why this change is needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test fails in REPR_B with the argument 29:
FAILURE /home/jepler/src/micropython/tests/results/stress_fun_call_limit.py
--- /home/jepler/src/micropython/tests/results/stress_fun_call_limit.py.exp 2025-09-16 09:50:39.647389928 -0500
+++ /home/jepler/src/micropython/tests/results/stress_fun_call_limit.py.out 2025-09-16 09:50:39.647389928 -0500
@@ -1,2 +1,2 @@
-32
+SyntaxError
SyntaxError
make: Leaving directory '/home/jepler/src/micropython/ports/unix'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test is if (i >= MP_SMALL_INT_BITS - 1) so the upper bound depends on the number of bits stolen by the obj_repr, not the actual number of bits in the underlying C type. Maybe this is another spot where MP_INT_BITS (introduced by me in this PR but local to uctypes) would be good to use, since this is about the C type not Python small ints.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, thanks for the reasoning.
since this is about the C type not Python small ints.
Actually, it is about small ints because star_args (which holds 1 << i) is encoded as a small int in the bytecode. So the C code is correct.
I suggest therefore:
- change the comment above this line to something like "This is the biggest number that will pass on a 32-bit port using object representation B, which has 1<<28 still fitting in a positive small int."
- the
for i in range(30, 70):below should be changed tofor i in range(28, 70):, so that it tests the transition from non-syntaxerror to syntaxerror (it should've beenrange(29,70)prior to this PR!)
tools/ci.sh
Outdated
| VARIANT=coverage | ||
| CFLAGS_EXTRA="-DMICROPY_OBJ_REPR=MICROPY_OBJ_REPR_B -Dmp_int_t=int32_t -Dmp_uint_t=uint32_t" | ||
| MICROPY_FORCE_32BIT=1 | ||
| RUN_TESTS_MPY_CROSS_FLAGS="--mpy-cross-flags=\"-march=x86 -msmall-int-bits=29\"" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't it be -msmall-int-bits=30?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, fixed.
| } | ||
|
|
||
| function ci_unix_coverage_repr_b_run_tests { | ||
| ci_unix_run_tests_helper "${CI_UNIX_OPTS_REPR_B[@]}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use ci_unix_run_tests_full_helper here? Or at least ci_unix_run_tests_full_no_native_helper?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As noted above
Native & viper tests fail if --via-mpy even after accounting for small int size.
full_no_native still runs viper tests via mpy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see.
Your remark is correct, that viper makes assumptions about small int encoding.
Maybe add a comment to that effect here, so we know in the future why it doesn't test mpy? (And maybe it can be fixed one day...)
No, it's best together to see exactly what's needed to get repr B working under CI. |
The field encodings for OBJ_REPR_B need to be slightly
different than for other representations, as there are
only 30 bits (rather than 31) available for small integers.
This accounts for about half of all the test failures
with an OBJ_REPR_B build, using a commandline like (wrapped):
```
make -j$(nproc) -C ports/unix/ VARIANT=coverage \
MICROPY_FORCE_32BIT=1 \
CFLAGS_EXTRA="-DMICROPY_OBJ_REPR=MICROPY_OBJ_REPR_B \
-Dmp_int_t=int32_t -Dmp_uint_t=uint32_t" test
```
The correct OFFSET_BITS and other values can be determined by the
number of lost bits between MP_SMALL_INT_BITS and the total number of
bits in the underlying C integral type.
Signed-off-by: Jeff Epler <[email protected]>
370dbbf to
2f17887
Compare
|
[actually pushed now] |
Different results are needed for different integer sizes. Signed-off-by: Jeff Epler <[email protected]>
Signed-off-by: Jeff Epler <[email protected]>
Signed-off-by: Jeff Epler <[email protected]>
The check for 'fits in a small int' is specific to the 31-bit int of other OBJ_REPRs. Signed-off-by: Jeff Epler <[email protected]>
This showed up some interesting errors (hopefully all fixed now). Signed-off-by: Jeff Epler <[email protected]>
| // Bit 0 is "is_signed", other bits give log2 of scalar size | ||
| #define GET_SCALAR_SIZE(val_type) (1 << ((val_type) >> 1)) | ||
| #define VALUE_MASK(type_nbits) ~((int)0x80000000 >> type_nbits) | ||
| #define VALUE_MASK(type_nbits) ~(MP_SMALL_INT_MIN >> type_nbits) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's nice to have these constants generalised like this. But, I think it's a (subtle) breaking change: this will change uctypes constant values and those can be embedded into .mpy files because the parser can optimise them to their int values (see mp_constants_table).
So, eg, someone using uctypes in a compiled .mpy file on a 64-bit arch, it won't work as expected after this change.
This change also means that, moving forward, building a 32-bit .mpy file on a 64-bit arch will use large ints for the substituted constants, rather than small ints. So it'll be suboptimal.
It's annoying, but we are stuck here.
Maybe the simplest way to handle this is just hard-code values like this:
#define MP_INT_BITS (32)
#define MP_UINT_MAX (0xffffffff)
#define MP_INT_MAX ((mp_int_t)(MP_UINT_MAX / 2))
#define MP_INT_MIN (-MP_INT_MAX - 1)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Upon further thought, this just isn't going to work at all. Consider the situation where mpy-cross builds a .mpy file using uctypes. That will use 31-bit small ints to encode these uctypes constants. Then if that .mpy is loaded on a obj-repr-B system the constant won't match the constant values used by that target.
There's not really an easy way to fix this. Either we need to change the constants for all targets so they fit on repr B. Or we need to have a way of storing in the .mpy which bit size was used. Both those options require a bump to the .mpy version (and the latter option is pretty tricky).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main point here is, if we want to allow saving of .mpy files that use uctypes, the constants must be the same across all platforms.
Some ways to make it work for repr B, or work around the issue :
- Store the constants as big ints, instead of small ints; see eg
sys.maxsizefor how to do that. I tried this and it can work, but it requires quite a few changes (eg MP_OBJ_SMALL_INT_VALUE -> mp_obj_get_int_truncated everywhere inmoductypes.c) - disable
uctypesfor obj repr B - disable loading of .mpy files for obj repr B
|
Thanks for all that background, I was unaware of I think that for my purposes, I can accept the cost of storing these as long ints and the code/memory overhead of doing so. I'll spin a fresh PR that will use macros to switch between "generic integer" APIs specifically in the case of REPR_B and the SMALL_INT APIs for all other REPRs. The other alternative I could think of would be to disable uctypes in REPR_B builds here in micropython and I just carry the incompatible mpy constant changes over in m68k-micropython. I'm good with either of those alternatives, but honestly as I think about it the 2nd is almost preferable (and I can put back REPR_B uctypes testing over in my m68k-micropython fork). |
|
Closing in favor of #18109 |
Summary
For m68k-micropython I'm interested in REPR_B. However, CI doesn't check any REPR_B builds, so of course there were test failures.
This patch series adds a 32-bit REPR_B build (i686 Linux) and fixes the test failures that arose.
Testing
I ran the tests locally with a REPR_B build. I'll check the CI for other failures.
Trade-offs and Alternatives
Just for the benefit of one test in the testsuite, I added
micropython.small_int_max. Instead, a few tests could be changed so that they just use numbers sized to work on "any repr that gets tested".