Skip to content

Commit 43be064

Browse files
committed
Use trampolines to link numba symbols to openmp target cpu modules
- Avoids relocation errors compared to absolute symbols with --defsym
1 parent 6ffd3e1 commit 43be064

1 file changed

Lines changed: 90 additions & 30 deletions

File tree

src/numba/openmp/__init__.py

Lines changed: 90 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
excinfo_t,
7575
CPUCallConv,
7676
)
77-
from functools import cached_property
77+
from functools import cached_property, lru_cache
7878
from numba.core.datamodel.registry import register_default as model_register
7979
from numba.core.datamodel.registry import default_manager as model_manager
8080
from numba.core.datamodel.models import OpaqueModel
@@ -113,57 +113,117 @@
113113

114114

115115
def link_shared_library(obj_path, out_path):
116+
# Generate trampolines for numba/NRT symbols. We use trampolines to link the
117+
# absolute symbol addresses from numba to the self-contained shared library
118+
# for the OpenMP target CPU module.
119+
# TODO: ask numba upstream to provide a static library with these symbols.
120+
@lru_cache
121+
def generate_trampolines():
122+
from numba import _helperlib
123+
from numba.core.runtime import _nrt_python as _nrt
124+
125+
# Signature mapping for numba/NRT functions. Add more as needed.
126+
SIGNATURES = {
127+
# GIL management
128+
"numba_gil_ensure": ("void", []),
129+
"numba_gil_release": ("void", []),
130+
# Memory allocation
131+
"NRT_MemInfo_alloc": ("void*", ["size_t"]),
132+
"NRT_MemInfo_alloc_safe": ("void*", ["size_t"]),
133+
"NRT_MemInfo_alloc_aligned": ("void*", ["size_t", "size_t"]),
134+
"NRT_MemInfo_alloc_safe_aligned": ("void*", ["size_t", "size_t"]),
135+
"NRT_MemInfo_free": ("void", ["void*"]),
136+
}
137+
138+
trampoline_c = """#include <stddef.h>"""
139+
140+
symbols = []
141+
# Process _helperlib symbols
142+
for py_name in _helperlib.c_helpers:
143+
c_name = "numba_" + py_name
144+
c_address = _helperlib.c_helpers[py_name]
145+
146+
if c_name in SIGNATURES:
147+
ret_type, params = SIGNATURES[c_name]
148+
symbols.append((c_name, c_address, ret_type, params))
149+
150+
# Process _nrt symbols
151+
for py_name in _nrt.c_helpers:
152+
if py_name.startswith("_"):
153+
c_name = py_name
154+
else:
155+
c_name = "NRT_" + py_name
156+
c_address = _nrt.c_helpers[py_name]
157+
158+
if c_name in SIGNATURES:
159+
ret_type, params = SIGNATURES[c_name]
160+
symbols.append((c_name, c_address, ret_type, params))
161+
162+
# Generate trampolines
163+
for c_name, c_address, ret_type, params in sorted(symbols):
164+
# Build parameter list
165+
if not params:
166+
param_list = "void"
167+
arg_list = ""
168+
else:
169+
param_list = ", ".join(
170+
f"{ptype} arg{i}" for i, ptype in enumerate(params)
171+
)
172+
arg_list = ", ".join(f"arg{i}" for i in range(len(params)))
173+
174+
# Build function pointer type
175+
func_ptr_type = f"{ret_type} (*)({', '.join(params) if params else 'void'})"
176+
177+
# Generate the trampoline
178+
trampoline_c += f"""
179+
__attribute__((visibility("default")))
180+
{ret_type} {c_name}({param_list}) {{
181+
{"" if ret_type == "void" else "return "}(({func_ptr_type})0x{c_address:x})({arg_list});
182+
}}
183+
"""
184+
185+
return trampoline_c
186+
116187
"""
117188
Produce a shared library from a single object file and link numba C symbols.
118189
Uses distutils' compiler.
119190
"""
120191
obj_path = str(Path(obj_path))
121192
out_path = str(Path(out_path))
122193

123-
from numba import _helperlib
124-
from numba.core.runtime import _nrt_python as _nrt
125-
126-
# Prepare list of Numba C symbols to link against.
127-
symbols = []
128-
for py_name in _helperlib.c_helpers:
129-
c_name = "numba_" + py_name
130-
c_address = _helperlib.c_helpers[py_name]
131-
symbols.append(f"-Wl,--defsym={c_name}=0x{c_address:x}")
132-
133-
for py_name in _nrt.c_helpers:
134-
if py_name.startswith("_"):
135-
# internal API
136-
c_name = py_name
137-
else:
138-
c_name = "NRT_" + py_name
139-
c_address = _nrt.c_helpers[py_name]
140-
symbols.append(f"-Wl,--defsym={c_name}=0x{c_address:x}")
194+
trampoline_code = generate_trampolines()
195+
fd, trampoline_c = tempfile.mkstemp(".c")
196+
os.close(fd)
197+
with open(trampoline_c, "w") as f:
198+
f.write(trampoline_code)
141199

142200
cc = ccompiler.new_compiler()
143201
sysconfig.customize_compiler(cc)
144-
145202
extra_pre = []
146203
extra_post = []
147204

148-
if sys.platform.startswith("linux") or sys.platform.startswith("freebsd"):
149-
# Add numba symbols to the link.
150-
extra_post += symbols
151-
elif sys.platform == "darwin":
152-
raise NotImplementedError("link_shared_library not implemented on Windows")
153-
elif os.name == "nt":
154-
raise NotImplementedError("link_shared_library not implemented on Windows")
155-
else:
156-
raise RuntimeError(f"Unsupported platform: {sys.platform}")
205+
try:
206+
trampoline_o = cc.compile([trampoline_c])
207+
except Exception as e:
208+
raise RuntimeError(
209+
f"Compilation failed for trampolines in {trampoline_c}"
210+
) from e
211+
finally:
212+
os.remove(trampoline_c)
157213

214+
objs = [obj_path] + trampoline_o
158215
try:
159216
cc.link_shared_object(
160-
objects=[obj_path],
217+
objects=objs,
161218
output_filename=out_path,
162219
extra_preargs=extra_pre,
163220
extra_postargs=extra_post,
164221
)
165222
except Exception as e:
166223
raise RuntimeError(f"Link failed for {out_path}") from e
224+
finally:
225+
for file_o in trampoline_o:
226+
os.remove(file_o)
167227

168228

169229
###

0 commit comments

Comments
 (0)