Adjust the clobbers list. This use to work with older versions of gcc, but now will error on newer versions (tested against 5.3) (as well as clang). Patch by Tee Hao Wei! llvm-svn: 261821
161 lines
5.6 KiB
C
161 lines
5.6 KiB
C
/* ===-- clear_cache.c - Implement __clear_cache ---------------------------===
|
|
*
|
|
* The LLVM Compiler Infrastructure
|
|
*
|
|
* This file is dual licensed under the MIT and the University of Illinois Open
|
|
* Source Licenses. See LICENSE.TXT for details.
|
|
*
|
|
* ===----------------------------------------------------------------------===
|
|
*/
|
|
|
|
#include "int_lib.h"
|
|
#include <stddef.h>
|
|
|
|
#if __APPLE__
|
|
#include <libkern/OSCacheControl.h>
|
|
#endif
|
|
#if (defined(__FreeBSD__) || defined(__Bitrig__)) && defined(__arm__)
|
|
#include <sys/types.h>
|
|
#include <machine/sysarch.h>
|
|
#endif
|
|
|
|
#if defined(__NetBSD__) && defined(__arm__)
|
|
#include <machine/sysarch.h>
|
|
#endif
|
|
|
|
#if defined(__mips__)
|
|
#include <sys/cachectl.h>
|
|
#include <sys/syscall.h>
|
|
#include <unistd.h>
|
|
#if defined(__ANDROID__) && defined(__LP64__)
|
|
/*
|
|
* clear_mips_cache - Invalidates instruction cache for Mips.
|
|
*/
|
|
static void clear_mips_cache(const void* Addr, size_t Size) {
|
|
asm volatile (
|
|
".set push\n"
|
|
".set noreorder\n"
|
|
".set noat\n"
|
|
"beq %[Size], $zero, 20f\n" /* If size == 0, branch around. */
|
|
"nop\n"
|
|
"daddu %[Size], %[Addr], %[Size]\n" /* Calculate end address + 1 */
|
|
"rdhwr $v0, $1\n" /* Get step size for SYNCI.
|
|
$1 is $HW_SYNCI_Step */
|
|
"beq $v0, $zero, 20f\n" /* If no caches require
|
|
synchronization, branch
|
|
around. */
|
|
"nop\n"
|
|
"10:\n"
|
|
"synci 0(%[Addr])\n" /* Synchronize all caches around
|
|
address. */
|
|
"daddu %[Addr], %[Addr], $v0\n" /* Add step size. */
|
|
"sltu $at, %[Addr], %[Size]\n" /* Compare current with end
|
|
address. */
|
|
"bne $at, $zero, 10b\n" /* Branch if more to do. */
|
|
"nop\n"
|
|
"sync\n" /* Clear memory hazards. */
|
|
"20:\n"
|
|
"bal 30f\n"
|
|
"nop\n"
|
|
"30:\n"
|
|
"daddiu $ra, $ra, 12\n" /* $ra has a value of $pc here.
|
|
Add offset of 12 to point to the
|
|
instruction after the last nop.
|
|
*/
|
|
"jr.hb $ra\n" /* Return, clearing instruction
|
|
hazards. */
|
|
"nop\n"
|
|
".set pop\n"
|
|
: [Addr] "+r"(Addr), [Size] "+r"(Size)
|
|
:: "at", "ra", "v0", "memory"
|
|
);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(__ANDROID__) && defined(__arm__)
|
|
#include <asm/unistd.h>
|
|
#endif
|
|
|
|
/*
|
|
* The compiler generates calls to __clear_cache() when creating
|
|
* trampoline functions on the stack for use with nested functions.
|
|
* It is expected to invalidate the instruction cache for the
|
|
* specified range.
|
|
*/
|
|
|
|
void __clear_cache(void *start, void *end) {
|
|
#if __i386__ || __x86_64__
|
|
/*
|
|
* Intel processors have a unified instruction and data cache
|
|
* so there is nothing to do
|
|
*/
|
|
#elif defined(__arm__) && !defined(__APPLE__)
|
|
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__Bitrig__)
|
|
struct arm_sync_icache_args arg;
|
|
|
|
arg.addr = (uintptr_t)start;
|
|
arg.len = (uintptr_t)end - (uintptr_t)start;
|
|
|
|
sysarch(ARM_SYNC_ICACHE, &arg);
|
|
#elif defined(__ANDROID__)
|
|
register int start_reg __asm("r0") = (int) (intptr_t) start;
|
|
const register int end_reg __asm("r1") = (int) (intptr_t) end;
|
|
const register int flags __asm("r2") = 0;
|
|
const register int syscall_nr __asm("r7") = __ARM_NR_cacheflush;
|
|
__asm __volatile("svc 0x0"
|
|
: "=r"(start_reg)
|
|
: "r"(syscall_nr), "r"(start_reg), "r"(end_reg));
|
|
if (start_reg != 0) {
|
|
compilerrt_abort();
|
|
}
|
|
#else
|
|
compilerrt_abort();
|
|
#endif
|
|
#elif defined(__mips__)
|
|
const uintptr_t start_int = (uintptr_t) start;
|
|
const uintptr_t end_int = (uintptr_t) end;
|
|
#if defined(__ANDROID__) && defined(__LP64__)
|
|
// Call synci implementation for short address range.
|
|
const uintptr_t address_range_limit = 256;
|
|
if ((end_int - start_int) <= address_range_limit) {
|
|
clear_mips_cache(start, (end_int - start_int));
|
|
} else {
|
|
syscall(__NR_cacheflush, start, (end_int - start_int), BCACHE);
|
|
}
|
|
#else
|
|
syscall(__NR_cacheflush, start, (end_int - start_int), BCACHE);
|
|
#endif
|
|
#elif defined(__aarch64__) && !defined(__APPLE__)
|
|
uint64_t xstart = (uint64_t)(uintptr_t) start;
|
|
uint64_t xend = (uint64_t)(uintptr_t) end;
|
|
uint64_t addr;
|
|
|
|
// Get Cache Type Info
|
|
uint64_t ctr_el0;
|
|
__asm __volatile("mrs %0, ctr_el0" : "=r"(ctr_el0));
|
|
|
|
/*
|
|
* dc & ic instructions must use 64bit registers so we don't use
|
|
* uintptr_t in case this runs in an IPL32 environment.
|
|
*/
|
|
const size_t dcache_line_size = 4 << ((ctr_el0 >> 16) & 15);
|
|
for (addr = xstart; addr < xend; addr += dcache_line_size)
|
|
__asm __volatile("dc cvau, %0" :: "r"(addr));
|
|
__asm __volatile("dsb ish");
|
|
|
|
const size_t icache_line_size = 4 << ((ctr_el0 >> 0) & 15);
|
|
for (addr = xstart; addr < xend; addr += icache_line_size)
|
|
__asm __volatile("ic ivau, %0" :: "r"(addr));
|
|
__asm __volatile("isb sy");
|
|
#else
|
|
#if __APPLE__
|
|
/* On Darwin, sys_icache_invalidate() provides this functionality */
|
|
sys_icache_invalidate(start, end-start);
|
|
#else
|
|
compilerrt_abort();
|
|
#endif
|
|
#endif
|
|
}
|
|
|