diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml
index d16122b720b6c..83afee8f1889a 100644
--- a/.github/workflows/examples.yml
+++ b/.github/workflows/examples.yml
@@ -20,6 +20,6 @@ jobs:
steps:
- uses: actions/checkout@v5
- name: Build
- run: make -C examples/embedding -f micropython_embed.mk && make -C examples/embedding
+ run: tools/ci.sh embedding_build
- name: Run
- run: ./examples/embedding/embed | grep "hello world"
+ run: tools/ci.sh embedding_run_tests
diff --git a/.github/workflows/ports_unix.yml b/.github/workflows/ports_unix.yml
index deee7b265fcb7..c54efc4fd62f6 100644
--- a/.github/workflows/ports_unix.yml
+++ b/.github/workflows/ports_unix.yml
@@ -160,6 +160,20 @@ jobs:
if: failure()
run: tests/run-tests.py --print-failures
+ repr_e:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v5
+ - name: Install packages
+ run: tools/ci.sh unix_32bit_setup
+ - name: Build
+ run: tools/ci.sh unix_repr_e_build
+ - name: Run main test suite
+ run: tools/ci.sh unix_repr_e_run_tests
+ - name: Print failures
+ if: failure()
+ run: tests/run-tests.py --print-failures
+
gil_enabled:
runs-on: ubuntu-latest
steps:
diff --git a/docs/develop/memorymgt.rst b/docs/develop/memorymgt.rst
index 5b1690cc827b3..6dfbb7b9af9aa 100644
--- a/docs/develop/memorymgt.rst
+++ b/docs/develop/memorymgt.rst
@@ -61,6 +61,9 @@ See ``py/mpconfig.h`` for the specific details of the available representations.
**Pointer tagging**
+This section describes how objects are stored in ``OBJ_REPR_A``. Terser descriptions of other object
+representations are in ``py/mpconfig.h``.
+
Because pointers are word-aligned, when they are stored in an ``mp_obj_t`` the
lower bits of this object handle will be zero. For example on a 32-bit architecture
the lower 2 bits will be zero:
diff --git a/ports/cc3200/bootmgr/bootloader.mk b/ports/cc3200/bootmgr/bootloader.mk
index 39a960ef4fb0a..e253be27e4510 100644
--- a/ports/cc3200/bootmgr/bootloader.mk
+++ b/ports/cc3200/bootmgr/bootloader.mk
@@ -123,6 +123,10 @@ $(BUILD)/bootloader.bin: $(BUILD)/bootmgr.bin
$(ECHO) "Create $@"
$(Q)$(SHELL) $(BOOT_GEN) $(BUILD)
+# Create an empty "float_consts.h" needed by py/mkrules.mk
+$(HEADER_BUILD)/float_consts.h: | $(HEADER_BUILD)
+ touch $@
+
# Create an empty "qstrdefs.generated.h" needed by py/mkrules.mk
$(HEADER_BUILD)/qstrdefs.generated.h: | $(HEADER_BUILD)
touch $@
diff --git a/ports/embed/embed.mk b/ports/embed/embed.mk
index bea5e5d65bbab..66641231f314e 100644
--- a/ports/embed/embed.mk
+++ b/ports/embed/embed.mk
@@ -25,6 +25,7 @@ CFLAGS += -Wall -Werror -std=c99
# Define the required generated header files.
GENHDR_OUTPUT = $(addprefix $(BUILD)/genhdr/, \
+ float_consts.h \
moduledefs.h \
mpversion.h \
qstrdefs.generated.h \
diff --git a/ports/unix/Makefile b/ports/unix/Makefile
index 81751bf0f7b2c..a67282c0fbb4f 100644
--- a/ports/unix/Makefile
+++ b/ports/unix/Makefile
@@ -241,7 +241,9 @@ endif
CXXFLAGS += $(filter-out -Wmissing-prototypes -Wold-style-definition -std=gnu99,$(CFLAGS) $(CXXFLAGS_MOD))
ifeq ($(MICROPY_FORCE_32BIT),1)
-RUN_TESTS_MPY_CROSS_FLAGS = --mpy-cross-flags='-march=x86'
+$(info RUN_TESTS_MPY_CROSS_FLAGS=$(RUN_TESTS_MPY_CROSS_FLAGS))
+RUN_TESTS_MPY_CROSS_FLAGS ?= --mpy-cross-flags='-march=x86'
+$(info RUN_TESTS_MPY_CROSS_FLAGS=$(RUN_TESTS_MPY_CROSS_FLAGS))
endif
ifeq ($(CROSS_COMPILE),arm-linux-gnueabi-)
diff --git a/ports/unix/main.c b/ports/unix/main.c
index db50e12f59821..e2fa6add4ab71 100644
--- a/ports/unix/main.c
+++ b/ports/unix/main.c
@@ -60,6 +60,11 @@
static bool compile_only = false;
static uint emit_opt = MP_EMIT_OPT_NONE;
+
+#if defined(MICROPY_UNIX_COVERAGE) && !defined(NO_QSTR)
+#include "genhdr/float_consts.h"
+#endif
+
#if MICROPY_ENABLE_GC
// Heap size of GC heap (if enabled)
// Make it larger on a 64 bit machine, because pointers are larger.
@@ -613,6 +618,7 @@ MP_NOINLINE int main_(int argc, char **argv) {
MP_DECLARE_CONST_FUN_OBJ_0(extra_cpp_coverage_obj);
mp_store_global(MP_QSTR_extra_coverage, MP_OBJ_FROM_PTR(&extra_coverage_obj));
mp_store_global(MP_QSTR_extra_cpp_coverage, MP_OBJ_FROM_PTR(&extra_cpp_coverage_obj));
+ mp_store_global(MP_QSTR_one_quarter, (mp_obj_t)MP_CONST_FLOAT_0__25);
}
#endif
diff --git a/ports/unix/variants/repr_e/mpconfigvariant.h b/ports/unix/variants/repr_e/mpconfigvariant.h
new file mode 100644
index 0000000000000..82fd84c633bd6
--- /dev/null
+++ b/ports/unix/variants/repr_e/mpconfigvariant.h
@@ -0,0 +1,43 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// This config exists to test that the MICROPY_OBJ_REPR_E variant
+// builds and passes tests. REPR_E uses memory-efficient floating point
+// objects encoded directly mp_obj_t (limited exponent range) plus boxed values
+// for full float range & precision. Therefore this variant should be built
+// using MICROPY_FORCE_32BIT=1
+
+#define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_E)
+#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
+
+// Set base feature level.
+#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES)
+
+// Not compatible with uctypes
+#define MICROPY_PY_UCTYPES (0)
+
+// Enable extra Unix features.
+#include "../mpconfigvariant_common.h"
diff --git a/ports/unix/variants/repr_e/mpconfigvariant.mk b/ports/unix/variants/repr_e/mpconfigvariant.mk
new file mode 100644
index 0000000000000..5765e86bc0ec8
--- /dev/null
+++ b/ports/unix/variants/repr_e/mpconfigvariant.mk
@@ -0,0 +1,8 @@
+# build interpreter with "bigints" implemented as "longlong"
+
+# otherwise, small int is essentially 64-bit
+MICROPY_FORCE_32BIT := 1
+
+MICROPY_PY_FFI := 0
+
+RUN_TESTS_MPY_CROSS_FLAGS = --mpy-cross-flags='-march=x86 -msmall-int-bits=30'
diff --git a/ports/windows/msvc/genhdr.targets b/ports/windows/msvc/genhdr.targets
index 57ec707ef91b4..e62fc5dee8c70 100644
--- a/ports/windows/msvc/genhdr.targets
+++ b/ports/windows/msvc/genhdr.targets
@@ -4,7 +4,7 @@
-
+
@@ -16,6 +16,7 @@
$(DestDir)qstrdefs.generated.h
$(DestDir)/moduledefs.collected
$(DestDir)/root_pointers.collected
+ $(DestDir)/float_consts.collected
$(DestDir)/compressed.collected
$(MICROPY_CPYTHON3)
python
@@ -48,7 +49,8 @@ using(var outFile = System.IO.File.CreateText(OutputFile)) {
foreach(var inFile in InputFiles)
foreach(var line in System.IO.File.ReadAllLines(inFile))
if((line.Contains(".c") && line.StartsWith("#line")) || line.Contains("MP_QSTR") ||
- line.Contains("MP_REGISTER") || line.Contains("MP_COMPRESSED_ROM_TEXT"))
+ line.Contains("MP_REGISTER") || line.Contains("MP_COMPRESSED_ROM_TEXT") ||
+ line.Contains("MP_CONST_FLOAT_"))
outFile.WriteLine( line );
}
]]>
@@ -122,6 +124,20 @@ using(var outFile = System.IO.File.CreateText(OutputFile)) {
+
+
+
+
+
+
+
+ $(DestDir)float_consts.h
+ $(DestFile).tmp
+
+
+
+
+
diff --git a/py/make_float_consts.py b/py/make_float_consts.py
new file mode 100644
index 0000000000000..89c1c598614c0
--- /dev/null
+++ b/py/make_float_consts.py
@@ -0,0 +1,134 @@
+"""
+This pre-processor parses a single file containing a list of
+MP_CONST_FLOAT items.
+
+These are used to generate a header with the required entries.
+"""
+
+import argparse
+import io
+import math
+import re
+import struct
+import sys
+
+from textwrap import dedent
+
+PATTERN = re.compile(r"MP_CONST_FLOAT_[_0-9a-zA-Z]+")
+
+
+def find_constant_floats(filename):
+ """Find any MP_CONST_FLOAT definitions in the provided file.
+
+ :param str filename: path to file to check
+ :return: List[variable_declaration]
+ """
+ with io.open(filename, encoding="utf-8") as c_file_obj:
+ content = c_file_obj.read()
+ return set(re.findall(PATTERN, content))
+
+
+def cast(from_, to, value):
+ return struct.unpack(to, struct.pack(from_, value))[0]
+
+
+def removeprefix(s, pfx): # assumes s starts with pfx
+ return s[len(pfx) :]
+
+
+repr_e_bias = 0x30080000
+repr_e_roll = 3
+
+
+def roll32_l(val, n):
+ return ((val << n) | (val >> (32 - n))) & 0xFFFFFFFF
+
+
+def generate_constant_float_definition(constant_name):
+ expression = removeprefix(constant_name, "MP_CONST_FLOAT_")
+ if expression.startswith("__"):
+ expression = "-" + expression[2:]
+ expression = expression.replace("__", ".")
+ value = eval(expression, math.__dict__)
+ if expression == "inf":
+ c_value = "INFINITY"
+ elif expression == "-inf":
+ c_value = "-INFINITY"
+ elif expression == "nan":
+ c_value = "NAN"
+ elif expression == "-0":
+ c_value = "-0."
+ else:
+ c_value = float(value)
+
+ print(f"// {constant_name} = {value}")
+ double_as_uint64 = cast("d", "Q", value)
+ repr_d_value = double_as_uint64 + 0x8004000000000000
+ float_as_uint32 = cast("f", "I", value)
+ repr_c_value = ((((float_as_uint32) & ~3) | 2) + 0x80800000) & 0xFFFFFFFF
+ repr_e_value = roll32_l((float_as_uint32 + repr_e_bias) & 0xFFFFFFFF, repr_e_roll)
+
+ print(
+ dedent(f"""\
+ #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B
+ extern const struct _mp_obj_float_t {constant_name}_obj;
+ #define {constant_name} MP_ROM_PTR(&{constant_name}_obj)
+ #if defined(MP_FLOAT_CONSTS_IMPL)
+ const mp_obj_float_t {constant_name}_obj = {{ {{&mp_type_float}}, (mp_float_t)({c_value}) }};
+ #endif
+ #elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D
+ #define {constant_name} {{ ((mp_obj_t)((uint64_t){repr_d_value:#x})) }}
+ #elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C
+ #define {constant_name} ((mp_obj_t)((uint64_t){repr_c_value:#x}))
+ #else // REPR_E""")
+ )
+
+ if repr_e_value & 0x3 == 3:
+ print(f"#define {constant_name} ((mp_obj_t)((uint64_t){repr_e_value:#x}))")
+ else:
+ print(
+ dedent(f"""\
+ extern const struct _mp_obj_float_t {constant_name}_obj;
+ #define {constant_name} MP_ROM_PTR(&{constant_name}_obj)
+ #if defined(MP_FLOAT_CONSTS_IMPL)
+ const mp_obj_float_t {constant_name}_obj = {{ {{&mp_type_float}}, (mp_float_t)({c_value}) }};
+ #endif""")
+ )
+
+ print("""#endif""")
+ print()
+
+
+def generate_constant_float_header(constant_floats):
+ """Generate header with root pointer entries.
+
+ :param List[variable_declaration] constant_floats: root pointer declarations
+ :return: None
+ """
+
+ # Print header file for all external modules.
+ print(
+ dedent("""\
+ // Automatically generated by make_constant_floats.py.
+ #ifndef MICROPY_INCLUDED_FLOAT_CONSTS_H
+ #define MICROPY_INCLUDED_FLOAT_CONSTS_H
+ """)
+ )
+
+ for item in constant_floats:
+ generate_constant_float_definition(item)
+
+ print("#endif")
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("file", nargs=1, help="file with MP_CONST_FLOAT definitions")
+ args = parser.parse_args()
+
+ constant_floats = find_constant_floats(args.file[0])
+ generate_constant_float_header(sorted(constant_floats))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/py/makeqstrdefs.py b/py/makeqstrdefs.py
index dd514c7033dd7..b1c6d0f4073d4 100644
--- a/py/makeqstrdefs.py
+++ b/py/makeqstrdefs.py
@@ -25,6 +25,9 @@
# Extract MP_REGISTER_ROOT_POINTER(...) macros.
_MODE_ROOT_POINTER = "root_pointer"
+# Extract MP_CONST_FLOAT_ macros.
+_MODE_FLOAT_CONST = "float_const"
+
class PreprocessorError(Exception):
pass
@@ -103,6 +106,8 @@ def process_file(f):
)
elif args.mode == _MODE_ROOT_POINTER:
re_match = re.compile(r"MP_REGISTER_ROOT_POINTER\(.*?\);")
+ elif args.mode == _MODE_FLOAT_CONST:
+ re_match = re.compile(r"MP_CONST_FLOAT_[_a-zA-Z0-9]+")
output = []
last_fname = None
for line in f:
@@ -122,7 +127,12 @@ def process_file(f):
if args.mode == _MODE_QSTR:
name = match.replace("MP_QSTR_", "")
output.append("Q(" + name + ")")
- elif args.mode in (_MODE_COMPRESS, _MODE_MODULE, _MODE_ROOT_POINTER):
+ elif args.mode in (
+ _MODE_COMPRESS,
+ _MODE_MODULE,
+ _MODE_ROOT_POINTER,
+ _MODE_FLOAT_CONST,
+ ):
output.append(match)
if last_fname:
@@ -220,7 +230,13 @@ class Args:
args.output_dir = sys.argv[4]
args.output_file = None if len(sys.argv) == 5 else sys.argv[5] # Unused for command=split
- if args.mode not in (_MODE_QSTR, _MODE_COMPRESS, _MODE_MODULE, _MODE_ROOT_POINTER):
+ if args.mode not in (
+ _MODE_QSTR,
+ _MODE_COMPRESS,
+ _MODE_MODULE,
+ _MODE_ROOT_POINTER,
+ _MODE_FLOAT_CONST,
+ ):
print("error: mode %s unrecognised" % sys.argv[2])
sys.exit(2)
diff --git a/py/mkrules.cmake b/py/mkrules.cmake
index e3d769cc59b5c..8884ff464e9ce 100644
--- a/py/mkrules.cmake
+++ b/py/mkrules.cmake
@@ -14,6 +14,9 @@ set(MICROPY_MODULEDEFS "${MICROPY_GENHDR_DIR}/moduledefs.h")
set(MICROPY_ROOT_POINTERS_SPLIT "${MICROPY_GENHDR_DIR}/root_pointers.split")
set(MICROPY_ROOT_POINTERS_COLLECTED "${MICROPY_GENHDR_DIR}/root_pointers.collected")
set(MICROPY_ROOT_POINTERS "${MICROPY_GENHDR_DIR}/root_pointers.h")
+set(MICROPY_FLOAT_CONSTS_SPLIT "${MICROPY_GENHDR_DIR}/float_consts.split")
+set(MICROPY_FLOAT_CONSTS_COLLECTED "${MICROPY_GENHDR_DIR}/float_consts.collected")
+set(MICROPY_FLOAT_CONSTS "${MICROPY_GENHDR_DIR}/float_consts.h")
set(MICROPY_COMPRESSED_SPLIT "${MICROPY_GENHDR_DIR}/compressed.split")
set(MICROPY_COMPRESSED_COLLECTED "${MICROPY_GENHDR_DIR}/compressed.collected")
set(MICROPY_COMPRESSED_DATA "${MICROPY_GENHDR_DIR}/compressed.data.h")
@@ -91,6 +94,7 @@ target_sources(${MICROPY_TARGET} PRIVATE
${MICROPY_MPVERSION}
${MICROPY_QSTRDEFS_GENERATED}
${MICROPY_MODULEDEFS}
+ ${MICROPY_FLOAT_CONSTS}
${MICROPY_ROOT_POINTERS}
)
@@ -219,6 +223,32 @@ add_custom_command(
DEPENDS ${MICROPY_ROOT_POINTERS_COLLECTED} ${MICROPY_PY_DIR}/make_root_pointers.py
)
+# Generate float_consts.h
+
+add_custom_command(
+ OUTPUT ${MICROPY_FLOAT_CONSTS_SPLIT}
+ COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makeqstrdefs.py split float_const ${MICROPY_GENHDR_DIR}/qstr.i.last ${MICROPY_GENHDR_DIR}/float_const _
+ COMMAND touch ${MICROPY_FLOAT_CONSTS_SPLIT}
+ DEPENDS ${MICROPY_QSTRDEFS_LAST}
+ VERBATIM
+ COMMAND_EXPAND_LISTS
+)
+
+add_custom_command(
+ OUTPUT ${MICROPY_FLOAT_CONSTS_COLLECTED}
+ COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makeqstrdefs.py cat float_const _ ${MICROPY_GENHDR_DIR}/float_const ${MICROPY_FLOAT_CONSTS_COLLECTED}
+ BYPRODUCTS "${MICROPY_FLOAT_CONSTS_COLLECTED}.hash"
+ DEPENDS ${MICROPY_FLOAT_CONSTS_SPLIT}
+ VERBATIM
+ COMMAND_EXPAND_LISTS
+)
+
+add_custom_command(
+ OUTPUT ${MICROPY_FLOAT_CONSTS}
+ COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/make_float_consts.py ${MICROPY_FLOAT_CONSTS_COLLECTED} > ${MICROPY_FLOAT_CONSTS}
+ DEPENDS ${MICROPY_FLOAT_CONSTS_COLLECTED} ${MICROPY_PY_DIR}/make_float_consts.py
+)
+
# Generate compressed.data.h
add_custom_command(
@@ -314,6 +344,7 @@ if(MICROPY_FROZEN_MANIFEST)
DEPENDS
${MICROPY_QSTRDEFS_GENERATED}
${MICROPY_ROOT_POINTERS}
+ ${MICROPY_FLOAT_CONSTS}
${MICROPY_MPYCROSS_DEPENDENCY}
VERBATIM
)
diff --git a/py/mkrules.mk b/py/mkrules.mk
index 4968ed58b03ea..3d0a4395030d7 100644
--- a/py/mkrules.mk
+++ b/py/mkrules.mk
@@ -18,7 +18,7 @@ HELP_MPY_LIB_SUBMODULE ?= "\033[1;31mError: micropython-lib submodule is not ini
OBJ_EXTRA_ORDER_DEPS =
# Generate header files.
-OBJ_EXTRA_ORDER_DEPS += $(HEADER_BUILD)/moduledefs.h $(HEADER_BUILD)/root_pointers.h
+OBJ_EXTRA_ORDER_DEPS += $(HEADER_BUILD)/moduledefs.h $(HEADER_BUILD)/root_pointers.h $(HEADER_BUILD)/float_consts.h
ifeq ($(MICROPY_ROM_TEXT_COMPRESSION),1)
# If compression is enabled, trigger the build of compressed.data.h...
@@ -165,6 +165,16 @@ $(HEADER_BUILD)/root_pointers.collected: $(HEADER_BUILD)/root_pointers.split
$(ECHO) "GEN $@"
$(Q)$(PYTHON) $(PY_SRC)/makeqstrdefs.py cat root_pointer _ $(HEADER_BUILD)/root_pointer $@
+# Floating point constants via MP_FLOAT_
+$(HEADER_BUILD)/float_consts.split: $(HEADER_BUILD)/qstr.i.last
+ $(ECHO) "GEN $@"
+ $(Q)$(PYTHON) $(PY_SRC)/makeqstrdefs.py split float_const $< $(HEADER_BUILD)/float_const _
+ $(Q)$(TOUCH) $@
+
+$(HEADER_BUILD)/float_consts.collected: $(HEADER_BUILD)/float_consts.split
+ $(ECHO) "GEN $@"
+ $(Q)$(PYTHON) $(PY_SRC)/makeqstrdefs.py cat float_const _ $(HEADER_BUILD)/float_const $@
+
# Compressed error strings.
$(HEADER_BUILD)/compressed.split: $(HEADER_BUILD)/qstr.i.last
$(ECHO) "GEN $@"
diff --git a/py/modcmath.c b/py/modcmath.c
index 33cb00cbe7e69..6a6d352b3f9b9 100644
--- a/py/modcmath.c
+++ b/py/modcmath.c
@@ -30,6 +30,10 @@
#include
+#ifndef NO_QSTR
+#include "genhdr/float_consts.h"
+#endif
+
// phase(z): returns the phase of the number z in the range (-pi, +pi]
static mp_obj_t mp_cmath_phase(mp_obj_t z_obj) {
mp_float_t real, imag;
@@ -114,8 +118,8 @@ static MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_sin_obj, mp_cmath_sin);
static const mp_rom_map_elem_t mp_module_cmath_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_cmath) },
- { MP_ROM_QSTR(MP_QSTR_e), mp_const_float_e },
- { MP_ROM_QSTR(MP_QSTR_pi), mp_const_float_pi },
+ { MP_ROM_QSTR(MP_QSTR_e), MP_CONST_FLOAT_e },
+ { MP_ROM_QSTR(MP_QSTR_pi), MP_CONST_FLOAT_pi },
{ MP_ROM_QSTR(MP_QSTR_phase), MP_ROM_PTR(&mp_cmath_phase_obj) },
{ MP_ROM_QSTR(MP_QSTR_polar), MP_ROM_PTR(&mp_cmath_polar_obj) },
{ MP_ROM_QSTR(MP_QSTR_rect), MP_ROM_PTR(&mp_cmath_rect_obj) },
diff --git a/py/modmath.c b/py/modmath.c
index 045c842150e44..2acd9c376f5bf 100644
--- a/py/modmath.c
+++ b/py/modmath.c
@@ -27,6 +27,10 @@
#include "py/builtin.h"
#include "py/runtime.h"
+#ifndef NO_QSTR
+#include "genhdr/float_consts.h"
+#endif
+
#if MICROPY_PY_BUILTINS_FLOAT && MICROPY_PY_MATH
#include
@@ -388,12 +392,12 @@ static MP_DEFINE_CONST_FUN_OBJ_1(mp_math_factorial_obj, mp_math_factorial);
static const mp_rom_map_elem_t mp_module_math_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_math) },
- { MP_ROM_QSTR(MP_QSTR_e), mp_const_float_e },
- { MP_ROM_QSTR(MP_QSTR_pi), mp_const_float_pi },
+ { MP_ROM_QSTR(MP_QSTR_e), MP_CONST_FLOAT_e },
+ { MP_ROM_QSTR(MP_QSTR_pi), MP_CONST_FLOAT_pi },
#if MICROPY_PY_MATH_CONSTANTS
- { MP_ROM_QSTR(MP_QSTR_tau), mp_const_float_tau },
- { MP_ROM_QSTR(MP_QSTR_inf), mp_const_float_inf },
- { MP_ROM_QSTR(MP_QSTR_nan), mp_const_float_nan },
+ { MP_ROM_QSTR(MP_QSTR_tau), MP_CONST_FLOAT_tau },
+ { MP_ROM_QSTR(MP_QSTR_inf), MP_CONST_FLOAT_inf },
+ { MP_ROM_QSTR(MP_QSTR_nan), MP_CONST_FLOAT_nan },
#endif
{ MP_ROM_QSTR(MP_QSTR_sqrt), MP_ROM_PTR(&mp_math_sqrt_obj) },
{ MP_ROM_QSTR(MP_QSTR_pow), MP_ROM_PTR(&mp_math_pow_obj) },
diff --git a/py/mpconfig.h b/py/mpconfig.h
index 97c40389b32f0..cdc841520e8db 100644
--- a/py/mpconfig.h
+++ b/py/mpconfig.h
@@ -159,6 +159,22 @@
// to the garbage collector.
#define MICROPY_OBJ_REPR_D (3)
+// A MicroPython object is a machine word having the following form (called R):
+// - pppppppp pppppppp pppppppp pppppp00 ptr (4 byte alignment)
+// - iiiiiiii iiiiiiii iiiiiiii iiiiii01 small int with 30-bit signed value
+// - 00000000 00qqqqqq qqqqqqqq qqqqq010 str with 19-bit qstr value
+// - 00000000 00000000 00000000 0ssss110 immediate object with 4-bit value
+// - xxxxxxxx xxxxxxxx xxxxxxxx xxxxxx11 limited-exponent 32-bit fp.
+// Float stored by: O = (R + 0x30800000 >>> 3), R = (O <<< 3) - 0x30800000
+// where <<< and >>> denote a 32-bit rotate operation.
+// This scheme only works with 32-bit word size and float enabled.
+// zeros, infinities, nan, and values with large/small exponents are "boxed"
+// This representation is loosely inspired by the paper Float Self-Tagging
+// by Olivier Melançon, Manuel Serrano, Marc Feeley
+// (https://arxiv.org/abs/2411.16544)
+#define MICROPY_OBJ_REPR_E (4)
+
+
#ifndef MICROPY_OBJ_REPR
#define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_A)
#endif
diff --git a/py/obj.c b/py/obj.c
index 26a912fc6825e..047f129d62af7 100644
--- a/py/obj.c
+++ b/py/obj.c
@@ -94,7 +94,7 @@ const mp_obj_type_t *MICROPY_WRAP_MP_OBJ_GET_TYPE(mp_obj_get_type)(mp_const_obj_
} else if (mp_obj_is_qstr(o_in)) {
return &mp_type_str;
#if MICROPY_PY_BUILTINS_FLOAT && ( \
- MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D)
+ MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_E)
} else if (mp_obj_is_float(o_in)) {
return &mp_type_float;
#endif
diff --git a/py/obj.h b/py/obj.h
index 9a5d273cdd297..407d8b8c445d1 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -102,21 +102,6 @@ static inline bool mp_obj_is_immediate_obj(mp_const_obj_t o) {
#define MP_OBJ_NEW_IMMEDIATE_OBJ(val) ((mp_obj_t)(((val) << 3) | 6))
#if MICROPY_PY_BUILTINS_FLOAT
-#define mp_const_float_e MP_ROM_PTR(&mp_const_float_e_obj)
-#define mp_const_float_pi MP_ROM_PTR(&mp_const_float_pi_obj)
-#if MICROPY_PY_MATH_CONSTANTS
-#define mp_const_float_tau MP_ROM_PTR(&mp_const_float_tau_obj)
-#define mp_const_float_inf MP_ROM_PTR(&mp_const_float_inf_obj)
-#define mp_const_float_nan MP_ROM_PTR(&mp_const_float_nan_obj)
-#endif
-extern const struct _mp_obj_float_t mp_const_float_e_obj;
-extern const struct _mp_obj_float_t mp_const_float_pi_obj;
-#if MICROPY_PY_MATH_CONSTANTS
-extern const struct _mp_obj_float_t mp_const_float_tau_obj;
-extern const struct _mp_obj_float_t mp_const_float_inf_obj;
-extern const struct _mp_obj_float_t mp_const_float_nan_obj;
-#endif
-
#define mp_obj_is_float(o) mp_obj_is_type((o), &mp_type_float)
mp_float_t mp_obj_float_get(mp_obj_t self_in);
mp_obj_t mp_obj_new_float(mp_float_t value);
@@ -147,21 +132,6 @@ static inline bool mp_obj_is_immediate_obj(mp_const_obj_t o) {
#define MP_OBJ_NEW_IMMEDIATE_OBJ(val) ((mp_obj_t)(((val) << 3) | 7))
#if MICROPY_PY_BUILTINS_FLOAT
-#define mp_const_float_e MP_ROM_PTR(&mp_const_float_e_obj)
-#define mp_const_float_pi MP_ROM_PTR(&mp_const_float_pi_obj)
-#if MICROPY_PY_MATH_CONSTANTS
-#define mp_const_float_tau MP_ROM_PTR(&mp_const_float_tau_obj)
-#define mp_const_float_inf MP_ROM_PTR(&mp_const_float_inf_obj)
-#define mp_const_float_nan MP_ROM_PTR(&mp_const_float_nan_obj)
-#endif
-extern const struct _mp_obj_float_t mp_const_float_e_obj;
-extern const struct _mp_obj_float_t mp_const_float_pi_obj;
-#if MICROPY_PY_MATH_CONSTANTS
-extern const struct _mp_obj_float_t mp_const_float_tau_obj;
-extern const struct _mp_obj_float_t mp_const_float_inf_obj;
-extern const struct _mp_obj_float_t mp_const_float_nan_obj;
-#endif
-
#define mp_obj_is_float(o) mp_obj_is_type((o), &mp_type_float)
mp_float_t mp_obj_float_get(mp_obj_t self_in);
mp_obj_t mp_obj_new_float(mp_float_t value);
@@ -185,16 +155,8 @@ static inline bool mp_obj_is_small_int(mp_const_obj_t o) {
#if MICROPY_PY_BUILTINS_FLOAT
#include
-// note: MP_OBJ_NEW_CONST_FLOAT should be a MP_ROM_PTR but that macro isn't available yet
#define MP_OBJ_NEW_CONST_FLOAT(f) ((mp_obj_t)((((((uint64_t)f) & ~3) | 2) + 0x80800000) & 0xffffffff))
-#define mp_const_float_e MP_OBJ_NEW_CONST_FLOAT(0x402df854)
-#define mp_const_float_pi MP_OBJ_NEW_CONST_FLOAT(0x40490fdb)
#define mp_const_float_nan MP_OBJ_NEW_CONST_FLOAT(0x7fc00000)
-#if MICROPY_PY_MATH_CONSTANTS
-#define mp_const_float_tau MP_OBJ_NEW_CONST_FLOAT(0x40c90fdb)
-#define mp_const_float_inf MP_OBJ_NEW_CONST_FLOAT(0x7f800000)
-#endif
-
static inline bool mp_obj_is_float(mp_const_obj_t o) {
// Ensure that 32-bit arch can only use single precision.
MP_STATIC_ASSERT(sizeof(mp_float_t) <= sizeof(mp_obj_t));
@@ -242,7 +204,8 @@ static inline bool mp_obj_is_obj(mp_const_obj_t o) {
}
#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D
-
+#include
+#define mp_const_float_nan {((mp_obj_t)((uint64_t)0x7ff8000000000000 + 0x8004000000000000))}
static inline bool mp_obj_is_small_int(mp_const_obj_t o) {
return (((uint64_t)(o)) & 0xffff000000000000) == 0x0001000000000000;
}
@@ -267,15 +230,6 @@ static inline bool mp_obj_is_immediate_obj(mp_const_obj_t o) {
#error MICROPY_OBJ_REPR_D requires MICROPY_FLOAT_IMPL_DOUBLE
#endif
-#include
-#define mp_const_float_e {((mp_obj_t)((uint64_t)0x4005bf0a8b145769 + 0x8004000000000000))}
-#define mp_const_float_pi {((mp_obj_t)((uint64_t)0x400921fb54442d18 + 0x8004000000000000))}
-#define mp_const_float_nan {((mp_obj_t)((uint64_t)0x7ff8000000000000 + 0x8004000000000000))}
-#if MICROPY_PY_MATH_CONSTANTS
-#define mp_const_float_tau {((mp_obj_t)((uint64_t)0x401921fb54442d18 + 0x8004000000000000))}
-#define mp_const_float_inf {((mp_obj_t)((uint64_t)0x7ff0000000000000 + 0x8004000000000000))}
-#endif
-
static inline bool mp_obj_is_float(mp_const_obj_t o) {
return ((uint64_t)(o) & 0xfffc000000000000) != 0;
}
@@ -323,6 +277,38 @@ typedef union _mp_rom_obj_t {
#define MP_ROM_PTR(p) {.u32 = {.lo = NULL, .hi = (p)}}
#endif
+#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_E
+
+#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_NONE
+#error "MICROPY_OBJ_REPR_E requires float to be enabled."
+#endif
+
+static inline bool mp_obj_is_small_int(mp_const_obj_t o) {
+ return (((mp_int_t)(o)) & 3) == 1;
+}
+#define MP_OBJ_SMALL_INT_VALUE(o) (((mp_int_t)(o)) >> 2)
+#define MP_OBJ_NEW_SMALL_INT(small_int) ((mp_obj_t)((((mp_uint_t)(small_int)) << 2) | 1))
+
+static inline bool mp_obj_is_qstr(mp_const_obj_t o) {
+ return (((mp_uint_t)(o)) & 0xff80000f) == 0x00000006;
+}
+#define MP_OBJ_QSTR_VALUE(o) (((mp_uint_t)(o)) >> 4)
+#define MP_OBJ_NEW_QSTR(qst) ((mp_obj_t)((((mp_uint_t)(qst)) << 4) | 0x00000006))
+
+static inline bool mp_obj_is_immediate_obj(mp_const_obj_t o) {
+ return (((mp_uint_t)(o)) & 0xff80000f) == 0x0000000e;
+}
+#define MP_OBJ_IMMEDIATE_OBJ_VALUE(o) (((mp_uint_t)(o)) >> 4)
+#define MP_OBJ_NEW_IMMEDIATE_OBJ(val) ((mp_obj_t)(((val) << 4) | 0xe))
+
+static inline bool mp_obj_is_obj(mp_const_obj_t o) {
+ return (((mp_int_t)(o)) & 3) == 0;
+}
+
+#include
+bool mp_obj_is_float(mp_const_obj_t o);
+mp_float_t mp_obj_float_get(mp_const_obj_t o);
+mp_obj_t mp_obj_new_float(mp_float_t f);
#endif
// Macros to convert between mp_obj_t and concrete object types.
diff --git a/py/objfloat.c b/py/objfloat.c
index b0ad70de4a3e8..599834aa50c9e 100644
--- a/py/objfloat.c
+++ b/py/objfloat.c
@@ -43,31 +43,15 @@
#include "py/formatfloat.h"
#if MICROPY_OBJ_REPR != MICROPY_OBJ_REPR_C && MICROPY_OBJ_REPR != MICROPY_OBJ_REPR_D
-
-// M_E and M_PI are not part of the math.h standard and may not be defined
-#ifndef M_E
-#define M_E (2.7182818284590452354)
-#endif
-#ifndef M_PI
-#define M_PI (3.14159265358979323846)
-#endif
-
typedef struct _mp_obj_float_t {
mp_obj_base_t base;
mp_float_t value;
} mp_obj_float_t;
-
-const mp_obj_float_t mp_const_float_e_obj = {{&mp_type_float}, (mp_float_t)M_E};
-const mp_obj_float_t mp_const_float_pi_obj = {{&mp_type_float}, (mp_float_t)M_PI};
-#if MICROPY_PY_MATH_CONSTANTS
-#ifndef NAN
-#error NAN macro is not defined
-#endif
-const mp_obj_float_t mp_const_float_tau_obj = {{&mp_type_float}, (mp_float_t)(2.0 * M_PI)};
-const mp_obj_float_t mp_const_float_inf_obj = {{&mp_type_float}, (mp_float_t)INFINITY};
-const mp_obj_float_t mp_const_float_nan_obj = {{&mp_type_float}, (mp_float_t)NAN};
#endif
+#ifndef NO_QSTR
+#define MP_FLOAT_CONSTS_IMPL
+#include "genhdr/float_consts.h"
#endif
#define MICROPY_FLOAT_ZERO MICROPY_FLOAT_CONST(0.0)
@@ -179,7 +163,71 @@ MP_DEFINE_CONST_OBJ_TYPE(
binary_op, float_binary_op
);
-#if MICROPY_OBJ_REPR != MICROPY_OBJ_REPR_C && MICROPY_OBJ_REPR != MICROPY_OBJ_REPR_D
+#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_E
+static mp_uint_t roll_l(mp_uint_t val, mp_uint_t n) {
+ return (val << n) | (val >> (32 - n));
+}
+#define MICROPY_FLOAT_ROLL (3)
+#define MICROPY_FLOAT_BIAS (0x30080000)
+#define MICROPY_FLOAT_TO_O(f) (roll_l((f) + MICROPY_FLOAT_BIAS, MICROPY_FLOAT_ROLL))
+#define MICROPY_FLOAT_FROM_O(f) (roll_l((f), 32 - MICROPY_FLOAT_ROLL) - MICROPY_FLOAT_BIAS)
+static const struct { mp_uint_t u;
+ mp_rom_obj_t o;
+} float_constants[] = {
+ { 0, MP_CONST_FLOAT_0 },
+ { 0x80000000, MP_CONST_FLOAT___0 },
+ { 0x7f800000, MP_CONST_FLOAT_inf },
+ { 0x8f800000, MP_CONST_FLOAT___inf },
+};
+
+mp_obj_t mp_obj_new_float(mp_float_t value) {
+ union {
+ mp_float_t f;
+ mp_uint_t u;
+ } num = {.f = value };
+ mp_uint_t u = MICROPY_FLOAT_TO_O(num.u);
+ if ((u & 0x3) == 3) {
+ return (mp_obj_t)u;
+ }
+ for (size_t i = 0; i < MP_ARRAY_SIZE(float_constants); i++) {
+ if (num.u == float_constants[i].u) {
+ return (mp_obj_t)float_constants[i].o;
+ }
+ }
+ if (isnan(value)) {
+ return (mp_obj_t)MP_CONST_FLOAT_nan;
+ }
+ // Don't use mp_obj_malloc here to avoid extra function call overhead.
+ mp_obj_float_t *o = m_new_obj(mp_obj_float_t);
+ o->base.type = &mp_type_float;
+ o->value = value;
+ return MP_OBJ_FROM_PTR(o);
+}
+mp_float_t mp_obj_float_get(mp_const_obj_t o) {
+ if (mp_obj_is_obj(o)) {
+ mp_obj_float_t *self = MP_OBJ_TO_PTR(o);
+ return self->value;
+ }
+ mp_uint_t u = (mp_uint_t)o;
+ u = (u << 3) | (u >> 29);
+ u = u - 0x30800000;
+ union {
+ mp_float_t f;
+ mp_uint_t u;
+ } num = {.u = MICROPY_FLOAT_FROM_O((mp_uint_t)o) };
+ return num.f;
+}
+bool mp_obj_is_float(mp_const_obj_t o) {
+ // Ensure that 32-bit arch can only use single precision.
+ MP_STATIC_ASSERT(sizeof(mp_float_t) <= sizeof(mp_obj_t));
+
+ if (mp_obj_is_obj(o)) {
+ const mp_obj_base_t *self = MP_OBJ_TO_PTR(o);
+ return self->type == &mp_type_float;
+ }
+ return ((mp_uint_t)(o) & 3) == 3;
+}
+#elif MICROPY_OBJ_REPR != MICROPY_OBJ_REPR_C && MICROPY_OBJ_REPR != MICROPY_OBJ_REPR_D
mp_obj_t mp_obj_new_float(mp_float_t value) {
// Don't use mp_obj_malloc here to avoid extra function call overhead.
diff --git a/py/objint.c b/py/objint.c
index 901264aca6564..27604ee250835 100644
--- a/py/objint.c
+++ b/py/objint.c
@@ -117,7 +117,7 @@ static mp_fp_as_int_class_t mp_classify_fp_as_int(mp_float_t val) {
}
// 8 * sizeof(uintptr_t) counts the number of bits for a small int
// TODO provide a way to configure this properly
- #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B
+ #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_E
if (e <= ((8 * sizeof(uintptr_t) + MP_FLOAT_EXP_BIAS - 4) << MP_FLOAT_EXP_SHIFT_I32)) {
return MP_FP_CLASS_FIT_SMALLINT;
}
diff --git a/py/py.mk b/py/py.mk
index e7716a1adc9a6..2036dd5f23501 100644
--- a/py/py.mk
+++ b/py/py.mk
@@ -261,6 +261,11 @@ $(HEADER_BUILD)/root_pointers.h: $(HEADER_BUILD)/root_pointers.collected $(PY_SR
@$(ECHO) "GEN $@"
$(Q)$(PYTHON) $(PY_SRC)/make_root_pointers.py $< > $@
+# build a list of registered root pointers for py/mpstate.h.
+$(HEADER_BUILD)/float_consts.h: $(HEADER_BUILD)/float_consts.collected $(PY_SRC)/make_float_consts.py
+ @$(ECHO) "GEN $@"
+ $(Q)$(PYTHON) $(PY_SRC)/make_float_consts.py $< > $@
+
# Standard C functions like memset need to be compiled with special flags so
# the compiler does not optimise these functions in terms of themselves.
CFLAGS_BUILTIN ?= -ffreestanding -fno-builtin -fno-lto
diff --git a/py/smallint.h b/py/smallint.h
index ec5b0af3b2867..9457866f2229c 100644
--- a/py/smallint.h
+++ b/py/smallint.h
@@ -41,7 +41,7 @@
// Mask to truncate mp_int_t to positive value
#define MP_SMALL_INT_POSITIVE_MASK ~(MP_OBJ_WORD_MSBIT_HIGH | (MP_OBJ_WORD_MSBIT_HIGH >> 1))
-#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B
+#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_E
#define MP_SMALL_INT_MIN ((mp_int_t)(((mp_int_t)MP_OBJ_WORD_MSBIT_HIGH) >> 2))
#define MP_SMALL_INT_FITS(n) ((((n) & MP_SMALL_INT_MIN) == 0) || (((n) & MP_SMALL_INT_MIN) == MP_SMALL_INT_MIN))
diff --git a/tests/ports/unix/extra_coverage.py b/tests/ports/unix/extra_coverage.py
index 2087e7ea2adc5..450a92bbe3177 100644
--- a/tests/ports/unix/extra_coverage.py
+++ b/tests/ports/unix/extra_coverage.py
@@ -149,3 +149,5 @@
foo_f()
print(foo_f == example_package.foo.f)
+
+print(one_quarter)
diff --git a/tests/ports/unix/extra_coverage.py.exp b/tests/ports/unix/extra_coverage.py.exp
index d11e5ee6f4215..45308ceee49f6 100644
--- a/tests/ports/unix/extra_coverage.py.exp
+++ b/tests/ports/unix/extra_coverage.py.exp
@@ -270,3 +270,4 @@ example_package.foo.bar.f
True
example_package.foo.f
True
+0.25
diff --git a/tools/ci.sh b/tools/ci.sh
index 42d231c458127..29607e9fbdd8d 100755
--- a/tools/ci.sh
+++ b/tools/ci.sh
@@ -937,6 +937,17 @@ function ci_unix_repr_b_run_tests {
ci_unix_run_tests_helper "${CI_UNIX_OPTS_REPR_B[@]}"
}
+function ci_unix_repr_e_build {
+ ci_unix_build_helper VARIANT=repr_e
+}
+
+function ci_unix_repr_e_run_tests {
+ # ci_unix_run_tests_full_no_native_helper is not used due to
+ # https://github.com/micropython/micropython/issues/18105
+ ci_unix_run_tests_helper VARIANT=repr_e
+}
+
+
########################################################################################
# ports/windows
@@ -1023,6 +1034,18 @@ function ci_alif_ae3_build {
make ${MAKEOPTS} -C ports/alif BOARD=ALIF_ENSEMBLE MCU_CORE=M55_DUAL
}
+########################################################################################
+# embedding
+#
+function ci_embedding_build {
+ make ${MAKEOPTS} -C examples/embedding -f micropython_embed.mk && make -C examples/embedding
+}
+
+function ci_embedding_run_tests {
+ set -o pipefail
+ ./examples/embedding/embed | grep "hello world"
+}
+
function _ci_help {
# Note: these lines must be indented with tab characters (required by bash <<-EOF)
cat <<-EOF