/* This file is derived from gcc-2.6.3/libgcc2.c, section L_clear_cache */ /* Copyright (C) 1989, 1992, 1993, 1994 Free Software Foundation, Inc. This file is part of GNU CC. GNU CC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNU CC 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 General Public License for more details. You should have received a copy of the GNU General Public License along with GNU CC; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Clear part of an instruction cache. */ /* Our emphasis here is _not_ to clear as few cache lines as possible * or with as few machine instructions as possible, but to do it _right_. */ /* This code is apparently untested!! */ #ifdef __m68k__ #ifdef NeXT #define CLEAR_INSN_CACHE(BEG, END) \ asm volatile ("trap #2") #endif #endif /* This is from Andreas Stolcke <stolcke@ICSI.Berkeley.EDU>. */ #if defined(__mips__) || defined(__mips64__) #ifdef ultrix #include <mips/cachectl.h> #else #include <sys/cachectl.h> #endif #define CLEAR_INSN_CACHE(BEG, END) \ cacheflush (BEG, END - BEG, BCACHE) #endif #ifdef __convex__ #define CLEAR_INSN_CACHE(BEG, END) \ asm ("pich") #endif void __TR_clear_cache (beg, end) char *beg, *end; { #ifdef CLEAR_INSN_CACHE CLEAR_INSN_CACHE (beg, end); #else #ifdef INSN_CACHE_SIZE /* This is actually dead code!! */ #define INSN_CACHE_PLANE_SIZE (INSN_CACHE_SIZE / INSN_CACHE_DEPTH) static char array[INSN_CACHE_SIZE + INSN_CACHE_PLANE_SIZE + INSN_CACHE_LINE_WIDTH]; static int initialized = 0; int offset; void *start_addr; void *end_addr; typedef (*function_ptr) (); #if (INSN_CACHE_SIZE / INSN_CACHE_LINE_WIDTH) < 16 /* It's cheaper to clear the whole cache. Put in a series of jump instructions so that calling the beginning of the cache will clear the whole thing. */ if (! initialized) { int ptr = (((int) array + INSN_CACHE_LINE_WIDTH - 1) & -INSN_CACHE_LINE_WIDTH); int end_ptr = ptr + INSN_CACHE_SIZE; while (ptr < end_ptr) { *(INSTRUCTION_TYPE *)ptr = JUMP_AHEAD_INSTRUCTION + INSN_CACHE_LINE_WIDTH; ptr += INSN_CACHE_LINE_WIDTH; } *(INSTRUCTION_TYPE *)(ptr - INSN_CACHE_LINE_WIDTH) = RETURN_INSTRUCTION; initialized = 1; } /* Call the beginning of the sequence. */ (((function_ptr) (((int) array + INSN_CACHE_LINE_WIDTH - 1) & -INSN_CACHE_LINE_WIDTH)) ()); #else /* Cache is large. */ if (! initialized) { int ptr = (((int) array + INSN_CACHE_LINE_WIDTH - 1) & -INSN_CACHE_LINE_WIDTH); while (ptr < (int) array + sizeof array) { *(INSTRUCTION_TYPE *)ptr = RETURN_INSTRUCTION; ptr += INSN_CACHE_LINE_WIDTH; } initialized = 1; } /* Find the location in array that occupies the same cache line as BEG. */ offset = ((int) beg & -INSN_CACHE_LINE_WIDTH) & (INSN_CACHE_PLANE_SIZE - 1); start_addr = (((int) (array + INSN_CACHE_PLANE_SIZE - 1) & -INSN_CACHE_PLANE_SIZE) + offset); /* Compute the cache alignment of the place to stop clearing. */ #if 0 /* This is not needed for gcc's purposes. */ /* If the block to clear is bigger than a cache plane, we clear the entire cache, and OFFSET is already correct. */ if (end < beg + INSN_CACHE_PLANE_SIZE) #endif offset = (((int) (end + INSN_CACHE_LINE_WIDTH - 1) & -INSN_CACHE_LINE_WIDTH) & (INSN_CACHE_PLANE_SIZE - 1)); #if INSN_CACHE_DEPTH > 1 end_addr = (start_addr & -INSN_CACHE_PLANE_SIZE) + offset; if (end_addr <= start_addr) end_addr += INSN_CACHE_PLANE_SIZE; for (plane = 0; plane < INSN_CACHE_DEPTH; plane++) { int addr = start_addr + plane * INSN_CACHE_PLANE_SIZE; int stop = end_addr + plane * INSN_CACHE_PLANE_SIZE; while (addr != stop) { /* Call the return instruction at ADDR. */ ((function_ptr) addr) (); addr += INSN_CACHE_LINE_WIDTH; } } #else /* just one plane */ do { /* Call the return instruction at START_ADDR. */ ((function_ptr) start_addr) (); start_addr += INSN_CACHE_LINE_WIDTH; } while ((start_addr % INSN_CACHE_SIZE) != offset); #endif /* just one plane */ #endif /* Cache is large */ #endif /* Cache exists */ #endif /* CLEAR_INSN_CACHE */ }