From 2328b6c25af76d0d96eb4927aa910b2c3cb8899d Mon Sep 17 00:00:00 2001 From: Jared Hance Date: Mon, 5 Oct 2026 17:38:35 +0000 Subject: [PATCH] [mypyc] Internally support static linking mode --- mypyc/codegen/emit.py | 7 +- mypyc/codegen/emitfunc.py | 10 +- mypyc/codegen/emitmodule.py | 199 +++++++++++------ mypyc/lib-rt/base64/librt_base64.c | 41 ++-- mypyc/lib-rt/base64/librt_base64.h | 10 + mypyc/lib-rt/base64/librt_base64_api.c | 8 + mypyc/lib-rt/base64/librt_base64_api.h | 5 + mypyc/lib-rt/internal/librt_internal.c | 201 +++++++++--------- mypyc/lib-rt/internal/librt_internal.h | 74 +++++-- mypyc/lib-rt/internal/librt_internal_api.c | 8 + mypyc/lib-rt/internal/librt_internal_api.h | 49 +++-- mypyc/lib-rt/module_shim.tmpl | 17 +- .../lib-rt/module_shim_no_gil_multiphase.tmpl | 18 +- mypyc/lib-rt/random/librt_random.c | 103 +++++---- mypyc/lib-rt/random/librt_random.h | 20 ++ mypyc/lib-rt/random/librt_random_api.c | 8 + mypyc/lib-rt/random/librt_random_api.h | 6 + mypyc/lib-rt/strings/librt_strings.c | 141 ++++++------ mypyc/lib-rt/strings/librt_strings.h | 22 ++ mypyc/lib-rt/strings/librt_strings_api.c | 8 + mypyc/lib-rt/strings/librt_strings_api.h | 8 + mypyc/lib-rt/threading/librt_threading.c | 73 ++++--- mypyc/lib-rt/threading/librt_threading.h | 15 ++ mypyc/lib-rt/threading/librt_threading_api.c | 8 + mypyc/lib-rt/threading/librt_threading_api.h | 6 + mypyc/lib-rt/time/librt_time.c | 29 ++- mypyc/lib-rt/time/librt_time.h | 9 + mypyc/lib-rt/time/librt_time_api.c | 8 + mypyc/lib-rt/time/librt_time_api.h | 5 + mypyc/lib-rt/vecs/librt_vecs.c | 4 + mypyc/lib-rt/vecs/librt_vecs.h | 142 +++++++++++++ mypyc/lib-rt/vecs/librt_vecs_api.c | 8 + mypyc/lib-rt/vecs/librt_vecs_api.h | 13 ++ mypyc/lib-rt/vecs/vec_nested.c | 15 +- mypyc/lib-rt/vecs/vec_t.c | 18 +- mypyc/lib-rt/vecs/vec_template.c | 18 +- mypyc/lib-rt/vecs_extra_ops.h | 4 + mypyc/options.py | 18 +- mypyc/primitives/misc_ops.py | 34 +-- mypyc/test-data/irbuild-classes.test | 32 +-- 40 files changed, 961 insertions(+), 461 deletions(-) diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index e78b3f343b283..cdca209d9c9d1 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -153,6 +153,9 @@ def __init__( self.names = names self.group_name = group_name self.group_map = group_map or {} + # Suffix that makes names of C globals local to the group unique across groups, + # so that groups can be statically linked together (empty if not using groups) + self.group_suffix = "_" + exported_name(group_name) if group_name else "" # Groups that this group depends on self.group_deps: set[str] = set() @@ -349,7 +352,9 @@ def static_name(self, id: str, module: str | None, prefix: str = STATIC_PREFIX) # the pointer also. star_maybe = "*" if lib_prefix else "" suffix = self.names.private_name(module or "", id) - return f"{star_maybe}{lib_prefix}{prefix}{suffix}" + # Statics without a module are local to the group, so make them unique per group + group_suffix = "" if module else self.context.group_suffix + return f"{star_maybe}{lib_prefix}{prefix}{suffix}{group_suffix}" def type_struct_name(self, cl: ClassIR) -> str: return self.static_name(cl.name, cl.module_name, prefix=TYPE_PREFIX) diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index 8637a2565006d..58ec718a7431a 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -394,10 +394,16 @@ def visit_load_error_value(self, op: LoadErrorValue) -> None: def visit_load_literal(self, op: LoadLiteral) -> None: index = self.literals.literal_index(op.value) if not is_int_rprimitive(op.type): - self.emit_line("%s = CPyStatics[%d];" % (self.reg(op), index), ann=op.value) + self.emit_line( + "%s = CPyStatics%s[%d];" + % (self.reg(op), self.emitter.context.group_suffix, index), + ann=op.value, + ) else: self.emit_line( - "%s = (CPyTagged)CPyStatics[%d] | 1;" % (self.reg(op), index), ann=op.value + "%s = (CPyTagged)CPyStatics%s[%d] | 1;" + % (self.reg(op), self.emitter.context.group_suffix, index), + ann=op.value, ) def get_attr_expr(self, obj: str, op: GetAttr | SetAttr, decl_cl: ClassIR) -> str: diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py index 1b21b51709db6..b6b56e57f9c94 100644 --- a/mypyc/codegen/emitmodule.py +++ b/mypyc/codegen/emitmodule.py @@ -669,6 +669,13 @@ def __init__( def group_suffix(self) -> str: return "_" + exported_name(self.group_name) if self.group_name else "" + def group_global(self, name: str) -> str: + """Make the name of a C global local to the group unique across groups. + + This allows groups to be linked together statically. + """ + return name + self.group_suffix + @property def short_group_suffix(self) -> str: return "_" + exported_name(self.group_name.split(".")[-1]) if self.group_name else "" @@ -779,7 +786,23 @@ def emit_dep_headers(decls: Emitter, internal: bool) -> None: declarations.emit_line(f'#include "__native{self.short_group_suffix}.h"') declarations.emit_line() - declarations.emit_line("int CPyGlobalsInit(void);") + # lib-rt and librt must be compiled for the same kind of linking as generated code + if self.compiler_options.static_linking: + declarations.emit_lines( + "#ifndef MYPYC_STATIC_LINKING", + '#error "Generated for static linking, but MYPYC_STATIC_LINKING is not defined"', + "#endif", + ) + else: + declarations.emit_lines( + "#ifdef MYPYC_STATIC_LINKING", + '#error "MYPYC_STATIC_LINKING is defined, but not generated for static linking"', + "#endif", + ) + declarations.emit_line() + declarations.emit_line(f"int {self.group_global('CPyGlobalsInit')}(void);") + if self.group_name and self.compiler_options.separate: + declarations.emit_line(f"int {self.group_global('ensure_deps')}(void);") declarations.emit_line() for module_name, module in self.modules.items(): @@ -795,9 +818,12 @@ def emit_dep_headers(decls: Emitter, internal: bool) -> None: for lib in sorted(self.context.group_deps): elib = exported_name(lib) short_lib = exported_name(lib.split(".")[-1]) + # With static linking, the export table of the other group is linked directly. + # Otherwise we have a copy, which is populated using a capsule. + storage = "extern " if self.compiler_options.static_linking else "" declarations.emit_lines( "#include <{}>".format(os.path.join(group_dir(lib), f"__native_{short_lib}.h")), - f"struct export_table_{elib} exports_{elib};", + f"{storage}struct export_table_{elib} exports_{elib};", ) sorted_decls = self.toposort_declarations() @@ -854,28 +880,44 @@ def generate_literal_tables(self) -> None: """ literals = self.context.literals # During module initialization we store all the constructed objects here - self.declare_global("PyObject *[%d]" % literals.num_literals(), "CPyStatics") + self.declare_global( + "PyObject *[%d]" % literals.num_literals(), self.group_global("CPyStatics") + ) # Descriptions of str literals init_str = c_string_array_initializer(literals.encoded_str_values()) - self.declare_global("const char * const []", "CPyLit_Str", initializer=init_str) + self.declare_global( + "const char * const []", self.group_global("CPyLit_Str"), initializer=init_str + ) # Descriptions of bytes literals init_bytes = c_string_array_initializer(literals.encoded_bytes_values()) - self.declare_global("const char * const []", "CPyLit_Bytes", initializer=init_bytes) + self.declare_global( + "const char * const []", self.group_global("CPyLit_Bytes"), initializer=init_bytes + ) # Descriptions of int literals init_int = c_string_array_initializer(literals.encoded_int_values()) - self.declare_global("const char * const []", "CPyLit_Int", initializer=init_int) + self.declare_global( + "const char * const []", self.group_global("CPyLit_Int"), initializer=init_int + ) # Descriptions of float literals init_floats = c_array_initializer(literals.encoded_float_values()) - self.declare_global("const double []", "CPyLit_Float", initializer=init_floats) + self.declare_global( + "const double []", self.group_global("CPyLit_Float"), initializer=init_floats + ) # Descriptions of complex literals init_complex = c_array_initializer(literals.encoded_complex_values()) - self.declare_global("const double []", "CPyLit_Complex", initializer=init_complex) + self.declare_global( + "const double []", self.group_global("CPyLit_Complex"), initializer=init_complex + ) # Descriptions of tuple literals init_tuple = c_array_initializer(literals.encoded_tuple_values()) - self.declare_global("const int []", "CPyLit_Tuple", initializer=init_tuple) + self.declare_global( + "const int []", self.group_global("CPyLit_Tuple"), initializer=init_tuple + ) # Descriptions of frozenset literals init_frozenset = c_array_initializer(literals.encoded_frozenset_values()) - self.declare_global("const int []", "CPyLit_FrozenSet", initializer=init_frozenset) + self.declare_global( + "const int []", self.group_global("CPyLit_FrozenSet"), initializer=init_frozenset + ) def generate_export_table(self, decl_emitter: Emitter, code_emitter: Emitter) -> None: """Generate the declaration and definition of the group's export struct. @@ -919,6 +961,9 @@ def generate_export_table(self, decl_emitter: Emitter, code_emitter: Emitter) -> }; To call `b.foo`, then, a function in another group would do `exports_b.CPyDef_bar(...)`. + + With static linking, there are no capsules. Instead, the table is a global + named exports_b, and other groups refer to it directly. """ decls = decl_emitter.context.declarations @@ -930,7 +975,11 @@ def generate_export_table(self, decl_emitter: Emitter, code_emitter: Emitter) -> decl_emitter.emit_line("};") - code_emitter.emit_lines("", f"static struct export_table{self.group_suffix} exports = {{") + if self.compiler_options.static_linking: + table = f"struct export_table{self.group_suffix} exports{self.group_suffix}" + else: + table = f"static struct export_table{self.group_suffix} exports" + code_emitter.emit_lines("", f"{table} = {{") for name, decl in decls.items(): if decl.needs_export: code_emitter.emit_line(f"&{name},") @@ -950,21 +999,21 @@ def generate_shared_lib_init(self, emitter: Emitter) -> None: values from all the modules. These capsules are stored in attributes of the shared library. + + With static linking, no capsules are created, since the shims, and other + groups, refer to the functions and the export table directly. """ assert self.group_name is not None emitter.emit_line() short_name = shared_lib_name(self.group_name).split(".")[-1] + static_linking = self.compiler_options.static_linking + ensure_deps = self.group_global("ensure_deps") - emitter.emit_lines( - f"static int exec_{short_name}(PyObject *module)", - "{", - "int res;", - "PyObject *capsule;", - "PyObject *tmp;", - "", - ) + emitter.emit_lines(f"static int exec_{short_name}(PyObject *module)", "{") + if not static_linking: + emitter.emit_lines("int res;", "PyObject *capsule;", "") lock_api = module_lock_api_name(self.group_name) emitter.emit_lines( @@ -973,11 +1022,11 @@ def generate_shared_lib_init(self, emitter: Emitter) -> None: f"if ({lock_api} == NULL) goto fail;", "}", "if (intern_strings() < 0) goto fail;", - "if (CPyGlobalsInit() < 0) goto fail;", + f"if ({self.group_global('CPyGlobalsInit')}() < 0) goto fail;", "", ) - if self.compiler_options.separate: + if self.compiler_options.separate and not static_linking: emitter.emit_lines( 'capsule = PyCapsule_New(&exports, "{}.exports", NULL);'.format( shared_lib_name(self.group_name) @@ -991,11 +1040,10 @@ def generate_shared_lib_init(self, emitter: Emitter) -> None: "goto fail;", "}", "", - # Expose ensure_deps_ as a capsule so the shim can call - # it before invoking the per-module init. - f"extern int ensure_deps_{short_name}(void);", - 'capsule = PyCapsule_New((void *)ensure_deps_{sh}, "{lib}.ensure_deps", NULL);'.format( - sh=short_name, lib=shared_lib_name(self.group_name) + # Expose ensure_deps as a capsule so the shim can call it before + # invoking the per-module init. + 'capsule = PyCapsule_New((void *){f}, "{lib}.ensure_deps", NULL);'.format( + f=ensure_deps, lib=shared_lib_name(self.group_name) ), "if (!capsule) {", "goto fail;", @@ -1008,50 +1056,54 @@ def generate_shared_lib_init(self, emitter: Emitter) -> None: "", ) - for mod in self.modules: - name = exported_name(mod) - if self.multi_phase_init: - capsule_func_name = module_exec_name(mod) - capsule_name_prefix = "exec_" - emitter.emit_line(f"extern int {capsule_func_name}(PyObject *);") - else: - capsule_func_name = module_init_name(mod) - capsule_name_prefix = "init_" - emitter.emit_line(f"extern PyObject *{capsule_func_name}(void);") - emitter.emit_lines( - 'capsule = PyCapsule_New((void *){}, "{}.{}{}", NULL);'.format( - capsule_func_name, shared_lib_name(self.group_name), capsule_name_prefix, name - ), - "if (!capsule) {", - "goto fail;", - "}", - f'res = PyObject_SetAttrString(module, "{capsule_name_prefix}{name}", capsule);', - "Py_DECREF(capsule);", - "if (res < 0) {", - "goto fail;", - "}", - "", - ) + # Expose the module init (or exec) functions to the shims. With static linking, + # shims call them directly instead. + if not static_linking: + for mod in self.modules: + name = exported_name(mod) + if self.multi_phase_init: + capsule_func_name = module_exec_name(mod) + capsule_name_prefix = "exec_" + emitter.emit_line(f"extern int {capsule_func_name}(PyObject *);") + else: + capsule_func_name = module_init_name(mod) + capsule_name_prefix = "init_" + emitter.emit_line(f"extern PyObject *{capsule_func_name}(void);") + emitter.emit_lines( + 'capsule = PyCapsule_New((void *){}, "{}.{}{}", NULL);'.format( + capsule_func_name, + shared_lib_name(self.group_name), + capsule_name_prefix, + name, + ), + "if (!capsule) {", + "goto fail;", + "}", + f'res = PyObject_SetAttrString(module, "{capsule_name_prefix}{name}", capsule);', + "Py_DECREF(capsule);", + "if (res < 0) {", + "goto fail;", + "}", + "", + ) # End of exec_: only sets up capsules/module attributes. # Cross-group imports (populating `exports_` tables) are split - # out into ensure_deps_() below and run later, from the - # shim's PyInit. See generate_shared_lib_init for details. + # out into ensure_deps() below and run later, from the shim's PyInit + # (or from the module exec function with static linking). emitter.emit_lines("return 0;", "fail:", "return -1;", "}") if self.compiler_options.separate: - # ensure_deps_(): populates cross-group exports tables. Run + # ensure_deps(): populates cross-group exports tables. Run # once, lazily, from the shim's PyInit just before invoking the # per-module init capsule. This defers cross-group imports out of # the shared-lib PyInit so they can't transitively trigger a # sibling package's __init__.py while another package __init__.py - # is still mid-flight. + # is still mid-flight. With static linking, the export tables are + # linked directly, but the other groups must still be imported to + # initialize them. emitter.emit_lines( - "", - f"int ensure_deps_{short_name}(void)", - "{", - "static int done = 0;", - "if (done) return 0;", + "", f"int {ensure_deps}(void)", "{", "static int done = 0;", "if (done) return 0;" ) if self.context.group_deps: emitter.emit_lines( @@ -1061,8 +1113,9 @@ def generate_shared_lib_init(self, emitter: Emitter) -> None: "if (!_mypyc_fromlist) return -1;", "}", "PyObject *tmp;", - "PyObject *caps;", ) + if not static_linking: + emitter.emit_line("PyObject *caps;") for group in sorted(self.context.group_deps): egroup = exported_name(group) # ImportModuleLevel with fromlist returns the leaf via @@ -1074,6 +1127,11 @@ def generate_shared_lib_init(self, emitter: Emitter) -> None: shared_lib_name(group) ), "if (!tmp) return -1;", + ) + if static_linking: + emitter.emit_line("Py_DECREF(tmp);") + continue + emitter.emit_lines( 'caps = PyObject_GetAttrString(tmp, "exports");', "Py_DECREF(tmp);", "if (!caps) return -1;", @@ -1140,7 +1198,7 @@ def generate_shared_lib_init(self, emitter: Emitter) -> None: def generate_globals_init(self, emitter: Emitter) -> None: emitter.emit_lines( "", - "int CPyGlobalsInit(void)", + f"int {self.group_global('CPyGlobalsInit')}(void)", "{", "static int is_initialized = 0;", "if (is_initialized) return 0;", @@ -1151,9 +1209,11 @@ def generate_globals_init(self, emitter: Emitter) -> None: for symbol, fixup in self.simple_inits: emitter.emit_line(f"{symbol} = {fixup};") - values = "CPyLit_Str, CPyLit_Bytes, CPyLit_Int, CPyLit_Float, CPyLit_Complex, CPyLit_Tuple, CPyLit_FrozenSet" + tables = ["Str", "Bytes", "Int", "Float", "Complex", "Tuple", "FrozenSet"] + values = ", ".join(self.group_global(f"CPyLit_{t}") for t in tables) + statics = self.group_global("CPyStatics") emitter.emit_lines( - f"if (CPyStatics_Initialize(CPyStatics, {values}) < 0) {{", "return -1;", "}" + f"if (CPyStatics_Initialize({statics}, {values}) < 0) {{", "return -1;", "}" ) emitter.emit_lines("is_initialized = 1;", "return 0;", "}") @@ -1296,6 +1356,15 @@ def emit_module_exec_func( state = module_import_state_name(module_name) module_cache = emitter.static_name(module_name, None, prefix=MODULE_PREFIX) emitter.emit_lines(f"static int {impl_name}(PyObject *module)", "{") + if ( + self.compiler_options.static_linking + and self.group_name + and self.compiler_options.separate + ): + # Without static linking, the shim calls ensure_deps (via a capsule) before + # the module init. Static shims call the module init directly, so do it here. + ensure_deps = self.group_global("ensure_deps") + emitter.emit_lines(f"if ({ensure_deps}() < 0)", " return -1;") if not self.use_shared_lib: emitter.emit_lines("if (intern_strings() < 0)", " return -1;") if self.compiler_options.depends_on_librt_internal: @@ -1366,7 +1435,9 @@ def emit_module_exec_func( if not self.use_shared_lib: # With shared lib we initialize globals in its init function in case # modules are executed concurrently. - emitter.emit_lines("if (CPyGlobalsInit() < 0)", " goto fail;") + emitter.emit_lines( + f"if ({self.group_global('CPyGlobalsInit')}() < 0)", " goto fail;" + ) self.generate_top_level_call(module, emitter) diff --git a/mypyc/lib-rt/base64/librt_base64.c b/mypyc/lib-rt/base64/librt_base64.c index 6f61b0d9bd419..886e17b3be7fb 100644 --- a/mypyc/lib-rt/base64/librt_base64.c +++ b/mypyc/lib-rt/base64/librt_base64.c @@ -4,6 +4,13 @@ #define BASE64_EXPORTS +// With static linking, compiled code calls the API functions and refers to the +// type objects directly, so they need external linkage +#ifdef MYPYC_STATIC_LINKING +#define LIBRT_API_LINKAGE +#else +#define LIBRT_API_LINKAGE static +#endif #include "librt_base64.h" #include "libbase64.h" #include "pythoncapi_compat.h" @@ -44,8 +51,8 @@ convert_urlsafe_to_encoded(const char *src, size_t len, char *buf) { } } -static PyObject * -b64encode_internal(PyObject *obj, bool urlsafe) { +LIBRT_API_LINKAGE PyObject * +LibRTBase64_b64encode_internal(PyObject *obj, bool urlsafe) { unsigned char *ascii_data; char *bin_data; int leftbits = 0; @@ -99,7 +106,7 @@ b64encode(PyObject *self, PyObject *const *args, size_t nargs) { PyErr_SetString(PyExc_TypeError, "b64encode() takes exactly one argument"); return 0; } - return b64encode_internal(args[0], false); + return LibRTBase64_b64encode_internal(args[0], false); } static PyObject* @@ -108,7 +115,7 @@ urlsafe_b64encode(PyObject *self, PyObject *const *args, size_t nargs) { PyErr_SetString(PyExc_TypeError, "urlsafe_b64encode() takes exactly one argument"); return 0; } - return b64encode_internal(args[0], true); + return LibRTBase64_b64encode_internal(args[0], true); } static inline int @@ -117,8 +124,8 @@ is_valid_base64_char(char c, bool allow_padding) { (c >= '0' && c <= '9') || (c == '+') || (c == '/') || (allow_padding && c == '=')); } -static PyObject * -b64decode_internal(PyObject *arg, bool urlsafe) { +LIBRT_API_LINKAGE PyObject * +LibRTBase64_b64decode_internal(PyObject *arg, bool urlsafe) { const char *src; Py_ssize_t srclen_ssz; @@ -307,7 +314,7 @@ b64decode(PyObject *self, PyObject *const *args, size_t nargs) { PyErr_SetString(PyExc_TypeError, "b64decode() takes exactly one argument"); return 0; } - return b64decode_internal(args[0], false); + return LibRTBase64_b64decode_internal(args[0], false); } static PyObject* @@ -316,7 +323,7 @@ urlsafe_b64decode(PyObject *self, PyObject *const *args, size_t nargs) { PyErr_SetString(PyExc_TypeError, "urlsafe_b64decode() takes exactly one argument"); return 0; } - return b64decode_internal(args[0], true); + return LibRTBase64_b64decode_internal(args[0], true); } static PyMethodDef librt_base64_module_methods[] = { @@ -327,30 +334,32 @@ static PyMethodDef librt_base64_module_methods[] = { {NULL, NULL, 0, NULL} }; -static int -base64_abi_version(void) { +LIBRT_API_LINKAGE int +LibRTBase64_ABIVersion(void) { return LIBRT_BASE64_ABI_VERSION; } -static int -base64_api_version(void) { +LIBRT_API_LINKAGE int +LibRTBase64_APIVersion(void) { return LIBRT_BASE64_API_VERSION; } static int librt_base64_module_exec(PyObject *m) { +#ifndef MYPYC_STATIC_LINKING // Export mypy internal C API, be careful with the order! static void *base64_api[LIBRT_BASE64_API_LEN] = { - (void *)base64_abi_version, - (void *)base64_api_version, - (void *)b64encode_internal, - (void *)b64decode_internal, + (void *)LibRTBase64_ABIVersion, + (void *)LibRTBase64_APIVersion, + (void *)LibRTBase64_b64encode_internal, + (void *)LibRTBase64_b64decode_internal, }; PyObject *c_api_object = PyCapsule_New((void *)base64_api, "librt.base64._C_API", NULL); if (PyModule_Add(m, "_C_API", c_api_object) < 0) { return -1; } +#endif return 0; } diff --git a/mypyc/lib-rt/base64/librt_base64.h b/mypyc/lib-rt/base64/librt_base64.h index f8efafda97813..7483f4be4834c 100644 --- a/mypyc/lib-rt/base64/librt_base64.h +++ b/mypyc/lib-rt/base64/librt_base64.h @@ -7,4 +7,14 @@ #define LIBRT_BASE64_API_VERSION 2 #define LIBRT_BASE64_API_LEN 4 +#ifdef MYPYC_STATIC_LINKING +// With static linking, the API is used directly (see librt_base64_api.h) +#include +#include +int LibRTBase64_ABIVersion(void); +int LibRTBase64_APIVersion(void); +PyObject *LibRTBase64_b64encode_internal(PyObject *obj, bool urlsafe); +PyObject *LibRTBase64_b64decode_internal(PyObject *arg, bool urlsafe); +#endif + #endif // LIBRT_BASE64_H diff --git a/mypyc/lib-rt/base64/librt_base64_api.c b/mypyc/lib-rt/base64/librt_base64_api.c index 8f8499245978c..33aa916f59028 100644 --- a/mypyc/lib-rt/base64/librt_base64_api.c +++ b/mypyc/lib-rt/base64/librt_base64_api.c @@ -1,6 +1,8 @@ #include "librt_base64_api.h" +#ifndef MYPYC_STATIC_LINKING void *LibRTBase64_API[LIBRT_BASE64_API_LEN] = {0}; +#endif int import_librt_base64(void) @@ -9,6 +11,11 @@ import_librt_base64(void) if (mod == NULL) return -1; Py_DECREF(mod); // we import just for the side effect of making the below work. +#ifdef MYPYC_STATIC_LINKING + // librt is statically linked, so the API is used directly. Importing the + // module above initializes it (e.g. its types). + return 0; +#else void **capsule = (void **)PyCapsule_Import("librt.base64._C_API", 0); if (capsule == NULL) return -1; @@ -40,4 +47,5 @@ import_librt_base64(void) // entries, so this copy is safe. memcpy(LibRTBase64_API, capsule, sizeof(LibRTBase64_API)); return 0; +#endif } diff --git a/mypyc/lib-rt/base64/librt_base64_api.h b/mypyc/lib-rt/base64/librt_base64_api.h index 41363b7274586..67c8ae1740389 100644 --- a/mypyc/lib-rt/base64/librt_base64_api.h +++ b/mypyc/lib-rt/base64/librt_base64_api.h @@ -3,12 +3,17 @@ #include "librt_base64.h" +#ifdef MYPYC_STATIC_LINKING +// librt is statically linked, so the API functions (declared in librt_base64.h) are +// called directly, and type objects are referred to directly +#else extern void *LibRTBase64_API[LIBRT_BASE64_API_LEN]; #define LibRTBase64_ABIVersion (*(int (*)(void)) LibRTBase64_API[0]) #define LibRTBase64_APIVersion (*(int (*)(void)) LibRTBase64_API[1]) #define LibRTBase64_b64encode_internal (*(PyObject* (*)(PyObject *source, bool urlsafe)) LibRTBase64_API[2]) #define LibRTBase64_b64decode_internal (*(PyObject* (*)(PyObject *source, bool urlsafe)) LibRTBase64_API[3]) +#endif int import_librt_base64(void); diff --git a/mypyc/lib-rt/internal/librt_internal.c b/mypyc/lib-rt/internal/librt_internal.c index dc811efbabad1..c25afe385c99d 100644 --- a/mypyc/lib-rt/internal/librt_internal.c +++ b/mypyc/lib-rt/internal/librt_internal.c @@ -6,11 +6,18 @@ #include #include "CPy.h" #define LIBRT_INTERNAL_MODULE +// With static linking, compiled code calls the API functions and refers to the +// type objects directly, so they need external linkage +#ifdef MYPYC_STATIC_LINKING +#define LIBRT_API_LINKAGE +#else +#define LIBRT_API_LINKAGE static +#endif #include "librt_internal.h" #define START_SIZE 512 -// See comment in read_int_internal() on motivation for these values. +// See comment in NativeInternal_read_int_internal() on motivation for these values. #define MIN_ONE_BYTE_INT -10 #define MAX_ONE_BYTE_INT 117 // 2 ** 7 - 1 - 10 #define MIN_TWO_BYTES_INT -100 @@ -72,12 +79,12 @@ typedef struct { PyObject *source; // The object that contains the buffer } ReadBufferObject; -static PyTypeObject ReadBufferType; +LIBRT_API_LINKAGE PyTypeObject NativeInternal_ReadBufferType; static PyObject* ReadBuffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - if (type != &ReadBufferType) { + if (type != &NativeInternal_ReadBufferType) { PyErr_SetString(PyExc_TypeError, "ReadBuffer should not be subclassed"); return NULL; } @@ -103,9 +110,9 @@ ReadBuffer_init_internal(ReadBufferObject *self, PyObject *source) { return 0; } -static PyObject* -ReadBuffer_internal(PyObject *source) { - ReadBufferObject *self = (ReadBufferObject *)ReadBufferType.tp_alloc(&ReadBufferType, 0); +LIBRT_API_LINKAGE PyObject* +NativeInternal_ReadBuffer_internal(PyObject *source) { + ReadBufferObject *self = (ReadBufferObject *)NativeInternal_ReadBufferType.tp_alloc(&NativeInternal_ReadBufferType, 0); if (self == NULL) return NULL; self->ptr = NULL; @@ -141,7 +148,7 @@ static PyMethodDef ReadBuffer_methods[] = { {NULL} /* Sentinel */ }; -static PyTypeObject ReadBufferType = { +LIBRT_API_LINKAGE PyTypeObject NativeInternal_ReadBufferType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "ReadBuffer", .tp_doc = PyDoc_STR("Mypy cache buffer objects"), @@ -165,12 +172,12 @@ typedef struct { char *end; // End of the buffer } WriteBufferObject; -static PyTypeObject WriteBufferType; +LIBRT_API_LINKAGE PyTypeObject NativeInternal_WriteBufferType; static PyObject* WriteBuffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - if (type != &WriteBufferType) { + if (type != &NativeInternal_WriteBufferType) { PyErr_SetString(PyExc_TypeError, "WriteBuffer cannot be subclassed"); return NULL; } @@ -197,9 +204,9 @@ WriteBuffer_init_internal(WriteBufferObject *self) { return 0; } -static PyObject* -WriteBuffer_internal(void) { - WriteBufferObject *self = (WriteBufferObject *)WriteBufferType.tp_alloc(&WriteBufferType, 0); +LIBRT_API_LINKAGE PyObject* +NativeInternal_WriteBuffer_internal(void) { + WriteBufferObject *self = (WriteBufferObject *)NativeInternal_WriteBufferType.tp_alloc(&NativeInternal_WriteBufferType, 0); if (self == NULL) return NULL; self->buf = NULL; @@ -236,8 +243,8 @@ WriteBuffer_dealloc(WriteBufferObject *self) Py_TYPE(self)->tp_free((PyObject *)self); } -static PyObject* -WriteBuffer_getvalue_internal(PyObject *self) +LIBRT_API_LINKAGE PyObject* +NativeInternal_WriteBuffer_getvalue_internal(PyObject *self) { WriteBufferObject *obj = (WriteBufferObject *)self; return PyBytes_FromStringAndSize(obj->buf, obj->ptr - obj->buf); @@ -256,7 +263,7 @@ static PyMethodDef WriteBuffer_methods[] = { {NULL} /* Sentinel */ }; -static PyTypeObject WriteBufferType = { +LIBRT_API_LINKAGE PyTypeObject NativeInternal_WriteBufferType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "WriteBuffer", .tp_doc = PyDoc_STR("Mypy cache buffer objects"), @@ -273,7 +280,7 @@ static PyTypeObject WriteBufferType = { static inline char _check_read_buffer(PyObject *data) { - if (unlikely(Py_TYPE(data) != &ReadBufferType)) { + if (unlikely(Py_TYPE(data) != &NativeInternal_ReadBufferType)) { PyErr_Format( PyExc_TypeError, "data must be a ReadBuffer object, got %s", Py_TYPE(data)->tp_name ); @@ -284,7 +291,7 @@ _check_read_buffer(PyObject *data) { static inline char _check_write_buffer(PyObject *data) { - if (unlikely(Py_TYPE(data) != &WriteBufferType)) { + if (unlikely(Py_TYPE(data) != &NativeInternal_WriteBufferType)) { PyErr_Format( PyExc_TypeError, "data must be a WriteBuffer object, got %s", Py_TYPE(data)->tp_name ); @@ -328,8 +335,8 @@ bool format: single byte \x01 - True */ -static char -read_bool_internal(PyObject *data) { +LIBRT_API_LINKAGE char +NativeInternal_read_bool_internal(PyObject *data) { _CHECK_READ(data, 1, CPY_BOOL_ERROR) char res; _READ(&res, data, char); @@ -349,7 +356,7 @@ read_bool(PyObject *self, PyObject *const *args, size_t nargs) { } PyObject *data = args[0]; _CHECK_READ_BUFFER(data, NULL) - char res = read_bool_internal(data); + char res = NativeInternal_read_bool_internal(data); if (unlikely(res == CPY_BOOL_ERROR)) return NULL; PyObject *retval = res ? Py_True : Py_False; @@ -357,8 +364,8 @@ read_bool(PyObject *self, PyObject *const *args, size_t nargs) { return retval; } -static char -write_bool_internal(PyObject *data, char value) { +LIBRT_API_LINKAGE char +NativeInternal_write_bool_internal(PyObject *data, char value) { _CHECK_WRITE(data, 1) _WRITE(data, char, value); return CPY_NONE; @@ -378,7 +385,7 @@ write_bool(PyObject *self, PyObject *const *args, size_t nargs) { PyErr_SetString(PyExc_TypeError, "value must be a bool"); return NULL; } - if (unlikely(write_bool_internal(data, Py_IsTrue(value)) == CPY_NONE_ERROR)) { + if (unlikely(NativeInternal_write_bool_internal(data, Py_IsTrue(value)) == CPY_NONE_ERROR)) { return NULL; } Py_INCREF(Py_None); @@ -414,8 +421,8 @@ _read_short_int(PyObject *data, uint8_t first) { return (higher + (Py_ssize_t)(first >> 3) + MIN_FOUR_BYTES_INT) << 1; } -static PyObject* -read_str_internal(PyObject *data) { +LIBRT_API_LINKAGE PyObject* +NativeInternal_read_str_internal(PyObject *data) { // Read string length. _CHECK_READ(data, 1, NULL) uint8_t first; @@ -453,7 +460,7 @@ read_str(PyObject *self, PyObject *const *args, size_t nargs) { } PyObject *data = args[0]; _CHECK_READ_BUFFER(data, NULL) - return read_str_internal(data); + return NativeInternal_read_str_internal(data); } // The caller *must* check that real_value is within allowed range (29 bits). @@ -482,8 +489,8 @@ _write_short_int(PyObject *data, Py_ssize_t real_value) { return CPY_NONE; } -static char -write_str_internal(PyObject *data, PyObject *value) { +LIBRT_API_LINKAGE char +NativeInternal_write_str_internal(PyObject *data, PyObject *value) { Py_ssize_t size; const char *chunk = PyUnicode_AsUTF8AndSize(value, &size); if (unlikely(chunk == NULL)) @@ -519,7 +526,7 @@ write_str(PyObject *self, PyObject *const *args, size_t nargs) { PyErr_SetString(PyExc_TypeError, "value must be a str"); return NULL; } - if (unlikely(write_str_internal(data, value) == CPY_NONE_ERROR)) { + if (unlikely(NativeInternal_write_str_internal(data, value) == CPY_NONE_ERROR)) { return NULL; } Py_INCREF(Py_None); @@ -530,8 +537,8 @@ write_str(PyObject *self, PyObject *const *args, size_t nargs) { bytes format: size as int (see below) followed by bytes */ -static PyObject* -read_bytes_internal(PyObject *data) { +LIBRT_API_LINKAGE PyObject* +NativeInternal_read_bytes_internal(PyObject *data) { // Read length. _CHECK_READ(data, 1, NULL) uint8_t first; @@ -569,11 +576,11 @@ read_bytes(PyObject *self, PyObject *const *args, size_t nargs) { } PyObject *data = args[0]; _CHECK_READ_BUFFER(data, NULL) - return read_bytes_internal(data); + return NativeInternal_read_bytes_internal(data); } -static char -write_bytes_internal(PyObject *data, PyObject *value) { +LIBRT_API_LINKAGE char +NativeInternal_write_bytes_internal(PyObject *data, PyObject *value) { const char *chunk = PyBytes_AsString(value); if (unlikely(chunk == NULL)) return CPY_NONE_ERROR; @@ -609,7 +616,7 @@ write_bytes(PyObject *self, PyObject *const *args, size_t nargs) { PyErr_SetString(PyExc_TypeError, "value must be a bytes object"); return NULL; } - if (unlikely(write_bytes_internal(data, value) == CPY_NONE_ERROR)) { + if (unlikely(NativeInternal_write_bytes_internal(data, value) == CPY_NONE_ERROR)) { return NULL; } Py_INCREF(Py_None); @@ -621,8 +628,8 @@ float format: stored using PyFloat helpers in little-endian format. */ -static double -read_float_internal(PyObject *data) { +LIBRT_API_LINKAGE double +NativeInternal_read_float_internal(PyObject *data) { _CHECK_READ(data, 8, CPY_FLOAT_ERROR) char *ptr = ((ReadBufferObject *)data)->ptr; double res = PyFloat_Unpack8(ptr, 1); @@ -641,15 +648,15 @@ read_float(PyObject *self, PyObject *const *args, size_t nargs) { } PyObject *data = args[0]; _CHECK_READ_BUFFER(data, NULL) - double retval = read_float_internal(data); + double retval = NativeInternal_read_float_internal(data); if (unlikely(retval == CPY_FLOAT_ERROR && PyErr_Occurred())) { return NULL; } return PyFloat_FromDouble(retval); } -static char -write_float_internal(PyObject *data, double value) { +LIBRT_API_LINKAGE char +NativeInternal_write_float_internal(PyObject *data, double value) { _CHECK_WRITE(data, 8) char *ptr = ((WriteBufferObject *)data)->ptr; int res = PyFloat_Pack8(value, ptr, 1); @@ -673,7 +680,7 @@ write_float(PyObject *self, PyObject *const *args, size_t nargs) { PyErr_SetString(PyExc_TypeError, "value must be a float"); return NULL; } - if (unlikely(write_float_internal(data, PyFloat_AsDouble(value)) == CPY_NONE_ERROR)) { + if (unlikely(NativeInternal_write_float_internal(data, PyFloat_AsDouble(value)) == CPY_NONE_ERROR)) { return NULL; } Py_INCREF(Py_None); @@ -691,8 +698,8 @@ Note: for fixed size formats we skew ranges towards more positive values, since negative integers are much more rare. */ -static CPyTagged -read_int_internal(PyObject *data) { +LIBRT_API_LINKAGE CPyTagged +NativeInternal_read_int_internal(PyObject *data) { _CHECK_READ(data, 1, CPY_INT_TAG) uint8_t first; @@ -743,7 +750,7 @@ read_int(PyObject *self, PyObject *const *args, size_t nargs) { } PyObject *data = args[0]; _CHECK_READ_BUFFER(data, NULL) - CPyTagged retval = read_int_internal(data); + CPyTagged retval = NativeInternal_read_int_internal(data); if (unlikely(retval == CPY_INT_TAG)) { return NULL; } @@ -806,13 +813,13 @@ _write_long_int(PyObject *data, CPyTagged value) { // Write absolute integer value as byte array in a variable-length little endian format. Py_ssize_t i; for (i = len; i > 1; i -= 2) { - if (write_tag_internal( + if (NativeInternal_write_tag_internal( data, hex_to_int(str[i - 1]) | (hex_to_int(str[i - 2]) << 4)) == CPY_NONE_ERROR) goto error; } // The final byte may correspond to only one hex digit. if (i == 1) { - if (write_tag_internal(data, hex_to_int(str[i - 1])) == CPY_NONE_ERROR) + if (NativeInternal_write_tag_internal(data, hex_to_int(str[i - 1])) == CPY_NONE_ERROR) goto error; } @@ -826,8 +833,8 @@ _write_long_int(PyObject *data, CPyTagged value) { return CPY_NONE_ERROR; } -static char -write_int_internal(PyObject *data, CPyTagged value) { +LIBRT_API_LINKAGE char +NativeInternal_write_int_internal(PyObject *data, CPyTagged value) { if (likely((value & CPY_INT_TAG) == 0)) { Py_ssize_t real_value = CPyTagged_ShortAsSsize_t(value); if (likely(real_value >= MIN_FOUR_BYTES_INT && real_value <= MAX_FOUR_BYTES_INT)) { @@ -855,7 +862,7 @@ write_int(PyObject *self, PyObject *const *args, size_t nargs) { return NULL; } CPyTagged tagged_value = CPyTagged_BorrowFromObject(value); - if (unlikely(write_int_internal(data, tagged_value) == CPY_NONE_ERROR)) { + if (unlikely(NativeInternal_write_int_internal(data, tagged_value) == CPY_NONE_ERROR)) { return NULL; } Py_INCREF(Py_None); @@ -867,8 +874,8 @@ integer tag format (0 <= t <= 255): stored as a uint8_t */ -static uint8_t -read_tag_internal(PyObject *data) { +LIBRT_API_LINKAGE uint8_t +NativeInternal_read_tag_internal(PyObject *data) { _CHECK_READ(data, 1, CPY_LL_UINT_ERROR) uint8_t ret; _READ(&ret, data, uint8_t); @@ -884,15 +891,15 @@ read_tag(PyObject *self, PyObject *const *args, size_t nargs) { } PyObject *data = args[0]; _CHECK_READ_BUFFER(data, NULL) - uint8_t retval = read_tag_internal(data); + uint8_t retval = NativeInternal_read_tag_internal(data); if (unlikely(retval == CPY_LL_UINT_ERROR && PyErr_Occurred())) { return NULL; } return PyLong_FromLong(retval); } -static char -write_tag_internal(PyObject *data, uint8_t value) { +LIBRT_API_LINKAGE char +NativeInternal_write_tag_internal(PyObject *data, uint8_t value) { _CHECK_WRITE(data, 1) _WRITE(data, uint8_t, value); return CPY_NONE; @@ -913,7 +920,7 @@ write_tag(PyObject *self, PyObject *const *args, size_t nargs) { CPy_TypeError("u8", value); return NULL; } - if (unlikely(write_tag_internal(data, unboxed) == CPY_NONE_ERROR)) { + if (unlikely(NativeInternal_write_tag_internal(data, unboxed) == CPY_NONE_ERROR)) { return NULL; } Py_INCREF(Py_None); @@ -1040,7 +1047,7 @@ _skip_list_gen(PyObject *data) { return CPY_NONE_ERROR; Py_ssize_t i; for (i = 0; i < size; i++) { - uint8_t tag = read_tag_internal(data); + uint8_t tag = NativeInternal_read_tag_internal(data); if (unlikely(tag == CPY_LL_UINT_ERROR && PyErr_Occurred())) { return CPY_NONE_ERROR; } @@ -1086,7 +1093,7 @@ _skip_dict_str_gen(PyObject *data) { // Bare key followed by tagged value. if (unlikely(_skip_str_bytes(data) == CPY_NONE_ERROR)) return CPY_NONE_ERROR; - uint8_t tag = read_tag_internal(data); + uint8_t tag = NativeInternal_read_tag_internal(data); if (unlikely(tag == CPY_LL_UINT_ERROR && PyErr_Occurred())) { return CPY_NONE_ERROR; } @@ -1101,7 +1108,7 @@ _skip_dict_str_gen(PyObject *data) { static inline char _skip_class(PyObject *data) { while (1) { - uint8_t tag = read_tag_internal(data); + uint8_t tag = NativeInternal_read_tag_internal(data); if (unlikely(tag == CPY_LL_UINT_ERROR && PyErr_Occurred())) { return CPY_NONE_ERROR; } @@ -1117,7 +1124,7 @@ _skip_class(PyObject *data) { // Instance has special compact layout (as an important optimization). static inline char _skip_instance(PyObject *data) { - uint8_t second_tag = read_tag_internal(data); + uint8_t second_tag = NativeInternal_read_tag_internal(data); if (unlikely(second_tag == CPY_LL_UINT_ERROR && PyErr_Occurred())) { return CPY_NONE_ERROR; } @@ -1171,8 +1178,8 @@ _skip_object(PyObject *data, uint8_t tag) { return CPY_NONE_ERROR; } -static PyObject* -extract_symbol_internal(PyObject *data) { +LIBRT_API_LINKAGE PyObject* +NativeInternal_extract_symbol_internal(PyObject *data) { char *ptr = ((ReadBufferObject *)data)->ptr; if (unlikely(_skip_class(data) == CPY_NONE_ERROR)) return NULL; @@ -1192,27 +1199,27 @@ extract_symbol(PyObject *self, PyObject *const *args, size_t nargs) { } PyObject *data = args[0]; _CHECK_READ_BUFFER(data, NULL) - return extract_symbol_internal(data); + return NativeInternal_extract_symbol_internal(data); } -static uint8_t -cache_version_internal(void) { +LIBRT_API_LINKAGE uint8_t +NativeInternal_cache_version_internal(void) { return 0; } static PyObject* cache_version(PyObject *self, PyObject *Py_UNUSED(ignored)) { - return PyLong_FromLong(cache_version_internal()); + return PyLong_FromLong(NativeInternal_cache_version_internal()); } -static PyTypeObject * -ReadBuffer_type_internal(void) { - return &ReadBufferType; // Return borrowed reference +LIBRT_API_LINKAGE PyTypeObject * +NativeInternal_ReadBuffer_type_internal(void) { + return &NativeInternal_ReadBufferType; // Return borrowed reference } -static PyTypeObject * -WriteBuffer_type_internal(void) { - return &WriteBufferType; // Return borrowed reference +LIBRT_API_LINKAGE PyTypeObject * +NativeInternal_WriteBuffer_type_internal(void) { + return &NativeInternal_WriteBufferType; // Return borrowed reference }; static PyMethodDef librt_internal_module_methods[] = { @@ -1233,12 +1240,12 @@ static PyMethodDef librt_internal_module_methods[] = { {NULL, NULL, 0, NULL} }; -static int +LIBRT_API_LINKAGE int NativeInternal_ABI_Version(void) { return LIBRT_INTERNAL_ABI_VERSION; } -static int +LIBRT_API_LINKAGE int NativeInternal_API_Version(void) { return LIBRT_INTERNAL_API_VERSION; } @@ -1246,47 +1253,49 @@ NativeInternal_API_Version(void) { static int librt_internal_module_exec(PyObject *m) { - if (PyType_Ready(&ReadBufferType) < 0) { + if (PyType_Ready(&NativeInternal_ReadBufferType) < 0) { return -1; } - if (PyType_Ready(&WriteBufferType) < 0) { + if (PyType_Ready(&NativeInternal_WriteBufferType) < 0) { return -1; } - if (PyModule_AddObjectRef(m, "ReadBuffer", (PyObject *) &ReadBufferType) < 0) { + if (PyModule_AddObjectRef(m, "ReadBuffer", (PyObject *) &NativeInternal_ReadBufferType) < 0) { return -1; } - if (PyModule_AddObjectRef(m, "WriteBuffer", (PyObject *) &WriteBufferType) < 0) { + if (PyModule_AddObjectRef(m, "WriteBuffer", (PyObject *) &NativeInternal_WriteBufferType) < 0) { return -1; } +#ifndef MYPYC_STATIC_LINKING // Export mypy internal C API, be careful with the order! static void *NativeInternal_API[LIBRT_INTERNAL_API_LEN] = { - (void *)ReadBuffer_internal, - (void *)WriteBuffer_internal, - (void *)WriteBuffer_getvalue_internal, - (void *)write_bool_internal, - (void *)read_bool_internal, - (void *)write_str_internal, - (void *)read_str_internal, - (void *)write_float_internal, - (void *)read_float_internal, - (void *)write_int_internal, - (void *)read_int_internal, - (void *)write_tag_internal, - (void *)read_tag_internal, + (void *)NativeInternal_ReadBuffer_internal, + (void *)NativeInternal_WriteBuffer_internal, + (void *)NativeInternal_WriteBuffer_getvalue_internal, + (void *)NativeInternal_write_bool_internal, + (void *)NativeInternal_read_bool_internal, + (void *)NativeInternal_write_str_internal, + (void *)NativeInternal_read_str_internal, + (void *)NativeInternal_write_float_internal, + (void *)NativeInternal_read_float_internal, + (void *)NativeInternal_write_int_internal, + (void *)NativeInternal_read_int_internal, + (void *)NativeInternal_write_tag_internal, + (void *)NativeInternal_read_tag_internal, (void *)NativeInternal_ABI_Version, - (void *)write_bytes_internal, - (void *)read_bytes_internal, - (void *)cache_version_internal, - (void *)ReadBuffer_type_internal, - (void *)WriteBuffer_type_internal, + (void *)NativeInternal_write_bytes_internal, + (void *)NativeInternal_read_bytes_internal, + (void *)NativeInternal_cache_version_internal, + (void *)NativeInternal_ReadBuffer_type_internal, + (void *)NativeInternal_WriteBuffer_type_internal, (void *)NativeInternal_API_Version, - (void *)extract_symbol_internal + (void *)NativeInternal_extract_symbol_internal }; PyObject *c_api_object = PyCapsule_New((void *)NativeInternal_API, "librt.internal._C_API", NULL); if (PyModule_Add(m, "_C_API", c_api_object) < 0) { return -1; } +#endif return 0; } diff --git a/mypyc/lib-rt/internal/librt_internal.h b/mypyc/lib-rt/internal/librt_internal.h index 37cd7b5cfd5ff..171c8f04c5941 100644 --- a/mypyc/lib-rt/internal/librt_internal.h +++ b/mypyc/lib-rt/internal/librt_internal.h @@ -20,30 +20,60 @@ #ifdef LIBRT_INTERNAL_MODULE -static PyObject *ReadBuffer_internal(PyObject *source); -static PyObject *WriteBuffer_internal(void); -static PyObject *WriteBuffer_getvalue_internal(PyObject *self); -static PyObject *ReadBuffer_internal(PyObject *source); +LIBRT_API_LINKAGE PyObject *NativeInternal_ReadBuffer_internal(PyObject *source); +LIBRT_API_LINKAGE PyObject *NativeInternal_WriteBuffer_internal(void); +LIBRT_API_LINKAGE PyObject *NativeInternal_WriteBuffer_getvalue_internal(PyObject *self); +LIBRT_API_LINKAGE PyObject *NativeInternal_ReadBuffer_internal(PyObject *source); static PyObject *ReadBuffer_internal_empty(void); -static char write_bool_internal(PyObject *data, char value); -static char read_bool_internal(PyObject *data); -static char write_str_internal(PyObject *data, PyObject *value); -static PyObject *read_str_internal(PyObject *data); -static char write_float_internal(PyObject *data, double value); -static double read_float_internal(PyObject *data); -static char write_int_internal(PyObject *data, CPyTagged value); -static CPyTagged read_int_internal(PyObject *data); -static char write_tag_internal(PyObject *data, uint8_t value); -static uint8_t read_tag_internal(PyObject *data); -static int NativeInternal_ABI_Version(void); -static char write_bytes_internal(PyObject *data, PyObject *value); -static PyObject *read_bytes_internal(PyObject *data); -static uint8_t cache_version_internal(void); -static PyTypeObject *ReadBuffer_type_internal(void); -static PyTypeObject *WriteBuffer_type_internal(void); -static int NativeInternal_API_Version(void); -static PyObject *extract_symbol_internal(PyObject *data); +LIBRT_API_LINKAGE char NativeInternal_write_bool_internal(PyObject *data, char value); +LIBRT_API_LINKAGE char NativeInternal_read_bool_internal(PyObject *data); +LIBRT_API_LINKAGE char NativeInternal_write_str_internal(PyObject *data, PyObject *value); +LIBRT_API_LINKAGE PyObject *NativeInternal_read_str_internal(PyObject *data); +LIBRT_API_LINKAGE char NativeInternal_write_float_internal(PyObject *data, double value); +LIBRT_API_LINKAGE double NativeInternal_read_float_internal(PyObject *data); +LIBRT_API_LINKAGE char NativeInternal_write_int_internal(PyObject *data, CPyTagged value); +LIBRT_API_LINKAGE CPyTagged NativeInternal_read_int_internal(PyObject *data); +LIBRT_API_LINKAGE char NativeInternal_write_tag_internal(PyObject *data, uint8_t value); +LIBRT_API_LINKAGE uint8_t NativeInternal_read_tag_internal(PyObject *data); +LIBRT_API_LINKAGE int NativeInternal_ABI_Version(void); +LIBRT_API_LINKAGE char NativeInternal_write_bytes_internal(PyObject *data, PyObject *value); +LIBRT_API_LINKAGE PyObject *NativeInternal_read_bytes_internal(PyObject *data); +LIBRT_API_LINKAGE uint8_t NativeInternal_cache_version_internal(void); +LIBRT_API_LINKAGE PyTypeObject *NativeInternal_ReadBuffer_type_internal(void); +LIBRT_API_LINKAGE PyTypeObject *NativeInternal_WriteBuffer_type_internal(void); +LIBRT_API_LINKAGE int NativeInternal_API_Version(void); +LIBRT_API_LINKAGE PyObject *NativeInternal_extract_symbol_internal(PyObject *data); #endif +#ifdef MYPYC_STATIC_LINKING +// With static linking, the API is used directly (see librt_internal_api.h) +#include +#include +#include "CPy.h" +extern PyTypeObject NativeInternal_ReadBufferType; +extern PyTypeObject NativeInternal_WriteBufferType; +PyObject *NativeInternal_ReadBuffer_internal(PyObject *source); +PyObject *NativeInternal_WriteBuffer_internal(void); +PyObject *NativeInternal_WriteBuffer_getvalue_internal(PyObject *self); +char NativeInternal_write_bool_internal(PyObject *data, char value); +char NativeInternal_read_bool_internal(PyObject *data); +char NativeInternal_write_str_internal(PyObject *data, PyObject *value); +PyObject *NativeInternal_read_str_internal(PyObject *data); +char NativeInternal_write_float_internal(PyObject *data, double value); +double NativeInternal_read_float_internal(PyObject *data); +char NativeInternal_write_int_internal(PyObject *data, CPyTagged value); +CPyTagged NativeInternal_read_int_internal(PyObject *data); +char NativeInternal_write_tag_internal(PyObject *data, uint8_t value); +uint8_t NativeInternal_read_tag_internal(PyObject *data); +int NativeInternal_ABI_Version(void); +char NativeInternal_write_bytes_internal(PyObject *data, PyObject *value); +PyObject *NativeInternal_read_bytes_internal(PyObject *data); +uint8_t NativeInternal_cache_version_internal(void); +PyTypeObject *NativeInternal_ReadBuffer_type_internal(void); +PyTypeObject *NativeInternal_WriteBuffer_type_internal(void); +int NativeInternal_API_Version(void); +PyObject *NativeInternal_extract_symbol_internal(PyObject *data); +#endif + #endif // LIBRT_INTERNAL_H diff --git a/mypyc/lib-rt/internal/librt_internal_api.c b/mypyc/lib-rt/internal/librt_internal_api.c index 075e39394f1fc..960b694a5560a 100644 --- a/mypyc/lib-rt/internal/librt_internal_api.c +++ b/mypyc/lib-rt/internal/librt_internal_api.c @@ -1,6 +1,8 @@ #include "librt_internal_api.h" +#ifndef MYPYC_STATIC_LINKING void *NativeInternal_API[LIBRT_INTERNAL_API_LEN] = {0}; +#endif int import_librt_internal(void) @@ -9,6 +11,11 @@ import_librt_internal(void) if (mod == NULL) return -1; Py_DECREF(mod); // we import just for the side effect of making the below work. +#ifdef MYPYC_STATIC_LINKING + // librt is statically linked, so the API is used directly. Importing the + // module above initializes it (e.g. its types). + return 0; +#else void **capsule = (void **)PyCapsule_Import("librt.internal._C_API", 0); if (capsule == NULL) return -1; @@ -40,4 +47,5 @@ import_librt_internal(void) // entries, so this copy is safe. memcpy(NativeInternal_API, capsule, sizeof(NativeInternal_API)); return 0; +#endif } diff --git a/mypyc/lib-rt/internal/librt_internal_api.h b/mypyc/lib-rt/internal/librt_internal_api.h index 46cdd6561ade5..273f5e24106e6 100644 --- a/mypyc/lib-rt/internal/librt_internal_api.h +++ b/mypyc/lib-rt/internal/librt_internal_api.h @@ -3,39 +3,46 @@ #include "librt_internal.h" +#ifdef MYPYC_STATIC_LINKING +// librt is statically linked, so the API functions (declared in librt_internal.h) are +// called directly, and type objects are referred to directly +#define NativeInternal_ReadBuffer_type_internal() (&NativeInternal_ReadBufferType) +#define NativeInternal_WriteBuffer_type_internal() (&NativeInternal_WriteBufferType) +#else extern void *NativeInternal_API[LIBRT_INTERNAL_API_LEN]; -#define ReadBuffer_internal (*(PyObject* (*)(PyObject *source)) NativeInternal_API[0]) -#define WriteBuffer_internal (*(PyObject* (*)(void)) NativeInternal_API[1]) -#define WriteBuffer_getvalue_internal (*(PyObject* (*)(PyObject *source)) NativeInternal_API[2]) -#define write_bool_internal (*(char (*)(PyObject *source, char value)) NativeInternal_API[3]) -#define read_bool_internal (*(char (*)(PyObject *source)) NativeInternal_API[4]) -#define write_str_internal (*(char (*)(PyObject *source, PyObject *value)) NativeInternal_API[5]) -#define read_str_internal (*(PyObject* (*)(PyObject *source)) NativeInternal_API[6]) -#define write_float_internal (*(char (*)(PyObject *source, double value)) NativeInternal_API[7]) -#define read_float_internal (*(double (*)(PyObject *source)) NativeInternal_API[8]) -#define write_int_internal (*(char (*)(PyObject *source, CPyTagged value)) NativeInternal_API[9]) -#define read_int_internal (*(CPyTagged (*)(PyObject *source)) NativeInternal_API[10]) -#define write_tag_internal (*(char (*)(PyObject *source, uint8_t value)) NativeInternal_API[11]) -#define read_tag_internal (*(uint8_t (*)(PyObject *source)) NativeInternal_API[12]) +#define NativeInternal_ReadBuffer_internal (*(PyObject* (*)(PyObject *source)) NativeInternal_API[0]) +#define NativeInternal_WriteBuffer_internal (*(PyObject* (*)(void)) NativeInternal_API[1]) +#define NativeInternal_WriteBuffer_getvalue_internal (*(PyObject* (*)(PyObject *source)) NativeInternal_API[2]) +#define NativeInternal_write_bool_internal (*(char (*)(PyObject *source, char value)) NativeInternal_API[3]) +#define NativeInternal_read_bool_internal (*(char (*)(PyObject *source)) NativeInternal_API[4]) +#define NativeInternal_write_str_internal (*(char (*)(PyObject *source, PyObject *value)) NativeInternal_API[5]) +#define NativeInternal_read_str_internal (*(PyObject* (*)(PyObject *source)) NativeInternal_API[6]) +#define NativeInternal_write_float_internal (*(char (*)(PyObject *source, double value)) NativeInternal_API[7]) +#define NativeInternal_read_float_internal (*(double (*)(PyObject *source)) NativeInternal_API[8]) +#define NativeInternal_write_int_internal (*(char (*)(PyObject *source, CPyTagged value)) NativeInternal_API[9]) +#define NativeInternal_read_int_internal (*(CPyTagged (*)(PyObject *source)) NativeInternal_API[10]) +#define NativeInternal_write_tag_internal (*(char (*)(PyObject *source, uint8_t value)) NativeInternal_API[11]) +#define NativeInternal_read_tag_internal (*(uint8_t (*)(PyObject *source)) NativeInternal_API[12]) #define NativeInternal_ABI_Version (*(int (*)(void)) NativeInternal_API[13]) -#define write_bytes_internal (*(char (*)(PyObject *source, PyObject *value)) NativeInternal_API[14]) -#define read_bytes_internal (*(PyObject* (*)(PyObject *source)) NativeInternal_API[15]) -#define cache_version_internal (*(uint8_t (*)(void)) NativeInternal_API[16]) -#define ReadBuffer_type_internal (*(PyTypeObject* (*)(void)) NativeInternal_API[17]) -#define WriteBuffer_type_internal (*(PyTypeObject* (*)(void)) NativeInternal_API[18]) +#define NativeInternal_write_bytes_internal (*(char (*)(PyObject *source, PyObject *value)) NativeInternal_API[14]) +#define NativeInternal_read_bytes_internal (*(PyObject* (*)(PyObject *source)) NativeInternal_API[15]) +#define NativeInternal_cache_version_internal (*(uint8_t (*)(void)) NativeInternal_API[16]) +#define NativeInternal_ReadBuffer_type_internal (*(PyTypeObject* (*)(void)) NativeInternal_API[17]) +#define NativeInternal_WriteBuffer_type_internal (*(PyTypeObject* (*)(void)) NativeInternal_API[18]) #define NativeInternal_API_Version (*(int (*)(void)) NativeInternal_API[19]) -#define extract_symbol_internal (*(PyObject* (*)(PyObject *source)) NativeInternal_API[20]) +#define NativeInternal_extract_symbol_internal (*(PyObject* (*)(PyObject *source)) NativeInternal_API[20]) +#endif int import_librt_internal(void); static inline bool CPyReadBuffer_Check(PyObject *obj) { - return Py_TYPE(obj) == ReadBuffer_type_internal(); + return Py_TYPE(obj) == NativeInternal_ReadBuffer_type_internal(); } static inline bool CPyWriteBuffer_Check(PyObject *obj) { - return Py_TYPE(obj) == WriteBuffer_type_internal(); + return Py_TYPE(obj) == NativeInternal_WriteBuffer_type_internal(); } #endif // LIBRT_INTERNAL_API_H diff --git a/mypyc/lib-rt/module_shim.tmpl b/mypyc/lib-rt/module_shim.tmpl index 1cb7fd20d47aa..053c451401aa1 100644 --- a/mypyc/lib-rt/module_shim.tmpl +++ b/mypyc/lib-rt/module_shim.tmpl @@ -1,5 +1,11 @@ #include +#ifdef MYPYC_STATIC_LINKING +// With static linking, functions in the shared library are linked directly instead +// of looked up via capsules. +extern PyObject *CPyInit_{full_modname}(void); +#endif + PyMODINIT_FUNC PyInit_{modname}(void) {{ @@ -16,6 +22,12 @@ PyInit_{modname}(void) }} tmp = PyImport_ImportModuleLevel("{libname}", NULL, NULL, fromlist, 0); if (!tmp) return NULL; +#ifdef MYPYC_STATIC_LINKING + // Importing the shared library initializes it, and the module init populates the + // cross-group export tables. + Py_DECREF(tmp); + return CPyInit_{full_modname}(); +#else // Populate cross-group export tables lazily, just before we instantiate // the per-module real init. Deferring this out of the shared lib's own // PyInit keeps separate-mode compiled modules from recursively triggering @@ -49,8 +61,11 @@ PyInit_{modname}(void) return NULL; }} return ((PyObject *(*)(void))init_func)(); +#endif }} +#ifndef MYPYC_STATIC_LINKING // distutils sometimes spuriously tells cl to export CPyInit___init__, -// so provide that so it chills out +// so provide that so it chills out (with static linking this would be a duplicate symbol) PyMODINIT_FUNC PyInit___init__(void) {{ return PyInit_{modname}(); }} +#endif diff --git a/mypyc/lib-rt/module_shim_no_gil_multiphase.tmpl b/mypyc/lib-rt/module_shim_no_gil_multiphase.tmpl index 234583bf0d105..ec5309a47cde0 100644 --- a/mypyc/lib-rt/module_shim_no_gil_multiphase.tmpl +++ b/mypyc/lib-rt/module_shim_no_gil_multiphase.tmpl @@ -1,5 +1,11 @@ #include +#ifdef MYPYC_STATIC_LINKING +// With static linking, functions in the shared library are linked directly instead +// of looked up via capsules. +extern int CPyExec_{full_modname}(PyObject *); +#endif + static int {modname}_exec(PyObject *module) {{ PyObject *tmp; @@ -15,6 +21,13 @@ static int {modname}_exec(PyObject *module) }} tmp = PyImport_ImportModuleLevel("{libname}", NULL, NULL, fromlist, 0); if (!tmp) return -1; +#ifdef MYPYC_STATIC_LINKING + // Importing the shared library initializes it, and the module exec populates the + // cross-group export tables. + Py_DECREF(tmp); + if (CPyExec_{full_modname}(module) != 0) return -1; + return 0; +#else // Populate cross-group export tables lazily, just before we instantiate // the per-module real init. Deferring this out of the shared lib's own // PyInit keeps separate-mode compiled modules from recursively triggering @@ -47,6 +60,7 @@ static int {modname}_exec(PyObject *module) if (!exec_func) return -1; if (((int (*)(PyObject *))exec_func)(module) != 0) return -1; return 0; +#endif }} static PyModuleDef_Slot {modname}_slots[] = {{ @@ -71,6 +85,8 @@ PyInit_{modname}(void) return PyModuleDef_Init(&{modname}_module); }} +#ifndef MYPYC_STATIC_LINKING // distutils sometimes spuriously tells cl to export CPyInit___init__, -// so provide that so it chills out +// so provide that so it chills out (with static linking this would be a duplicate symbol) PyMODINIT_FUNC PyInit___init__(void) {{ return PyInit_{modname}(); }} +#endif diff --git a/mypyc/lib-rt/random/librt_random.c b/mypyc/lib-rt/random/librt_random.c index 7dc590eaa5946..d4a0c320a04d8 100644 --- a/mypyc/lib-rt/random/librt_random.c +++ b/mypyc/lib-rt/random/librt_random.c @@ -13,6 +13,13 @@ #include "mypyc_util.h" #include "CPy.h" +// With static linking, compiled code calls the API functions and refers to the +// type objects directly, so they need external linkage +#ifdef MYPYC_STATIC_LINKING +#define LIBRT_API_LINKAGE +#else +#define LIBRT_API_LINKAGE static +#endif #include "librt_random.h" // @@ -442,12 +449,12 @@ typedef struct { chacha8_rng rng; } RandomObject; -static PyTypeObject RandomType; +LIBRT_API_LINKAGE PyTypeObject LibRTRandom_RandomType; static PyObject* Random_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - if (type != &RandomType) { + if (type != &LibRTRandom_RandomType) { PyErr_SetString(PyExc_TypeError, "Random cannot be subclassed"); return NULL; } @@ -487,9 +494,9 @@ Random_init(RandomObject *self, PyObject *args, PyObject *kwds) // Internal constructors for capsule API (bypass tp_new/tp_init) -static PyObject * -Random_internal(void) { - RandomObject *self = (RandomObject *)RandomType.tp_alloc(&RandomType, 0); +LIBRT_API_LINKAGE PyObject * +LibRTRandom_Random_internal(void) { + RandomObject *self = (RandomObject *)LibRTRandom_RandomType.tp_alloc(&LibRTRandom_RandomType, 0); if (self == NULL) return NULL; if (chacha8_init(&self->rng) < 0) { @@ -499,22 +506,22 @@ Random_internal(void) { return (PyObject *)self; } -static PyObject * -Random_from_seed_internal(int64_t seed_val) { - RandomObject *self = (RandomObject *)RandomType.tp_alloc(&RandomType, 0); +LIBRT_API_LINKAGE PyObject * +LibRTRandom_Random_from_seed_internal(int64_t seed_val) { + RandomObject *self = (RandomObject *)LibRTRandom_RandomType.tp_alloc(&LibRTRandom_RandomType, 0); if (self == NULL) return NULL; chacha8_seed_int(&self->rng, seed_val); return (PyObject *)self; } -static PyTypeObject * -Random_type_internal(void) { - return &RandomType; +LIBRT_API_LINKAGE PyTypeObject * +LibRTRandom_Random_type_internal(void) { + return &LibRTRandom_RandomType; } -static int64_t -Random_randrange1_internal(PyObject *self, int64_t stop) { +LIBRT_API_LINKAGE int64_t +LibRTRandom_Random_randrange1_internal(PyObject *self, int64_t stop) { if (unlikely(stop <= 0)) { PyErr_SetString(PyExc_ValueError, "empty range for randrange()"); return CPY_LL_INT_ERROR; @@ -522,8 +529,8 @@ Random_randrange1_internal(PyObject *self, int64_t stop) { return (int64_t)chacha8_next_ranged(&((RandomObject *)self)->rng, (uint64_t)stop); } -static int64_t -Random_randrange2_internal(PyObject *self, int64_t start, int64_t stop) { +LIBRT_API_LINKAGE int64_t +LibRTRandom_Random_randrange2_internal(PyObject *self, int64_t start, int64_t stop) { if (unlikely(start >= stop)) { PyErr_SetString(PyExc_ValueError, "empty range for randrange()"); return CPY_LL_INT_ERROR; @@ -532,8 +539,8 @@ Random_randrange2_internal(PyObject *self, int64_t start, int64_t stop) { return random_i64_from_range(&((RandomObject *)self)->rng, start, range); } -static int64_t -Random_randint_internal(PyObject *self, int64_t a, int64_t b) { +LIBRT_API_LINKAGE int64_t +LibRTRandom_Random_randint_internal(PyObject *self, int64_t a, int64_t b) { if (unlikely(a > b)) { PyErr_SetString(PyExc_ValueError, "empty range for randint()"); return CPY_LL_INT_ERROR; @@ -542,8 +549,8 @@ Random_randint_internal(PyObject *self, int64_t a, int64_t b) { return random_i64_from_range(&((RandomObject *)self)->rng, a, range); } -static double -Random_random_internal(PyObject *self) { +LIBRT_API_LINKAGE double +LibRTRandom_Random_random_internal(PyObject *self) { return random_double_impl(&((RandomObject *)self)->rng); } @@ -615,7 +622,7 @@ static PyMethodDef Random_methods[] = { {NULL} /* Sentinel */ }; -static PyTypeObject RandomType = { +LIBRT_API_LINKAGE PyTypeObject LibRTRandom_RandomType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "Random", .tp_doc = PyDoc_STR("Fast random number generator using ChaCha8"), @@ -647,16 +654,16 @@ static PyMethodDef librt_random_module_methods[] = { // Module-level internal functions for mypyc primitives (use thread-local RNG) -static double -module_random_internal(void) { +LIBRT_API_LINKAGE double +LibRTRandom_module_random_internal(void) { chacha8_rng *rng = get_thread_rng(); if (rng == NULL) return CPY_FLOAT_ERROR; return random_double_impl(rng); } -static int64_t -module_randint_internal(int64_t a, int64_t b) { +LIBRT_API_LINKAGE int64_t +LibRTRandom_module_randint_internal(int64_t a, int64_t b) { if (unlikely(a > b)) { PyErr_SetString(PyExc_ValueError, "empty range for randint()"); return CPY_LL_INT_ERROR; @@ -668,8 +675,8 @@ module_randint_internal(int64_t a, int64_t b) { return random_i64_from_range(rng, a, range); } -static int64_t -module_randrange1_internal(int64_t stop) { +LIBRT_API_LINKAGE int64_t +LibRTRandom_module_randrange1_internal(int64_t stop) { if (unlikely(stop <= 0)) { PyErr_SetString(PyExc_ValueError, "empty range for randrange()"); return CPY_LL_INT_ERROR; @@ -680,8 +687,8 @@ module_randrange1_internal(int64_t stop) { return (int64_t)chacha8_next_ranged(rng, (uint64_t)stop); } -static int64_t -module_randrange2_internal(int64_t start, int64_t stop) { +LIBRT_API_LINKAGE int64_t +LibRTRandom_module_randrange2_internal(int64_t start, int64_t stop) { if (unlikely(start >= stop)) { PyErr_SetString(PyExc_ValueError, "empty range for randrange()"); return CPY_LL_INT_ERROR; @@ -693,13 +700,13 @@ module_randrange2_internal(int64_t start, int64_t stop) { return random_i64_from_range(rng, start, range); } -static int -random_abi_version(void) { +LIBRT_API_LINKAGE int +LibRTRandom_ABIVersion(void) { return LIBRT_RANDOM_ABI_VERSION; } -static int -random_api_version(void) { +LIBRT_API_LINKAGE int +LibRTRandom_APIVersion(void) { return LIBRT_RANDOM_API_VERSION; } @@ -709,32 +716,34 @@ librt_random_module_exec(PyObject *m) if (ensure_tls_key() < 0) { return -1; } - if (PyType_Ready(&RandomType) < 0) { + if (PyType_Ready(&LibRTRandom_RandomType) < 0) { return -1; } - if (PyModule_AddObjectRef(m, "Random", (PyObject *) &RandomType) < 0) { + if (PyModule_AddObjectRef(m, "Random", (PyObject *) &LibRTRandom_RandomType) < 0) { return -1; } +#ifndef MYPYC_STATIC_LINKING // Export mypyc internal C API via capsule static void *librt_random_api[LIBRT_RANDOM_API_LEN] = { - (void *)random_abi_version, - (void *)random_api_version, - (void *)Random_internal, - (void *)Random_from_seed_internal, - (void *)Random_type_internal, - (void *)Random_random_internal, - (void *)Random_randint_internal, - (void *)Random_randrange1_internal, - (void *)Random_randrange2_internal, - (void *)module_random_internal, - (void *)module_randint_internal, - (void *)module_randrange1_internal, - (void *)module_randrange2_internal, + (void *)LibRTRandom_ABIVersion, + (void *)LibRTRandom_APIVersion, + (void *)LibRTRandom_Random_internal, + (void *)LibRTRandom_Random_from_seed_internal, + (void *)LibRTRandom_Random_type_internal, + (void *)LibRTRandom_Random_random_internal, + (void *)LibRTRandom_Random_randint_internal, + (void *)LibRTRandom_Random_randrange1_internal, + (void *)LibRTRandom_Random_randrange2_internal, + (void *)LibRTRandom_module_random_internal, + (void *)LibRTRandom_module_randint_internal, + (void *)LibRTRandom_module_randrange1_internal, + (void *)LibRTRandom_module_randrange2_internal, }; PyObject *c_api_object = PyCapsule_New((void *)librt_random_api, "librt.random._C_API", NULL); if (PyModule_Add(m, "_C_API", c_api_object) < 0) { return -1; } +#endif return 0; } diff --git a/mypyc/lib-rt/random/librt_random.h b/mypyc/lib-rt/random/librt_random.h index 2eabfbd021bc9..0c383c675ea8b 100644 --- a/mypyc/lib-rt/random/librt_random.h +++ b/mypyc/lib-rt/random/librt_random.h @@ -7,4 +7,24 @@ #define LIBRT_RANDOM_API_VERSION 9 #define LIBRT_RANDOM_API_LEN 13 +#ifdef MYPYC_STATIC_LINKING +// With static linking, the API is used directly (see librt_random_api.h) +#include +#include +extern PyTypeObject LibRTRandom_RandomType; +int LibRTRandom_ABIVersion(void); +int LibRTRandom_APIVersion(void); +PyObject *LibRTRandom_Random_internal(void); +PyObject *LibRTRandom_Random_from_seed_internal(int64_t seed_val); +PyTypeObject *LibRTRandom_Random_type_internal(void); +double LibRTRandom_Random_random_internal(PyObject *self); +int64_t LibRTRandom_Random_randint_internal(PyObject *self, int64_t a, int64_t b); +int64_t LibRTRandom_Random_randrange1_internal(PyObject *self, int64_t stop); +int64_t LibRTRandom_Random_randrange2_internal(PyObject *self, int64_t start, int64_t stop); +double LibRTRandom_module_random_internal(void); +int64_t LibRTRandom_module_randint_internal(int64_t a, int64_t b); +int64_t LibRTRandom_module_randrange1_internal(int64_t stop); +int64_t LibRTRandom_module_randrange2_internal(int64_t start, int64_t stop); +#endif + #endif // LIBRT_RANDOM_H diff --git a/mypyc/lib-rt/random/librt_random_api.c b/mypyc/lib-rt/random/librt_random_api.c index 157fa82b82eb3..b76047e49a155 100644 --- a/mypyc/lib-rt/random/librt_random_api.c +++ b/mypyc/lib-rt/random/librt_random_api.c @@ -2,7 +2,9 @@ #include "librt_random_api.h" +#ifndef MYPYC_STATIC_LINKING void *LibRTRandom_API[LIBRT_RANDOM_API_LEN] = {0}; +#endif int import_librt_random(void) @@ -11,6 +13,11 @@ import_librt_random(void) if (mod == NULL) return -1; Py_DECREF(mod); // we import just for the side effect of making the below work. +#ifdef MYPYC_STATIC_LINKING + // librt is statically linked, so the API is used directly. Importing the + // module above initializes it (e.g. its types). + return 0; +#else void **capsule = (void **)PyCapsule_Import("librt.random._C_API", 0); if (capsule == NULL) return -1; @@ -42,4 +49,5 @@ import_librt_random(void) // entries, so this copy is safe. memcpy(LibRTRandom_API, capsule, sizeof(LibRTRandom_API)); return 0; +#endif } diff --git a/mypyc/lib-rt/random/librt_random_api.h b/mypyc/lib-rt/random/librt_random_api.h index 2794de0dd7e58..98ca139fa7167 100644 --- a/mypyc/lib-rt/random/librt_random_api.h +++ b/mypyc/lib-rt/random/librt_random_api.h @@ -9,6 +9,11 @@ int import_librt_random(void); +#ifdef MYPYC_STATIC_LINKING +// librt is statically linked, so the API functions (declared in librt_random.h) are +// called directly, and type objects are referred to directly +#define LibRTRandom_Random_type_internal() (&LibRTRandom_RandomType) +#else extern void *LibRTRandom_API[LIBRT_RANDOM_API_LEN]; #define LibRTRandom_ABIVersion (*(int (*)(void)) LibRTRandom_API[0]) @@ -24,6 +29,7 @@ extern void *LibRTRandom_API[LIBRT_RANDOM_API_LEN]; #define LibRTRandom_module_randint_internal (*(int64_t (*)(int64_t, int64_t)) LibRTRandom_API[10]) #define LibRTRandom_module_randrange1_internal (*(int64_t (*)(int64_t)) LibRTRandom_API[11]) #define LibRTRandom_module_randrange2_internal (*(int64_t (*)(int64_t, int64_t)) LibRTRandom_API[12]) +#endif static inline bool CPyRandom_Check(PyObject *obj) { return Py_TYPE(obj) == LibRTRandom_Random_type_internal(); diff --git a/mypyc/lib-rt/strings/librt_strings.c b/mypyc/lib-rt/strings/librt_strings.c index 153ef54454f36..b4b37a193878e 100644 --- a/mypyc/lib-rt/strings/librt_strings.c +++ b/mypyc/lib-rt/strings/librt_strings.c @@ -4,6 +4,13 @@ #include #include #include "CPy.h" +// With static linking, compiled code calls the API functions and refers to the +// type objects directly, so they need external linkage +#ifdef MYPYC_STATIC_LINKING +#define LIBRT_API_LINKAGE +#else +#define LIBRT_API_LINKAGE static +#endif #include "librt_strings.h" #define CPY_BOOL_ERROR 2 @@ -18,10 +25,10 @@ ((BytesWriterObject *)data)->len += sizeof(type); \ } while (0) -static PyTypeObject BytesWriterType; +LIBRT_API_LINKAGE PyTypeObject LibRTStrings_BytesWriterType; -static bool -_grow_buffer(BytesWriterObject *data, Py_ssize_t n) { +LIBRT_API_LINKAGE bool +LibRTStrings_ByteWriter_grow_buffer_internal(BytesWriterObject *data, Py_ssize_t n) { if (unlikely(n > PY_SSIZE_T_MAX - data->len)) { PyErr_NoMemory(); return false; @@ -59,7 +66,7 @@ ensure_bytes_writer_size(BytesWriterObject *data, Py_ssize_t n) { if (likely(data->capacity - data->len >= n)) { return true; } else { - return _grow_buffer(data, n); + return LibRTStrings_ByteWriter_grow_buffer_internal(data, n); } } @@ -73,7 +80,7 @@ BytesWriter_init_internal(BytesWriterObject *self) { static PyObject* BytesWriter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - if (type != &BytesWriterType) { + if (type != &LibRTStrings_BytesWriterType) { PyErr_SetString(PyExc_TypeError, "BytesWriter cannot be subclassed"); return NULL; } @@ -84,9 +91,9 @@ BytesWriter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return (PyObject *)self; } -static PyObject * -BytesWriter_internal(void) { - BytesWriterObject *self = (BytesWriterObject *)BytesWriterType.tp_alloc(&BytesWriterType, 0); +LIBRT_API_LINKAGE PyObject * +LibRTStrings_BytesWriter_internal(void) { + BytesWriterObject *self = (BytesWriterObject *)LibRTStrings_BytesWriterType.tp_alloc(&LibRTStrings_BytesWriterType, 0); if (self == NULL) return NULL; BytesWriter_init_internal(self); @@ -123,8 +130,8 @@ BytesWriter_dealloc(BytesWriterObject *self) Py_TYPE(self)->tp_free((PyObject *)self); } -static PyObject* -BytesWriter_getvalue_internal(PyObject *self) +LIBRT_API_LINKAGE PyObject* +LibRTStrings_BytesWriter_getvalue_internal(PyObject *self) { BytesWriterObject *obj = (BytesWriterObject *)self; return PyBytes_FromStringAndSize(obj->buf, obj->len); @@ -133,7 +140,7 @@ BytesWriter_getvalue_internal(PyObject *self) static PyObject* BytesWriter_repr(BytesWriterObject *self) { - PyObject *value = BytesWriter_getvalue_internal((PyObject *)self); + PyObject *value = LibRTStrings_BytesWriter_getvalue_internal((PyObject *)self); if (value == NULL) { return NULL; } @@ -229,7 +236,7 @@ static PyMethodDef BytesWriter_methods[] = { {NULL} /* Sentinel */ }; -static PyTypeObject BytesWriterType = { +LIBRT_API_LINKAGE PyTypeObject LibRTStrings_BytesWriterType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "BytesWriter", .tp_doc = PyDoc_STR("Memory buffer for building bytes objects from parts"), @@ -246,7 +253,7 @@ static PyTypeObject BytesWriterType = { static inline bool check_bytes_writer(PyObject *data) { - if (unlikely(Py_TYPE(data) != &BytesWriterType)) { + if (unlikely(Py_TYPE(data) != &LibRTStrings_BytesWriterType)) { PyErr_Format( PyExc_TypeError, "data must be a BytesWriter object, got %s", Py_TYPE(data)->tp_name ); @@ -296,8 +303,8 @@ BytesWriter_write(PyObject *self, PyObject *const *args, size_t nargs) { return Py_None; } -static inline char -BytesWriter_append_internal(BytesWriterObject *self, uint8_t value) { +LIBRT_API_LINKAGE inline char +LibRTStrings_BytesWriter_append_internal(BytesWriterObject *self, uint8_t value) { if (!ensure_bytes_writer_size(self, 1)) return CPY_NONE_ERROR; _WRITE_BYTES(self, uint8_t, value); @@ -320,15 +327,15 @@ BytesWriter_append(PyObject *self, PyObject *const *args, size_t nargs) { CPy_TypeError("u8", value); return NULL; } - if (unlikely(BytesWriter_append_internal((BytesWriterObject *)self, unboxed) == CPY_NONE_ERROR)) { + if (unlikely(LibRTStrings_BytesWriter_append_internal((BytesWriterObject *)self, unboxed) == CPY_NONE_ERROR)) { return NULL; } Py_INCREF(Py_None); return Py_None; } -static char -BytesWriter_truncate_internal(PyObject *self, int64_t size) { +LIBRT_API_LINKAGE char +LibRTStrings_BytesWriter_truncate_internal(PyObject *self, int64_t size) { BytesWriterObject *writer = (BytesWriterObject *)self; Py_ssize_t current_size = writer->len; @@ -371,16 +378,16 @@ BytesWriter_truncate(PyObject *self, PyObject *const *args, size_t nargs) { return NULL; } - if (unlikely(BytesWriter_truncate_internal(self, size) == CPY_NONE_ERROR)) { + if (unlikely(LibRTStrings_BytesWriter_truncate_internal(self, size) == CPY_NONE_ERROR)) { return NULL; } Py_INCREF(Py_None); return Py_None; } -static PyTypeObject * -BytesWriter_type_internal(void) { - return &BytesWriterType; // Return borrowed reference +LIBRT_API_LINKAGE PyTypeObject * +LibRTStrings_BytesWriter_type_internal(void) { + return &LibRTStrings_BytesWriterType; // Return borrowed reference }; static CPyTagged @@ -393,7 +400,7 @@ BytesWriter_len_internal(PyObject *self) { // StringWriter // -static PyTypeObject StringWriterType; +LIBRT_API_LINKAGE PyTypeObject LibRTStrings_StringWriterType; static void convert_string_data_in_place(char *buf, Py_ssize_t len, char old_kind, char new_kind); @@ -455,7 +462,7 @@ grow_string_buffer_helper(StringWriterObject *self, Py_ssize_t target_capacity, return true; } -static bool grow_string_buffer(StringWriterObject *data, Py_ssize_t n) { +LIBRT_API_LINKAGE bool LibRTStrings_grow_string_buffer(StringWriterObject *data, Py_ssize_t n) { if (unlikely(n > PY_SSIZE_T_MAX - data->len)) { PyErr_NoMemory(); return false; @@ -470,7 +477,7 @@ ensure_string_writer_size(StringWriterObject *data, Py_ssize_t n) { } else { // Don't inline the grow function since this is slow path and we // want to keep this as short as possible for better inlining - return grow_string_buffer(data, n); + return LibRTStrings_grow_string_buffer(data, n); } } @@ -485,7 +492,7 @@ StringWriter_init_internal(StringWriterObject *self) { static PyObject* StringWriter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - if (type != &StringWriterType) { + if (type != &LibRTStrings_StringWriterType) { PyErr_SetString(PyExc_TypeError, "StringWriter cannot be subclassed"); return NULL; } @@ -496,9 +503,9 @@ StringWriter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return (PyObject *)self; } -static PyObject * -StringWriter_internal(void) { - StringWriterObject *self = (StringWriterObject *)StringWriterType.tp_alloc(&StringWriterType, 0); +LIBRT_API_LINKAGE PyObject * +LibRTStrings_StringWriter_internal(void) { + StringWriterObject *self = (StringWriterObject *)LibRTStrings_StringWriterType.tp_alloc(&LibRTStrings_StringWriterType, 0); if (self == NULL) return NULL; StringWriter_init_internal(self); @@ -535,8 +542,8 @@ StringWriter_dealloc(StringWriterObject *self) Py_TYPE(self)->tp_free((PyObject *)self); } -static PyObject* -StringWriter_getvalue_internal(PyObject *self) +LIBRT_API_LINKAGE PyObject* +LibRTStrings_StringWriter_getvalue_internal(PyObject *self) { StringWriterObject *obj = (StringWriterObject *)self; return PyUnicode_FromKindAndData(obj->kind, obj->buf, obj->len); @@ -545,7 +552,7 @@ StringWriter_getvalue_internal(PyObject *self) static PyObject* StringWriter_repr(StringWriterObject *self) { - PyObject *value = StringWriter_getvalue_internal((PyObject *)self); + PyObject *value = LibRTStrings_StringWriter_getvalue_internal((PyObject *)self); if (value == NULL) { return NULL; } @@ -619,7 +626,7 @@ static PyMethodDef StringWriter_methods[] = { {NULL} /* Sentinel */ }; -static PyTypeObject StringWriterType = { +LIBRT_API_LINKAGE PyTypeObject LibRTStrings_StringWriterType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "StringWriter", .tp_doc = PyDoc_STR("Memory buffer for building string objects from parts"), @@ -636,7 +643,7 @@ static PyTypeObject StringWriterType = { static inline bool check_string_writer(PyObject *data) { - if (unlikely(Py_TYPE(data) != &StringWriterType)) { + if (unlikely(Py_TYPE(data) != &LibRTStrings_StringWriterType)) { PyErr_Format( PyExc_TypeError, "data must be a StringWriter object, got %s", Py_TYPE(data)->tp_name ); @@ -647,8 +654,8 @@ check_string_writer(PyObject *data) { static char string_writer_switch_kind(StringWriterObject *self, int32_t value); -static char -StringWriter_write_internal(PyObject *obj, PyObject *value) { +LIBRT_API_LINKAGE char +LibRTStrings_StringWriter_write_internal(PyObject *obj, PyObject *value) { StringWriterObject *self = (StringWriterObject *)obj; Py_ssize_t str_len = PyUnicode_GET_LENGTH(value); if (str_len == 0) { @@ -705,7 +712,7 @@ StringWriter_write(PyObject *self, PyObject *const *args, size_t nargs) { PyErr_SetString(PyExc_TypeError, "value must be a str object"); return NULL; } - if (unlikely(StringWriter_write_internal(self, value) == CPY_NONE_ERROR)) { + if (unlikely(LibRTStrings_StringWriter_write_internal(self, value) == CPY_NONE_ERROR)) { return NULL; } Py_INCREF(Py_None); @@ -781,7 +788,7 @@ static char string_writer_switch_kind(StringWriterObject *self, int32_t value) { } // Handle all append cases except for append that stays within kind 1 -static char string_append_slow_path(StringWriterObject *self, int32_t value) { +LIBRT_API_LINKAGE char LibRTStrings_string_append_slow_path(StringWriterObject *self, int32_t value) { if (self->kind == 2) { if ((uint32_t)value <= 0xffff) { // Fast path - kind 2 stays the same @@ -798,7 +805,7 @@ static char string_append_slow_path(StringWriterObject *self, int32_t value) { goto fail_range; if (string_writer_switch_kind(self, value) == CPY_NONE_ERROR) return CPY_NONE_ERROR; - return string_append_slow_path(self, value); + return LibRTStrings_string_append_slow_path(self, value); } else if (self->kind == 1) { // Check precondition -- this must only be used on slow path assert((uint32_t)value > 0xff); @@ -806,7 +813,7 @@ static char string_append_slow_path(StringWriterObject *self, int32_t value) { goto fail_range; if (string_writer_switch_kind(self, value) == CPY_NONE_ERROR) return CPY_NONE_ERROR; - return string_append_slow_path(self, value); + return LibRTStrings_string_append_slow_path(self, value); } assert(self->kind == 4); if (unlikely((uint32_t)value > 0x10FFFF)) @@ -835,7 +842,7 @@ StringWriter_append_internal(StringWriterObject *self, int32_t value) { self->kind = kind; return CPY_NONE; } - return string_append_slow_path(self, value); + return LibRTStrings_string_append_slow_path(self, value); } static PyObject* @@ -861,9 +868,9 @@ StringWriter_append(PyObject *self, PyObject *const *args, size_t nargs) { return Py_None; } -static PyTypeObject * -StringWriter_type_internal(void) { - return &StringWriterType; // Return borrowed reference +LIBRT_API_LINKAGE PyTypeObject * +LibRTStrings_StringWriter_type_internal(void) { + return &LibRTStrings_StringWriterType; // Return borrowed reference }; static CPyTagged @@ -1288,53 +1295,55 @@ static PyMethodDef librt_strings_module_methods[] = { {NULL, NULL, 0, NULL} }; -static int -strings_abi_version(void) { +LIBRT_API_LINKAGE int +LibRTStrings_ABIVersion(void) { return LIBRT_STRINGS_ABI_VERSION; } -static int -strings_api_version(void) { +LIBRT_API_LINKAGE int +LibRTStrings_APIVersion(void) { return LIBRT_STRINGS_API_VERSION; } static int librt_strings_module_exec(PyObject *m) { - if (PyType_Ready(&BytesWriterType) < 0) { + if (PyType_Ready(&LibRTStrings_BytesWriterType) < 0) { return -1; } - if (PyType_Ready(&StringWriterType) < 0) { + if (PyType_Ready(&LibRTStrings_StringWriterType) < 0) { return -1; } - if (PyModule_AddObjectRef(m, "BytesWriter", (PyObject *) &BytesWriterType) < 0) { + if (PyModule_AddObjectRef(m, "BytesWriter", (PyObject *) &LibRTStrings_BytesWriterType) < 0) { return -1; } - if (PyModule_AddObjectRef(m, "StringWriter", (PyObject *) &StringWriterType) < 0) { + if (PyModule_AddObjectRef(m, "StringWriter", (PyObject *) &LibRTStrings_StringWriterType) < 0) { return -1; } +#ifndef MYPYC_STATIC_LINKING // Export mypy internal C API, be careful with the order! static void *librt_strings_api[LIBRT_STRINGS_API_LEN] = { - (void *)strings_abi_version, - (void *)strings_api_version, - (void *)BytesWriter_internal, - (void *)BytesWriter_getvalue_internal, - (void *)BytesWriter_append_internal, - (void *)_grow_buffer, - (void *)BytesWriter_type_internal, - (void *)BytesWriter_truncate_internal, - (void *)StringWriter_internal, - (void *)StringWriter_getvalue_internal, - (void *)string_append_slow_path, - (void *)StringWriter_type_internal, - (void *)StringWriter_write_internal, - (void *)grow_string_buffer, + (void *)LibRTStrings_ABIVersion, + (void *)LibRTStrings_APIVersion, + (void *)LibRTStrings_BytesWriter_internal, + (void *)LibRTStrings_BytesWriter_getvalue_internal, + (void *)LibRTStrings_BytesWriter_append_internal, + (void *)LibRTStrings_ByteWriter_grow_buffer_internal, + (void *)LibRTStrings_BytesWriter_type_internal, + (void *)LibRTStrings_BytesWriter_truncate_internal, + (void *)LibRTStrings_StringWriter_internal, + (void *)LibRTStrings_StringWriter_getvalue_internal, + (void *)LibRTStrings_string_append_slow_path, + (void *)LibRTStrings_StringWriter_type_internal, + (void *)LibRTStrings_StringWriter_write_internal, + (void *)LibRTStrings_grow_string_buffer, }; PyObject *c_api_object = PyCapsule_New((void *)librt_strings_api, "librt.strings._C_API", NULL); if (PyModule_Add(m, "_C_API", c_api_object) < 0) { return -1; } +#endif return 0; } diff --git a/mypyc/lib-rt/strings/librt_strings.h b/mypyc/lib-rt/strings/librt_strings.h index 33af57dc2a2a8..a94858b03a9a6 100644 --- a/mypyc/lib-rt/strings/librt_strings.h +++ b/mypyc/lib-rt/strings/librt_strings.h @@ -128,4 +128,26 @@ static inline int32_t LibRTStrings_ToLower(int32_t c) { return LibRTStrings_ChangeCase_slow(c, "lower"); } +#ifdef MYPYC_STATIC_LINKING +// With static linking, the API is used directly (see librt_strings_api.h) +#include +#include +extern PyTypeObject LibRTStrings_BytesWriterType; +extern PyTypeObject LibRTStrings_StringWriterType; +int LibRTStrings_ABIVersion(void); +int LibRTStrings_APIVersion(void); +PyObject *LibRTStrings_BytesWriter_internal(void); +PyObject *LibRTStrings_BytesWriter_getvalue_internal(PyObject *self); +char LibRTStrings_BytesWriter_append_internal(BytesWriterObject *self, uint8_t value); +bool LibRTStrings_ByteWriter_grow_buffer_internal(BytesWriterObject *data, Py_ssize_t n); +PyTypeObject *LibRTStrings_BytesWriter_type_internal(void); +char LibRTStrings_BytesWriter_truncate_internal(PyObject *self, int64_t size); +PyObject *LibRTStrings_StringWriter_internal(void); +PyObject *LibRTStrings_StringWriter_getvalue_internal(PyObject *self); +char LibRTStrings_string_append_slow_path(StringWriterObject *self, int32_t value); +PyTypeObject *LibRTStrings_StringWriter_type_internal(void); +char LibRTStrings_StringWriter_write_internal(PyObject *obj, PyObject *value); +bool LibRTStrings_grow_string_buffer(StringWriterObject *data, Py_ssize_t n); +#endif + #endif // LIBRT_STRINGS_H diff --git a/mypyc/lib-rt/strings/librt_strings_api.c b/mypyc/lib-rt/strings/librt_strings_api.c index 50511e7a3fe00..d934d03bd0632 100644 --- a/mypyc/lib-rt/strings/librt_strings_api.c +++ b/mypyc/lib-rt/strings/librt_strings_api.c @@ -1,6 +1,8 @@ #include "librt_strings_api.h" +#ifndef MYPYC_STATIC_LINKING void *LibRTStrings_API[LIBRT_STRINGS_API_LEN] = {0}; +#endif int import_librt_strings(void) @@ -9,6 +11,11 @@ import_librt_strings(void) if (mod == NULL) return -1; Py_DECREF(mod); // we import just for the side effect of making the below work. +#ifdef MYPYC_STATIC_LINKING + // librt is statically linked, so the API is used directly. Importing the + // module above initializes it (e.g. its types). + return 0; +#else void **capsule = (void **)PyCapsule_Import("librt.strings._C_API", 0); if (capsule == NULL) return -1; @@ -40,4 +47,5 @@ import_librt_strings(void) // entries, so this copy is safe. memcpy(LibRTStrings_API, capsule, sizeof(LibRTStrings_API)); return 0; +#endif } diff --git a/mypyc/lib-rt/strings/librt_strings_api.h b/mypyc/lib-rt/strings/librt_strings_api.h index 536b90ad7f21c..942c5b0a7a2ce 100644 --- a/mypyc/lib-rt/strings/librt_strings_api.h +++ b/mypyc/lib-rt/strings/librt_strings_api.h @@ -8,6 +8,13 @@ import_librt_strings(void); #include #include "librt_strings.h" +#ifdef MYPYC_STATIC_LINKING +// librt is statically linked, so the API functions (declared in librt_strings.h) are +// called directly, and type objects are referred to directly +#define LibRTStrings_BytesWriter_type_internal() (&LibRTStrings_BytesWriterType) +#define LibRTStrings_StringWriter_type_internal() (&LibRTStrings_StringWriterType) +#define LibRTStrings_BytesWriter_append_internal(a0, a1) LibRTStrings_BytesWriter_append_internal((BytesWriterObject *)(a0), (a1)) +#else extern void *LibRTStrings_API[LIBRT_STRINGS_API_LEN]; #define LibRTStrings_ABIVersion (*(int (*)(void)) LibRTStrings_API[0]) @@ -24,6 +31,7 @@ extern void *LibRTStrings_API[LIBRT_STRINGS_API_LEN]; #define LibRTStrings_StringWriter_type_internal (*(PyTypeObject* (*)(void)) LibRTStrings_API[11]) #define LibRTStrings_StringWriter_write_internal (*(char (*)(PyObject *source, PyObject *value)) LibRTStrings_API[12]) #define LibRTStrings_grow_string_buffer (*(bool (*)(StringWriterObject *obj, Py_ssize_t n)) LibRTStrings_API[13]) +#endif static inline bool CPyBytesWriter_Check(PyObject *obj) { diff --git a/mypyc/lib-rt/threading/librt_threading.c b/mypyc/lib-rt/threading/librt_threading.c index 84d7346a75872..8cb666238dd04 100644 --- a/mypyc/lib-rt/threading/librt_threading.c +++ b/mypyc/lib-rt/threading/librt_threading.c @@ -2,6 +2,13 @@ #define PY_SSIZE_T_CLEAN #include +// With static linking, compiled code calls the API functions and refers to the +// type objects directly, so they need external linkage +#ifdef MYPYC_STATIC_LINKING +#define LIBRT_API_LINKAGE +#else +#define LIBRT_API_LINKAGE static +#endif #include "librt_threading.h" #include "mypyc_util.h" @@ -448,12 +455,12 @@ Lock_is_locked(LockObject *self) // ---------- Python type methods (shared across platforms) ---------- -static PyTypeObject LockType; +LIBRT_API_LINKAGE PyTypeObject LibRTThreading_LockType; static PyObject * Lock_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - if (type != &LockType) { + if (type != &LibRTThreading_LockType) { PyErr_SetString(PyExc_TypeError, "Lock cannot be subclassed"); return NULL; } @@ -579,7 +586,7 @@ static PyMethodDef Lock_methods[] = { {NULL} }; -static PyTypeObject LockType = { +LIBRT_API_LINKAGE PyTypeObject LibRTThreading_LockType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "Lock", .tp_doc = PyDoc_STR("A fast mutual exclusion lock"), @@ -592,17 +599,17 @@ static PyTypeObject LockType = { .tp_methods = Lock_methods, }; -static PyTypeObject * -Lock_type_internal(void) { - return &LockType; +LIBRT_API_LINKAGE PyTypeObject * +LibRTThreading_Lock_type_internal(void) { + return &LibRTThreading_LockType; } // Create a new Lock object (for use from compiled code) -static PyObject * -Lock_new_internal(void) { - LockObject *self = (LockObject *)LockType.tp_alloc(&LockType, 0); +LIBRT_API_LINKAGE PyObject * +LibRTThreading_Lock_new_internal(void) { + LockObject *self = (LockObject *)LibRTThreading_LockType.tp_alloc(&LibRTThreading_LockType, 0); if (self != NULL && Lock_init_internal(self) < 0) { - LockType.tp_free((PyObject *)self); + LibRTThreading_LockType.tp_free((PyObject *)self); return NULL; } return (PyObject *)self; @@ -610,8 +617,8 @@ Lock_new_internal(void) { // Acquire the lock (blocking), for use from compiled code. // Returns true on success, sets error and returns 2 (ERR_MAGIC) on failure. -static char -Lock_acquire_internal(PyObject *self) { +LIBRT_API_LINKAGE char +LibRTThreading_Lock_acquire_internal(PyObject *self) { int result = Lock_acquire_impl((LockObject *)self, 1); if (result < 0) { return 2; @@ -622,8 +629,8 @@ Lock_acquire_internal(PyObject *self) { // Acquire the lock with explicit blocking arg, for use from compiled code. // Returns true if acquired, false otherwise. Sets error and returns 2 // (ERR_MAGIC) on failure. -static char -Lock_acquire_blocking_internal(PyObject *self, char blocking) { +LIBRT_API_LINKAGE char +LibRTThreading_Lock_acquire_blocking_internal(PyObject *self, char blocking) { int result = Lock_acquire_impl((LockObject *)self, blocking); if (result < 0) { return 2; @@ -633,8 +640,8 @@ Lock_acquire_blocking_internal(PyObject *self, char blocking) { // Release the lock, for use from compiled code. // Returns 0 (None) on success, sets error and returns 2 (ERR_MAGIC) on failure. -static char -Lock_release_internal(PyObject *self) { +LIBRT_API_LINKAGE char +LibRTThreading_Lock_release_internal(PyObject *self) { if (Lock_release_impl((LockObject *)self) < 0) { PyErr_SetString(PyExc_RuntimeError, "cannot release an unlocked lock"); return 2; @@ -643,8 +650,8 @@ Lock_release_internal(PyObject *self) { } // Check if the lock is held, for use from compiled code. -static char -Lock_locked_internal(PyObject *self) { +LIBRT_API_LINKAGE char +LibRTThreading_Lock_locked_internal(PyObject *self) { return (char)Lock_is_locked((LockObject *)self); } @@ -652,41 +659,43 @@ static PyMethodDef librt_threading_module_methods[] = { {NULL, NULL, 0, NULL} }; -static int -threading_abi_version(void) { +LIBRT_API_LINKAGE int +LibRTThreading_ABIVersion(void) { return LIBRT_THREADING_ABI_VERSION; } -static int -threading_api_version(void) { +LIBRT_API_LINKAGE int +LibRTThreading_APIVersion(void) { return LIBRT_THREADING_API_VERSION; } static int librt_threading_module_exec(PyObject *m) { - if (PyType_Ready(&LockType) < 0) { + if (PyType_Ready(&LibRTThreading_LockType) < 0) { return -1; } - if (PyModule_AddObjectRef(m, "Lock", (PyObject *)&LockType) < 0) { + if (PyModule_AddObjectRef(m, "Lock", (PyObject *)&LibRTThreading_LockType) < 0) { return -1; } +#ifndef MYPYC_STATIC_LINKING // Export mypyc internal C API via capsule static void *threading_api[LIBRT_THREADING_API_LEN] = { - (void *)threading_abi_version, - (void *)threading_api_version, - (void *)Lock_type_internal, - (void *)Lock_new_internal, - (void *)Lock_acquire_internal, - (void *)Lock_release_internal, - (void *)Lock_locked_internal, - (void *)Lock_acquire_blocking_internal, + (void *)LibRTThreading_ABIVersion, + (void *)LibRTThreading_APIVersion, + (void *)LibRTThreading_Lock_type_internal, + (void *)LibRTThreading_Lock_new_internal, + (void *)LibRTThreading_Lock_acquire_internal, + (void *)LibRTThreading_Lock_release_internal, + (void *)LibRTThreading_Lock_locked_internal, + (void *)LibRTThreading_Lock_acquire_blocking_internal, }; PyObject *c_api_object = PyCapsule_New((void *)threading_api, "librt.threading._C_API", NULL); if (PyModule_Add(m, "_C_API", c_api_object) < 0) { return -1; } +#endif return 0; } diff --git a/mypyc/lib-rt/threading/librt_threading.h b/mypyc/lib-rt/threading/librt_threading.h index a22284f3fdbcd..117b0f1f35f46 100644 --- a/mypyc/lib-rt/threading/librt_threading.h +++ b/mypyc/lib-rt/threading/librt_threading.h @@ -7,4 +7,19 @@ #define LIBRT_THREADING_API_VERSION 1 #define LIBRT_THREADING_API_LEN 8 +#ifdef MYPYC_STATIC_LINKING +// With static linking, the API is used directly (see librt_threading_api.h) +#include +#include +extern PyTypeObject LibRTThreading_LockType; +int LibRTThreading_ABIVersion(void); +int LibRTThreading_APIVersion(void); +PyTypeObject *LibRTThreading_Lock_type_internal(void); +PyObject *LibRTThreading_Lock_new_internal(void); +char LibRTThreading_Lock_acquire_internal(PyObject *self); +char LibRTThreading_Lock_release_internal(PyObject *self); +char LibRTThreading_Lock_locked_internal(PyObject *self); +char LibRTThreading_Lock_acquire_blocking_internal(PyObject *self, char blocking); +#endif + #endif // LIBRT_THREADING_H diff --git a/mypyc/lib-rt/threading/librt_threading_api.c b/mypyc/lib-rt/threading/librt_threading_api.c index 156975d243638..b6c90276a3e94 100644 --- a/mypyc/lib-rt/threading/librt_threading_api.c +++ b/mypyc/lib-rt/threading/librt_threading_api.c @@ -1,6 +1,8 @@ #include "librt_threading_api.h" +#ifndef MYPYC_STATIC_LINKING void *LibRTThreading_API[LIBRT_THREADING_API_LEN] = {0}; +#endif int import_librt_threading(void) @@ -9,6 +11,11 @@ import_librt_threading(void) if (mod == NULL) return -1; Py_DECREF(mod); // we import just for the side effect of making the below work. +#ifdef MYPYC_STATIC_LINKING + // librt is statically linked, so the API is used directly. Importing the + // module above initializes it (e.g. its types). + return 0; +#else void **capsule = (void **)PyCapsule_Import("librt.threading._C_API", 0); if (capsule == NULL) return -1; @@ -40,4 +47,5 @@ import_librt_threading(void) // entries, so this copy is safe. memcpy(LibRTThreading_API, capsule, sizeof(LibRTThreading_API)); return 0; +#endif } diff --git a/mypyc/lib-rt/threading/librt_threading_api.h b/mypyc/lib-rt/threading/librt_threading_api.h index 5182f2f2e65b6..9caff32b2c446 100644 --- a/mypyc/lib-rt/threading/librt_threading_api.h +++ b/mypyc/lib-rt/threading/librt_threading_api.h @@ -8,6 +8,11 @@ int import_librt_threading(void); +#ifdef MYPYC_STATIC_LINKING +// librt is statically linked, so the API functions (declared in librt_threading.h) are +// called directly, and type objects are referred to directly +#define LibRTThreading_Lock_type_internal() (&LibRTThreading_LockType) +#else extern void *LibRTThreading_API[LIBRT_THREADING_API_LEN]; #define LibRTThreading_ABIVersion (*(int (*)(void)) LibRTThreading_API[0]) @@ -18,6 +23,7 @@ extern void *LibRTThreading_API[LIBRT_THREADING_API_LEN]; #define LibRTThreading_Lock_release_internal (*(char (*)(PyObject *self)) LibRTThreading_API[5]) #define LibRTThreading_Lock_locked_internal (*(char (*)(PyObject *self)) LibRTThreading_API[6]) #define LibRTThreading_Lock_acquire_blocking_internal (*(char (*)(PyObject *self, char blocking)) LibRTThreading_API[7]) +#endif static inline bool CPyLock_Check(PyObject *obj) { return Py_TYPE(obj) == LibRTThreading_Lock_type_internal(); diff --git a/mypyc/lib-rt/time/librt_time.c b/mypyc/lib-rt/time/librt_time.c index 1bbdf9ecb1515..3c73d75ba8c1f 100644 --- a/mypyc/lib-rt/time/librt_time.c +++ b/mypyc/lib-rt/time/librt_time.c @@ -2,6 +2,13 @@ #include #include #include +// With static linking, compiled code calls the API functions and refers to the +// type objects directly, so they need external linkage +#ifdef MYPYC_STATIC_LINKING +#define LIBRT_API_LINKAGE +#else +#define LIBRT_API_LINKAGE static +#endif #include "librt_time.h" #include "pythoncapi_compat.h" #include "mypyc_util.h" @@ -14,8 +21,8 @@ // Internal function that returns a C double for mypyc primitives // Returns high-precision time in seconds (like time.time()) -static double -time_time_internal(void) { +LIBRT_API_LINKAGE double +LibRTTime_time(void) { #ifdef _WIN32 // Windows: Use GetSystemTimePreciseAsFileTime for ~100ns precision FILETIME ft; @@ -65,7 +72,7 @@ time_time(PyObject *self, PyObject *const *args, size_t nargs) { return NULL; } - double result = time_time_internal(); + double result = LibRTTime_time(); if (result == CPY_FLOAT_ERROR) { return NULL; } @@ -78,29 +85,31 @@ static PyMethodDef librt_time_module_methods[] = { {NULL, NULL, 0, NULL} }; -static int -time_abi_version(void) { +LIBRT_API_LINKAGE int +LibRTTime_ABIVersion(void) { return LIBRT_TIME_ABI_VERSION; } -static int -time_api_version(void) { +LIBRT_API_LINKAGE int +LibRTTime_APIVersion(void) { return LIBRT_TIME_API_VERSION; } static int librt_time_module_exec(PyObject *m) { +#ifndef MYPYC_STATIC_LINKING // Export mypyc internal C API via capsule static void *time_api[LIBRT_TIME_API_LEN] = { - (void *)time_abi_version, - (void *)time_api_version, - (void *)time_time_internal, + (void *)LibRTTime_ABIVersion, + (void *)LibRTTime_APIVersion, + (void *)LibRTTime_time, }; PyObject *c_api_object = PyCapsule_New((void *)time_api, "librt.time._C_API", NULL); if (PyModule_Add(m, "_C_API", c_api_object) < 0) { return -1; } +#endif return 0; } diff --git a/mypyc/lib-rt/time/librt_time.h b/mypyc/lib-rt/time/librt_time.h index 09646b9f2f28e..0aee6b434e352 100644 --- a/mypyc/lib-rt/time/librt_time.h +++ b/mypyc/lib-rt/time/librt_time.h @@ -7,4 +7,13 @@ #define LIBRT_TIME_API_VERSION 1 #define LIBRT_TIME_API_LEN 3 +#ifdef MYPYC_STATIC_LINKING +// With static linking, the API is used directly (see librt_time_api.h) +#include +#include +int LibRTTime_ABIVersion(void); +int LibRTTime_APIVersion(void); +double LibRTTime_time(void); +#endif + #endif // LIBRT_TIME_H diff --git a/mypyc/lib-rt/time/librt_time_api.c b/mypyc/lib-rt/time/librt_time_api.c index 4afc78bce3975..83d9e5a7b42fd 100644 --- a/mypyc/lib-rt/time/librt_time_api.c +++ b/mypyc/lib-rt/time/librt_time_api.c @@ -1,6 +1,8 @@ #include "librt_time_api.h" +#ifndef MYPYC_STATIC_LINKING void *LibRTTime_API[LIBRT_TIME_API_LEN] = {0}; +#endif int import_librt_time(void) @@ -9,6 +11,11 @@ import_librt_time(void) if (mod == NULL) return -1; Py_DECREF(mod); // we import just for the side effect of making the below work. +#ifdef MYPYC_STATIC_LINKING + // librt is statically linked, so the API is used directly. Importing the + // module above initializes it (e.g. its types). + return 0; +#else void **capsule = (void **)PyCapsule_Import("librt.time._C_API", 0); if (capsule == NULL) return -1; @@ -40,4 +47,5 @@ import_librt_time(void) // entries, so this copy is safe. memcpy(LibRTTime_API, capsule, sizeof(LibRTTime_API)); return 0; +#endif } diff --git a/mypyc/lib-rt/time/librt_time_api.h b/mypyc/lib-rt/time/librt_time_api.h index fd4f4d036d390..c2bb0072b4b3c 100644 --- a/mypyc/lib-rt/time/librt_time_api.h +++ b/mypyc/lib-rt/time/librt_time_api.h @@ -6,10 +6,15 @@ int import_librt_time(void); +#ifdef MYPYC_STATIC_LINKING +// librt is statically linked, so the API functions (declared in librt_time.h) are +// called directly, and type objects are referred to directly +#else extern void *LibRTTime_API[LIBRT_TIME_API_LEN]; #define LibRTTime_ABIVersion (*(int (*)(void)) LibRTTime_API[0]) #define LibRTTime_APIVersion (*(int (*)(void)) LibRTTime_API[1]) #define LibRTTime_time (*(double (*)(void)) LibRTTime_API[2]) +#endif #endif // LIBRT_TIME_API_H diff --git a/mypyc/lib-rt/vecs/librt_vecs.c b/mypyc/lib-rt/vecs/librt_vecs.c index 67c2148005b25..07b4e4f3014dd 100644 --- a/mypyc/lib-rt/vecs/librt_vecs.c +++ b/mypyc/lib-rt/vecs/librt_vecs.c @@ -951,6 +951,7 @@ static PyObject *vec_extend(PyObject *self, PyObject *args) } } +#ifndef MYPYC_STATIC_LINKING // Return the base VecType for isinstance checks static PyTypeObject *get_vec_type(void) { return &VecType; @@ -979,6 +980,7 @@ static VecCapsule Capsule = { &Vec_BoolAPI, get_vec_type, }; +#endif static PyMethodDef VecsMethods[] = { {"append", vec_append, METH_VARARGS, "Append a value to the end of a vec"}, @@ -1091,6 +1093,7 @@ librt_vecs_module_exec(PyObject *m) return -1; } +#ifndef MYPYC_STATIC_LINKING PyObject *c_api = PyCapsule_New(&Capsule, "librt.vecs._C_API", NULL); if (c_api == NULL) return -1; @@ -1100,6 +1103,7 @@ librt_vecs_module_exec(PyObject *m) Py_DECREF(&VecType); return -1; } +#endif Py_DECREF(ext); return 0; diff --git a/mypyc/lib-rt/vecs/librt_vecs.h b/mypyc/lib-rt/vecs/librt_vecs.h index 73a16225aee26..9519258156e70 100644 --- a/mypyc/lib-rt/vecs/librt_vecs.h +++ b/mypyc/lib-rt/vecs/librt_vecs.h @@ -507,6 +507,42 @@ typedef struct _VecNestedAPI { VecNested (*extend_vec)(VecNested, VecNested); } VecNestedAPI; +// Initializers for the API structs. These are used by librt.vecs, and also by +// compiled code when librt is statically linked (see librt_vecs_api.h). +#define VEC_API_INIT(prefix) VEC_API_INIT_(prefix) +#define VEC_API_INIT_(prefix) { \ + &prefix##Type, \ + &prefix##BufType, \ + prefix##_New, \ + prefix##_Box, \ + prefix##_Unbox, \ + prefix##_ConvertFromNested, \ + prefix##_Append, \ + prefix##_Pop, \ + prefix##_Remove, \ + prefix##_Slice, \ + prefix##_FromIterable, \ + prefix##_Extend, \ + prefix##_ExtendVec, \ + prefix##_ToList, \ + prefix##_ToTuple, \ +} +#define VEC_NESTED_API_INIT_(prefix) { \ + &prefix##Type, \ + &prefix##BufType, \ + prefix##_New, \ + prefix##_Box, \ + prefix##_Unbox, \ + prefix##_ConvertFromNested, \ + prefix##_Append, \ + prefix##_Pop, \ + prefix##_Remove, \ + prefix##_Slice, \ + prefix##_Extend, \ + prefix##_ExtendVec, \ +} +#define VEC_NESTED_API_INIT VEC_NESTED_API_INIT_(VecNested) + typedef struct { int (*abi_version)(void); int (*api_version)(void); @@ -995,4 +1031,110 @@ int Vec_GenericRemove(Py_ssize_t *len, PyObject **items, PyObject *item); PyObject *Vec_GenericPopWrapper(Py_ssize_t *len, PyObject **items, PyObject *args); PyObject *Vec_GenericPop(Py_ssize_t *len, PyObject **items, Py_ssize_t index); +#ifdef MYPYC_STATIC_LINKING +// With static linking, compiled code calls the API directly (see librt_vecs_api.h) +extern PyTypeObject VecType; +VecI64 VecI64_New(Py_ssize_t, Py_ssize_t); +PyObject *VecI64_Box(VecI64); +VecI64 VecI64_Unbox(PyObject *); +VecI64 VecI64_ConvertFromNested(VecNestedBufItem); +VecI64 VecI64_Append(VecI64, int64_t); +VecI64PopResult VecI64_Pop(VecI64, Py_ssize_t); +VecI64 VecI64_Remove(VecI64, int64_t); +VecI64 VecI64_Slice(VecI64, int64_t, int64_t); +VecI64 VecI64_FromIterable(PyObject *, int64_t); +VecI64 VecI64_Extend(VecI64, PyObject *); +VecI64 VecI64_ExtendVec(VecI64, VecI64); +PyObject *VecI64_ToList(VecI64); +PyObject *VecI64_ToTuple(VecI64); +VecI32 VecI32_New(Py_ssize_t, Py_ssize_t); +PyObject *VecI32_Box(VecI32); +VecI32 VecI32_Unbox(PyObject *); +VecI32 VecI32_ConvertFromNested(VecNestedBufItem); +VecI32 VecI32_Append(VecI32, int32_t); +VecI32PopResult VecI32_Pop(VecI32, Py_ssize_t); +VecI32 VecI32_Remove(VecI32, int32_t); +VecI32 VecI32_Slice(VecI32, int64_t, int64_t); +VecI32 VecI32_FromIterable(PyObject *, int64_t); +VecI32 VecI32_Extend(VecI32, PyObject *); +VecI32 VecI32_ExtendVec(VecI32, VecI32); +PyObject *VecI32_ToList(VecI32); +PyObject *VecI32_ToTuple(VecI32); +VecI16 VecI16_New(Py_ssize_t, Py_ssize_t); +PyObject *VecI16_Box(VecI16); +VecI16 VecI16_Unbox(PyObject *); +VecI16 VecI16_ConvertFromNested(VecNestedBufItem); +VecI16 VecI16_Append(VecI16, int16_t); +VecI16PopResult VecI16_Pop(VecI16, Py_ssize_t); +VecI16 VecI16_Remove(VecI16, int16_t); +VecI16 VecI16_Slice(VecI16, int64_t, int64_t); +VecI16 VecI16_FromIterable(PyObject *, int64_t); +VecI16 VecI16_Extend(VecI16, PyObject *); +VecI16 VecI16_ExtendVec(VecI16, VecI16); +PyObject *VecI16_ToList(VecI16); +PyObject *VecI16_ToTuple(VecI16); +VecU8 VecU8_New(Py_ssize_t, Py_ssize_t); +PyObject *VecU8_Box(VecU8); +VecU8 VecU8_Unbox(PyObject *); +VecU8 VecU8_ConvertFromNested(VecNestedBufItem); +VecU8 VecU8_Append(VecU8, uint8_t); +VecU8PopResult VecU8_Pop(VecU8, Py_ssize_t); +VecU8 VecU8_Remove(VecU8, uint8_t); +VecU8 VecU8_Slice(VecU8, int64_t, int64_t); +VecU8 VecU8_FromIterable(PyObject *, int64_t); +VecU8 VecU8_Extend(VecU8, PyObject *); +VecU8 VecU8_ExtendVec(VecU8, VecU8); +PyObject *VecU8_ToList(VecU8); +PyObject *VecU8_ToTuple(VecU8); +VecFloat VecFloat_New(Py_ssize_t, Py_ssize_t); +PyObject *VecFloat_Box(VecFloat); +VecFloat VecFloat_Unbox(PyObject *); +VecFloat VecFloat_ConvertFromNested(VecNestedBufItem); +VecFloat VecFloat_Append(VecFloat, double); +VecFloatPopResult VecFloat_Pop(VecFloat, Py_ssize_t); +VecFloat VecFloat_Remove(VecFloat, double); +VecFloat VecFloat_Slice(VecFloat, int64_t, int64_t); +VecFloat VecFloat_FromIterable(PyObject *, int64_t); +VecFloat VecFloat_Extend(VecFloat, PyObject *); +VecFloat VecFloat_ExtendVec(VecFloat, VecFloat); +PyObject *VecFloat_ToList(VecFloat); +PyObject *VecFloat_ToTuple(VecFloat); +VecBool VecBool_New(Py_ssize_t, Py_ssize_t); +PyObject *VecBool_Box(VecBool); +VecBool VecBool_Unbox(PyObject *); +VecBool VecBool_ConvertFromNested(VecNestedBufItem); +VecBool VecBool_Append(VecBool, char); +VecBoolPopResult VecBool_Pop(VecBool, Py_ssize_t); +VecBool VecBool_Remove(VecBool, char); +VecBool VecBool_Slice(VecBool, int64_t, int64_t); +VecBool VecBool_FromIterable(PyObject *, int64_t); +VecBool VecBool_Extend(VecBool, PyObject *); +VecBool VecBool_ExtendVec(VecBool, VecBool); +PyObject *VecBool_ToList(VecBool); +PyObject *VecBool_ToTuple(VecBool); +VecT VecT_New(Py_ssize_t, Py_ssize_t, size_t); +PyObject *VecT_Box(VecT, size_t); +VecT VecT_Unbox(PyObject *, size_t); +VecT VecT_ConvertFromNested(VecNestedBufItem); +VecT VecT_Append(VecT, PyObject *, size_t); +VecTPopResult VecT_Pop(VecT, Py_ssize_t); +VecT VecT_Remove(VecT, PyObject *); +VecT VecT_Slice(VecT, int64_t, int64_t); +VecT VecT_FromIterable(size_t, PyObject *, int64_t); +VecT VecT_Extend(VecT, PyObject *, size_t); +VecT VecT_ExtendVec(VecT, VecT, size_t); +PyObject *VecT_ToList(VecT); +PyObject *VecT_ToTuple(VecT); +VecNested VecNested_New(Py_ssize_t, Py_ssize_t, size_t, size_t depth); +PyObject *VecNested_Box(VecNested); +VecNested VecNested_Unbox(PyObject *, size_t, size_t depth); +VecNested VecNested_ConvertFromNested(VecNestedBufItem); +VecNested VecNested_Append(VecNested, VecNestedBufItem); +VecNestedPopResult VecNested_Pop(VecNested, Py_ssize_t); +VecNested VecNested_Remove(VecNested, VecNestedBufItem); +VecNested VecNested_Slice(VecNested, int64_t, int64_t); +VecNested VecNested_Extend(VecNested, PyObject *); +VecNested VecNested_ExtendVec(VecNested, VecNested); +#endif + #endif // VEC_H_INCL diff --git a/mypyc/lib-rt/vecs/librt_vecs_api.c b/mypyc/lib-rt/vecs/librt_vecs_api.c index 8c45874f1bfd2..18359bae5c488 100644 --- a/mypyc/lib-rt/vecs/librt_vecs_api.c +++ b/mypyc/lib-rt/vecs/librt_vecs_api.c @@ -1,5 +1,6 @@ #include "librt_vecs_api.h" +#ifndef MYPYC_STATIC_LINKING VecCapsule *VecApi = NULL; VecI64API VecI64Api = {0}; VecI32API VecI32Api = {0}; @@ -9,6 +10,7 @@ VecFloatAPI VecFloatApi = {0}; VecBoolAPI VecBoolApi = {0}; VecTAPI VecTApi = {0}; VecNestedAPI VecNestedApi = {0}; +#endif int import_librt_vecs(void) @@ -17,6 +19,11 @@ import_librt_vecs(void) if (mod == NULL) return -1; Py_DECREF(mod); // we import just for the side effect of making the below work. +#ifdef MYPYC_STATIC_LINKING + // librt is statically linked, so the API is called directly. Importing the + // module above initializes it (e.g. its types). + return 0; +#else VecCapsule *capsule = PyCapsule_Import("librt.vecs._C_API", 0); if (!capsule) return -1; @@ -48,4 +55,5 @@ import_librt_vecs(void) VecTApi = *VecApi->t; VecNestedApi = *VecApi->nested; return 0; +#endif } diff --git a/mypyc/lib-rt/vecs/librt_vecs_api.h b/mypyc/lib-rt/vecs/librt_vecs_api.h index 6f42c0b7251d0..65a943656ae4b 100644 --- a/mypyc/lib-rt/vecs/librt_vecs_api.h +++ b/mypyc/lib-rt/vecs/librt_vecs_api.h @@ -10,6 +10,18 @@ int import_librt_vecs(void); +#ifdef MYPYC_STATIC_LINKING +// librt is statically linked. These have the same contents as the API structs in +// librt.vecs, but since they are constants, calls are direct (and can be inlined). +static const VecI64API VecI64Api = VEC_API_INIT(VecI64); +static const VecI32API VecI32Api = VEC_API_INIT(VecI32); +static const VecI16API VecI16Api = VEC_API_INIT(VecI16); +static const VecU8API VecU8Api = VEC_API_INIT(VecU8); +static const VecFloatAPI VecFloatApi = VEC_API_INIT(VecFloat); +static const VecBoolAPI VecBoolApi = VEC_API_INIT(VecBool); +static const VecTAPI VecTApi = VEC_API_INIT(VecT); +static const VecNestedAPI VecNestedApi = VEC_NESTED_API_INIT; +#else // Global API pointers initialized by import_librt_vecs() extern VecCapsule *VecApi; extern VecI64API VecI64Api; @@ -20,5 +32,6 @@ extern VecFloatAPI VecFloatApi; extern VecBoolAPI VecBoolApi; extern VecTAPI VecTApi; extern VecNestedAPI VecNestedApi; +#endif #endif // LIBRT_VECS_API_H diff --git a/mypyc/lib-rt/vecs/vec_nested.c b/mypyc/lib-rt/vecs/vec_nested.c index c2f04ba8566e9..b4a260b0b1b0a 100644 --- a/mypyc/lib-rt/vecs/vec_nested.c +++ b/mypyc/lib-rt/vecs/vec_nested.c @@ -749,17 +749,4 @@ PyObject *VecNested_FromIterable(size_t item_type, size_t depth, PyObject *itera return VecNested_Box(v); } -VecNestedAPI Vec_NestedAPI = { - &VecNestedType, - &VecNestedBufType, - VecNested_New, - VecNested_Box, - VecNested_Unbox, - VecNested_ConvertFromNested, - VecNested_Append, - VecNested_Pop, - VecNested_Remove, - VecNested_Slice, - VecNested_Extend, - VecNested_ExtendVec, -}; +VecNestedAPI Vec_NestedAPI = VEC_NESTED_API_INIT; diff --git a/mypyc/lib-rt/vecs/vec_t.c b/mypyc/lib-rt/vecs/vec_t.c index 5c0865ed7772e..d667fc5e6cb3c 100644 --- a/mypyc/lib-rt/vecs/vec_t.c +++ b/mypyc/lib-rt/vecs/vec_t.c @@ -850,20 +850,4 @@ VecT VecT_FromIterable(size_t item_type, PyObject *iterable, int64_t cap) { return v; } -VecTAPI Vec_TAPI = { - &VecTType, - &VecTBufType, - VecT_New, - VecT_Box, - VecT_Unbox, - VecT_ConvertFromNested, - VecT_Append, - VecT_Pop, - VecT_Remove, - VecT_Slice, - VecT_FromIterable, - VecT_Extend, - VecT_ExtendVec, - VecT_ToList, - VecT_ToTuple, -}; +VecTAPI Vec_TAPI = VEC_API_INIT(VecT); diff --git a/mypyc/lib-rt/vecs/vec_template.c b/mypyc/lib-rt/vecs/vec_template.c index a6b09ba2a7b29..6c9c3c1fa7790 100644 --- a/mypyc/lib-rt/vecs/vec_template.c +++ b/mypyc/lib-rt/vecs/vec_template.c @@ -822,23 +822,7 @@ PyTypeObject VEC_TYPE = { .tp_methods = vec_methods, }; -NAME(API) FEATURES = { - &VEC_TYPE, - &BUF_TYPE, - FUNC(New), - FUNC(Box), - FUNC(Unbox), - FUNC(ConvertFromNested), - FUNC(Append), - FUNC(Pop), - FUNC(Remove), - FUNC(Slice), - FUNC(FromIterable), - FUNC(Extend), - FUNC(ExtendVec), - FUNC(ToList), - FUNC(ToTuple), -}; +NAME(API) FEATURES = VEC_API_INIT(VEC); #undef VEC_BUF #undef VEC_CAP diff --git a/mypyc/lib-rt/vecs_extra_ops.h b/mypyc/lib-rt/vecs_extra_ops.h index 4c31d7cd34488..4604d8f304097 100644 --- a/mypyc/lib-rt/vecs_extra_ops.h +++ b/mypyc/lib-rt/vecs_extra_ops.h @@ -5,7 +5,11 @@ // Check if obj is an instance of vec (any vec type) static inline int CPyVec_Check(PyObject *obj) { +#ifdef MYPYC_STATIC_LINKING + return PyObject_TypeCheck(obj, &VecType); +#else return PyObject_TypeCheck(obj, VecApi->get_vec_type()); +#endif } static inline PyObject *CPyVecU8_ToBytes(VecU8 v) { diff --git a/mypyc/options.py b/mypyc/options.py index 8eb33b2282567..5afeaf54e149d 100644 --- a/mypyc/options.py +++ b/mypyc/options.py @@ -20,6 +20,7 @@ def __init__( depends_on_librt_internal: bool = False, experimental_features: bool = False, strict_traceback_checks: bool = False, + static_linking: bool = False, ) -> None: self.strip_asserts = strip_asserts self.multi_file = multi_file @@ -27,8 +28,13 @@ def __init__( self.separate = separate self.global_opts = not separate self.target_dir = target_dir or "build" + if static_linking and include_runtime_files: + # Each group would have a copy of the runtime, which can't be linked together + raise ValueError("include_runtime_files is not supported with static_linking") self.include_runtime_files = ( - include_runtime_files if include_runtime_files is not None else not multi_file + include_runtime_files + if include_runtime_files is not None + else not multi_file and not static_linking ) # The target Python C API version. Overriding this is mostly # useful in IR tests, since there's no guarantee that @@ -73,3 +79,13 @@ def __init__( # tests to make sure that no new code which leads to incorrect tracebacks is # added. self.strict_traceback_checks = strict_traceback_checks + # Generate code for statically linking the compiled extensions together, along + # with librt. No capsules are used: other groups, shim modules and librt are + # accessed directly through linked C symbols instead. This is only useful when + # using an advanced build system (e.g. Bazel or Buck2) that statically links + # extensions together, and it's only supported via mypyc_build(). The build + # system is responsible for compiling and linking everything, including librt + # and the lib-rt runtime, and must define MYPYC_STATIC_LINKING when compiling + # all the C (generated code, lib-rt and librt). Shims must be generated from + # the module_shim*.tmpl templates in lib-rt. + self.static_linking = static_linking diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py index 3f4520f0a954b..94ed1089dc9e6 100644 --- a/mypyc/primitives/misc_ops.py +++ b/mypyc/primitives/misc_ops.py @@ -461,7 +461,7 @@ name="librt.internal.ReadBuffer", arg_types=[bytes_rprimitive], return_type=read_buffer_rprimitive, - c_function_name="ReadBuffer_internal", + c_function_name="NativeInternal_ReadBuffer_internal", error_kind=ERR_MAGIC, ) @@ -470,7 +470,7 @@ name="librt.internal.WriteBuffer", arg_types=[], return_type=write_buffer_rprimitive, - c_function_name="WriteBuffer_internal", + c_function_name="NativeInternal_WriteBuffer_internal", error_kind=ERR_MAGIC, ) @@ -478,7 +478,7 @@ name="getvalue", arg_types=[write_buffer_rprimitive], return_type=bytes_rprimitive, - c_function_name="WriteBuffer_getvalue_internal", + c_function_name="NativeInternal_WriteBuffer_getvalue_internal", error_kind=ERR_MAGIC, ) @@ -486,7 +486,7 @@ name="librt.internal.write_bool", arg_types=[object_rprimitive, bool_rprimitive], return_type=none_rprimitive, - c_function_name="write_bool_internal", + c_function_name="NativeInternal_write_bool_internal", error_kind=ERR_MAGIC, ) @@ -494,7 +494,7 @@ name="librt.internal.read_bool", arg_types=[object_rprimitive], return_type=bool_rprimitive, - c_function_name="read_bool_internal", + c_function_name="NativeInternal_read_bool_internal", error_kind=ERR_MAGIC, ) @@ -502,7 +502,7 @@ name="librt.internal.write_str", arg_types=[object_rprimitive, str_rprimitive], return_type=none_rprimitive, - c_function_name="write_str_internal", + c_function_name="NativeInternal_write_str_internal", error_kind=ERR_MAGIC, ) @@ -510,7 +510,7 @@ name="librt.internal.read_str", arg_types=[object_rprimitive], return_type=str_rprimitive, - c_function_name="read_str_internal", + c_function_name="NativeInternal_read_str_internal", error_kind=ERR_MAGIC, ) @@ -518,7 +518,7 @@ name="librt.internal.write_bytes", arg_types=[object_rprimitive, bytes_rprimitive], return_type=none_rprimitive, - c_function_name="write_bytes_internal", + c_function_name="NativeInternal_write_bytes_internal", error_kind=ERR_MAGIC, ) @@ -526,7 +526,7 @@ name="librt.internal.read_bytes", arg_types=[object_rprimitive], return_type=bytes_rprimitive, - c_function_name="read_bytes_internal", + c_function_name="NativeInternal_read_bytes_internal", error_kind=ERR_MAGIC, ) @@ -534,7 +534,7 @@ name="librt.internal.write_float", arg_types=[object_rprimitive, float_rprimitive], return_type=none_rprimitive, - c_function_name="write_float_internal", + c_function_name="NativeInternal_write_float_internal", error_kind=ERR_MAGIC, ) @@ -542,7 +542,7 @@ name="librt.internal.read_float", arg_types=[object_rprimitive], return_type=float_rprimitive, - c_function_name="read_float_internal", + c_function_name="NativeInternal_read_float_internal", error_kind=ERR_MAGIC_OVERLAPPING, ) @@ -550,7 +550,7 @@ name="librt.internal.write_int", arg_types=[object_rprimitive, int_rprimitive], return_type=none_rprimitive, - c_function_name="write_int_internal", + c_function_name="NativeInternal_write_int_internal", error_kind=ERR_MAGIC, ) @@ -558,7 +558,7 @@ name="librt.internal.read_int", arg_types=[object_rprimitive], return_type=int_rprimitive, - c_function_name="read_int_internal", + c_function_name="NativeInternal_read_int_internal", error_kind=ERR_MAGIC, ) @@ -566,7 +566,7 @@ name="librt.internal.write_tag", arg_types=[object_rprimitive, uint8_rprimitive], return_type=none_rprimitive, - c_function_name="write_tag_internal", + c_function_name="NativeInternal_write_tag_internal", error_kind=ERR_MAGIC, ) @@ -574,7 +574,7 @@ name="librt.internal.read_tag", arg_types=[object_rprimitive], return_type=uint8_rprimitive, - c_function_name="read_tag_internal", + c_function_name="NativeInternal_read_tag_internal", error_kind=ERR_MAGIC_OVERLAPPING, ) @@ -582,7 +582,7 @@ name="librt.internal.cache_version", arg_types=[], return_type=uint8_rprimitive, - c_function_name="cache_version_internal", + c_function_name="NativeInternal_cache_version_internal", error_kind=ERR_NEVER, ) @@ -590,7 +590,7 @@ name="librt.internal.extract_symbol", arg_types=[object_rprimitive], return_type=bytes_rprimitive, - c_function_name="extract_symbol_internal", + c_function_name="NativeInternal_extract_symbol_internal", error_kind=ERR_MAGIC, ) diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index a08bfb61646e9..27090d2c4e71f 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -1568,32 +1568,32 @@ def foo(): r15, t :: int r16, u, r17, v :: u8 L0: - r0 = WriteBuffer_internal() + r0 = NativeInternal_WriteBuffer_internal() b = r0 r1 = 'foo' - r2 = write_str_internal(b, r1) + r2 = NativeInternal_write_str_internal(b, r1) r3 = b'bar' - r4 = write_bytes_internal(b, r3) - r5 = write_bool_internal(b, 1) - r6 = write_float_internal(b, 0.1) - r7 = write_int_internal(b, 2) - r8 = write_tag_internal(b, 1) - r9 = WriteBuffer_getvalue_internal(b) - r10 = ReadBuffer_internal(r9) + r4 = NativeInternal_write_bytes_internal(b, r3) + r5 = NativeInternal_write_bool_internal(b, 1) + r6 = NativeInternal_write_float_internal(b, 0.1) + r7 = NativeInternal_write_int_internal(b, 2) + r8 = NativeInternal_write_tag_internal(b, 1) + r9 = NativeInternal_WriteBuffer_getvalue_internal(b) + r10 = NativeInternal_ReadBuffer_internal(r9) rb = r10 - r11 = read_str_internal(rb) + r11 = NativeInternal_read_str_internal(rb) x = r11 - r12 = read_bytes_internal(rb) + r12 = NativeInternal_read_bytes_internal(rb) xb = r12 - r13 = read_bool_internal(rb) + r13 = NativeInternal_read_bool_internal(rb) y = r13 - r14 = read_float_internal(rb) + r14 = NativeInternal_read_float_internal(rb) z = r14 - r15 = read_int_internal(rb) + r15 = NativeInternal_read_int_internal(rb) t = r15 - r16 = read_tag_internal(rb) + r16 = NativeInternal_read_tag_internal(rb) u = r16 - r17 = cache_version_internal() + r17 = NativeInternal_cache_version_internal() v = r17 return 1