--- /dev/null
+/* JIT compiler - Flushing the instruction cache.
+
+ Copyright (C) 1995-2023 Free Software Foundation, Inc.
+
+ This file is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ This file is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2020. */
+
+#if ENABLE_VALGRIND_SUPPORT
+# include <valgrind/valgrind.h>
+#endif
+#if defined _WIN32 && !defined __CYGWIN__
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#endif
+#if defined __APPLE__ && defined __MACH__
+# include <libkern/OSCacheControl.h>
+#endif
+#if defined _AIX
+# include <sys/cache.h>
+#endif
+#if defined __sgi
+# include <sys/cachectl.h>
+#endif
+
+/* Clears the instruction cache for addresses
+ start <= address < end.
+ We need this because some CPUs have separate data cache and instruction
+ cache. The freshly built trampoline is visible to the data cache, but
+ maybe not to the instruction cache. This is hairy. */
+static inline void
+clear_cache (void *start, void *end)
+{
+#if ENABLE_VALGRIND_SUPPORT
+ /* Documentation:
+ <https://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.clientreq> */
+ VALGRIND_DISCARD_TRANSLATIONS (start, (char *) end - (char *) start);
+#endif
+#if (defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86)
+ /* On this architecture, data cache and instruction cache are not separate.
+ Therefore, nothing to do.
+ For details, see
+ <https://stackoverflow.com/questions/10989403/how-is-x86-instruction-cache-synchronized> */
+
+/* Use the operating system provided function, when available. */
+#elif defined _WIN32 && !defined __CYGWIN__
+ /* Native Windows.
+ FlushInstructionCache
+ <https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-flushinstructioncache> */
+ HANDLE process = GetCurrentProcess ();
+ while (!FlushInstructionCache (process, start, (char *) end - (char *) start))
+ ;
+#elif defined __APPLE__ && defined __MACH__
+ /* macOS */
+ sys_icache_invalidate (start, (char *) end - (char *) start);
+#elif defined _AIX
+ /* AIX. */
+ _sync_cache_range (start, (char *) end - (char *) start);
+#elif defined __sgi
+ /* IRIX. */
+ cacheflush (start, (char *) end - (char *) start, ICACHE);
+#elif defined __sun
+ /* Solaris. */
+ extern void sync_instruction_memory (char *, size_t);
+ sync_instruction_memory (start, (char *) end - (char *) start);
+
+/* No operating system provided function. Dispatch according to the CPU. */
+#elif (defined __GNUC__ || defined __clang__) && defined __powerpc__
+ /* XXX Is this enough, or do we also need the 'clf' instruction? */
+ uintptr_t addr = (uintptr_t) start & ~(intptr_t)3;
+ uintptr_t end_addr = (uintptr_t) end;
+ do
+ {
+ asm volatile ("icbi 0,%0; dcbf 0,%0" : : "r" (addr));
+ addr += 4;
+ }
+ while (addr < end_addr);
+ asm volatile ("sync; isync");
+#elif (defined __GNUC__ || defined __clang__) && defined __sparc
+ /* Use inline assembly. */
+ /* The 'flush' instruction was earlier called 'iflush'. */
+ uintptr_t addr = (uintptr_t) start & ~(intptr_t)7;
+ uintptr_t end_addr = (uintptr_t) end;
+ do
+ {
+ asm volatile ("flush %0+0" : : "r" (addr));
+ addr += 8;
+ }
+ while (addr < end_addr);
+#elif (defined __GNUC__ || defined __clang__) && defined __m68k__ && defined __linux__
+ /* Use inline assembly to call the 'cacheflush' system call.
+ sys_cacheflush (addr, scope, cache, len)
+ d1 d2 d3 d4
+ */
+ register uintptr_t addr __asm__ ("%d1") = (uintptr_t) start;
+ register uintptr_t len __asm__ ("%d4") = (uintptr_t) end - addr;
+ __asm__ __volatile__ (
+ "move%.l %#123,%/d0" /* __NR_cacheflush */
+ "\n\t" "move%.l %#1,%/d2" /* FLUSH_SCOPE_LINE */
+ "\n\t" "move%.l %#3,%/d3" /* FLUSH_CACHE_BOTH */
+ "\n\t" "trap %#0"
+ :
+ : "d" (addr), "d" (len)
+ : "%d0", "%d2", "%d3"
+ );
+#elif (__GNUC__ + (__GNUC_MINOR__ >= 3) > 4) \
+ || ((__clang_major__ + (__clang_minor__ >= 4) > 3) \
+ && (defined __aarch64__ /* arm64 */ || defined __arm__))
+ /* GCC >= 4.3 has a GCC built-in.
+ <https://gcc.gnu.org/onlinedocs/gcc-4.3.6/gcc/Other-Builtins.html>
+ But it's sometimes not correctly implemented.
+ clang >= 3.4 has it as well, at least on ARM and ARM64. */
+ /* On ARM, cache flushing can only be done through a system call.
+ GCC implements it for Linux with EABI, through an "swi 0" with code
+ 0xf0002. For other systems, it may be an "swi 0x9f0002",
+ an "swi 0xf00000", or similar. */
+ /* On ARM64, cache flushing is done through special instructions,
+ and the length of the cache lines must be determined at runtime.
+ See gcc/libgcc/config/aarch64/sync-cache.c. */
+ __builtin___clear_cache (start, end);
+#elif HAVE___CLEAR_CACHE
+ /* Older versions of GCC have this libgcc function, but only on some
+ platforms. */
+ extern void __clear_cache (char *, char *);
+ __clear_cache (start, end);
+#else
+# error "Don't know how to implement clear_cache on this platform."
+#endif
+}