@@ -29,10 +29,85 @@ namespace Internal {
2929
3030using std::string;
3131
32+ // On 32-bit targets, LLVM JIT code may reference libgcc helper functions
33+ // that dlsym can't always resolve (e.g. under QEMU, or when the host
34+ // compiler inlined them). We provide wrappers and register them in a
35+ // builtins map that getSymbolAddress consults as a fallback.
36+
3237#if defined(__GNUC__) && defined(__i386__)
3338extern " C" unsigned long __udivdi3 (unsigned long a, unsigned long b);
39+
40+ static const std::map<std::string, uint64_t > i386_builtins = {
41+ {" __udivdi3" , (uintptr_t )&__udivdi3},
42+ };
43+ #endif
44+
45+ // On arm-32, LLVM generates calls to __sync_* libgcc functions for atomic
46+ // operations. We provide wrappers using GCC's __sync_* builtins, guarded
47+ // by the __GCC_HAVE_SYNC_COMPARE_AND_SWAP_N predefined macros.
48+ #if defined(__arm__)
49+
50+ static void halide__sync_synchronize () {
51+ __sync_synchronize ();
52+ }
53+
54+ #ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_1
55+ static uint8_t halide__sync_lock_test_and_set_1 (volatile uint8_t *ptr, uint8_t val) {
56+ return __sync_lock_test_and_set (ptr, val);
57+ }
58+ #endif
59+
60+ #ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4
61+ static uint32_t halide__sync_fetch_and_add_4 (volatile uint32_t *ptr, uint32_t val) {
62+ return __sync_fetch_and_add (ptr, val);
63+ }
64+ static uint32_t halide__sync_fetch_and_sub_4 (volatile uint32_t *ptr, uint32_t val) {
65+ return __sync_fetch_and_sub (ptr, val);
66+ }
67+ static uint32_t halide__sync_fetch_and_or_4 (volatile uint32_t *ptr, uint32_t val) {
68+ return __sync_fetch_and_or (ptr, val);
69+ }
70+ static uint32_t halide__sync_fetch_and_and_4 (volatile uint32_t *ptr, uint32_t val) {
71+ return __sync_fetch_and_and (ptr, val);
72+ }
73+ static uint32_t halide__sync_val_compare_and_swap_4 (volatile uint32_t *ptr, uint32_t oldval, uint32_t newval) {
74+ return __sync_val_compare_and_swap (ptr, oldval, newval);
75+ }
3476#endif
3577
78+ #ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8
79+ static uint64_t halide__sync_fetch_and_add_8 (volatile uint64_t *ptr, uint64_t val) {
80+ return __sync_fetch_and_add (ptr, val);
81+ }
82+ static uint64_t halide__sync_fetch_and_sub_8 (volatile uint64_t *ptr, uint64_t val) {
83+ return __sync_fetch_and_sub (ptr, val);
84+ }
85+ static uint64_t halide__sync_val_compare_and_swap_8 (volatile uint64_t *ptr, uint64_t oldval, uint64_t newval) {
86+ return __sync_val_compare_and_swap (ptr, oldval, newval);
87+ }
88+ #endif
89+
90+ static const std::map<std::string, uint64_t > arm32_sync_builtins = {
91+ {" __sync_synchronize" , (uintptr_t )&halide__sync_synchronize},
92+ #ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_1
93+ {" __sync_lock_test_and_set_1" , (uintptr_t )&halide__sync_lock_test_and_set_1},
94+ #endif
95+ #ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4
96+ {" __sync_fetch_and_add_4" , (uintptr_t )&halide__sync_fetch_and_add_4},
97+ {" __sync_fetch_and_sub_4" , (uintptr_t )&halide__sync_fetch_and_sub_4},
98+ {" __sync_fetch_and_or_4" , (uintptr_t )&halide__sync_fetch_and_or_4},
99+ {" __sync_fetch_and_and_4" , (uintptr_t )&halide__sync_fetch_and_and_4},
100+ {" __sync_val_compare_and_swap_4" , (uintptr_t )&halide__sync_val_compare_and_swap_4},
101+ #endif
102+ #ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8
103+ {" __sync_fetch_and_add_8" , (uintptr_t )&halide__sync_fetch_and_add_8},
104+ {" __sync_fetch_and_sub_8" , (uintptr_t )&halide__sync_fetch_and_sub_8},
105+ {" __sync_val_compare_and_swap_8" , (uintptr_t )&halide__sync_val_compare_and_swap_8},
106+ #endif
107+ };
108+
109+ #endif // defined(__arm__)
110+
36111#ifdef _WIN32
37112void *get_symbol_address (const char *s) {
38113 return (void *)GetProcAddress (GetModuleHandle (nullptr ), s);
@@ -247,25 +322,6 @@ class HalideJITMemoryManager : public SectionMemoryManager {
247322 }
248323 }
249324 uint64_t result = SectionMemoryManager::getSymbolAddress (name);
250- #if defined(__GNUC__) && defined(__i386__)
251- // This is a workaround for an odd corner case (cross-compiling + testing
252- // Python bindings x86-32 on an x86-64 system): __udivdi3 is a helper function
253- // that GCC uses to do u64/u64 division on 32-bit systems; it's usually included
254- // by the linker on these systems as needed. When we JIT, LLVM will include references
255- // to this call; MCJIT fixes up these references by doing (roughly) dlopen(NULL)
256- // to look up the symbol. For normal JIT tests, this works fine, as dlopen(NULL)
257- // finds the test executable, which has the right lookups to locate it inside libHalide.so.
258- // If, however, we are running a JIT-via-Python test, dlopen(NULL) returns the
259- // CPython executable... which apparently *doesn't* include this as an exported
260- // function, so the lookup fails and crashiness ensues. So our workaround here is
261- // a bit icky, but expedient: check for this name if we can't find it elsewhere,
262- // and if so, return the one we know should be present. (Obviously, if other runtime
263- // helper functions of this sort crop up in the future, this should be expanded
264- // into a "builtins map".)
265- if (result == 0 && name == " __udivdi3" ) {
266- result = (uint64_t )&__udivdi3;
267- }
268- #endif
269325 internal_assert (result != 0 )
270326 << " HalideJITMemoryManager: unable to find address for " << name << " \n " ;
271327 return result;
@@ -420,6 +476,25 @@ void compile_module_impl(
420476 }
421477 }
422478 }
479+
480+ // On 32-bit targets, add libgcc builtins that dlsym can't find.
481+ #if defined(__GNUC__) && defined(__i386__)
482+ for (const auto &[sym_name, sym_addr] : i386_builtins) {
483+ auto name = symbolStringPool->intern (sym_name);
484+ if (!newSymbols.count (name)) {
485+ newSymbols.insert ({name, {llvm::orc::ExecutorAddr (sym_addr), JITSymbolFlags::Exported}});
486+ }
487+ }
488+ #endif
489+ #if defined(__arm__)
490+ for (const auto &[sym_name, sym_addr] : arm32_sync_builtins) {
491+ auto name = symbolStringPool->intern (sym_name);
492+ if (!newSymbols.count (name)) {
493+ newSymbols.insert ({name, {llvm::orc::ExecutorAddr (sym_addr), JITSymbolFlags::Exported}});
494+ }
495+ }
496+ #endif
497+
423498 err = JIT ->getMainJITDylib ().define (orc::absoluteSymbols (std::move (newSymbols)));
424499 internal_assert (!err) << llvm::toString (std::move (err)) << " \n " ;
425500
0 commit comments