--- gcc-3.4.2/gcc/config/mips/mips.h Thu Jul 15 02:42:47 2004 +++ gcc-3.4.2/gcc/config/mips/mips.h Sat Sep 18 00:41:48 2004 @@ -122,6 +122,7 @@ extern const char *mips_isa_string; /* for -mips{1,2,3,4} */ extern const char *mips_abi_string; /* for -mabi={32,n32,64} */ extern const char *mips_cache_flush_func;/* for -mflush-func= and -mno-flush-func */ +extern const char *mips_ip28_cache_barrier;/* for -mip28-cache-barrier */ extern int mips_string_length; /* length of strings for mips16 */ extern const struct mips_cpu_info mips_cpu_info_table[]; extern const struct mips_cpu_info *mips_arch_info; @@ -333,6 +334,7 @@ #define TARGET_MIPS9000 (mips_arch == PROCESSOR_R9000) #define TARGET_SB1 (mips_arch == PROCESSOR_SB1) #define TARGET_SR71K (mips_arch == PROCESSOR_SR71000) +#define TARGET_IP28 (mips_ip28_cache_barrier != 0) /* Scheduling target defines. */ #define TUNE_MIPS3000 (mips_tune == PROCESSOR_R3000) @@ -752,6 +754,8 @@ N_("Don't call any cache flush functions"), 0}, \ { "flush-func=", &mips_cache_flush_func, \ N_("Specify cache flush function"), 0}, \ + { "ip28-cache-barrier", &mips_ip28_cache_barrier, \ + N_("Generate special cache barriers for SGI Indigo2 R10k"), 0}, \ } /* This is meant to be redefined in the host dependent files. */ @@ -3448,3 +3452,11 @@ " TEXT_SECTION_ASM_OP); #endif #endif + +#define ASM_OUTPUT_R10K_CACHE_BARRIER(STREAM) \ + fprintf (STREAM, "\tcache 0x14,0($sp)\t%s Cache Barrier\n", ASM_COMMENT_START) + +/* + * mips.h Thu Jul 15 02:42:47 2004 + * mips.h Fri Sep 17 23:18:19 2004 ip28 + */ --- gcc-3.4.2/gcc/config/mips/mips.c Wed Jul 7 21:21:10 2004 +++ gcc-3.4.2/gcc/config/mips/mips.c Fri Sep 17 23:33:44 2004 @@ -502,6 +502,11 @@ const char *mips_cache_flush_func = CACHE_FLUSH_FUNC; +/* Nonzero means generate special cache barriers to inhibit speculative + stores which might endanger cache coherency or reference invalid + addresses (especially on SGI's Indigo2 R10k (IP28)). */ +const char *mips_ip28_cache_barrier; + /* If TRUE, we split addresses into their high and low parts in the RTL. */ int mips_split_addresses; @@ -9676,3 +9681,7 @@ #endif /* TARGET_IRIX */ #include "gt-mips.h" +/* + * mips.c Wed Jul 7 21:21:10 2004 + * mips.c Fri Sep 17 23:25:53 2004 ip28 + */ --- gcc-3.4.2/gcc/final.c Sun Jan 18 23:39:57 2004 +++ gcc-3.4.2/gcc/final.c Thu Apr 7 00:00:05 2005 @@ -146,6 +146,13 @@ static rtx last_ignored_compare = 0; +/* Flag indicating this insn is the start of a new basic block. */ + +#define NEW_BLOCK_LABEL 1 +#define NEW_BLOCK_BRANCH 2 + +static int new_block = NEW_BLOCK_LABEL; + /* Assign a unique number to each insn that is output. This can be used to generate unique local labels. */ @@ -235,6 +242,7 @@ #ifdef HAVE_ATTR_length static int align_fuzz (rtx, rtx, int, unsigned); #endif +static int output_store_cache_barrier (FILE *, rtx); /* Initialize data in final at the beginning of a compilation. */ @@ -1505,6 +1513,7 @@ int seen = 0; last_ignored_compare = 0; + new_block = NEW_BLOCK_LABEL; #ifdef SDB_DEBUGGING_INFO /* When producing SDB debugging info, delete troublesome line number @@ -1571,6 +1580,7 @@ insn = final_scan_insn (insn, file, optimize, prescan, 0, &seen); } + new_block = 0; } const char * @@ -1851,6 +1861,7 @@ #endif if (prescan > 0) break; + new_block = NEW_BLOCK_LABEL; if (LABEL_NAME (insn)) (*debug_hooks->label) (insn); @@ -2009,6 +2020,26 @@ break; } + +#ifdef TARGET_IP28 + if (new_block) + { + /* .reorder: not really in the branch-delay-slot. */ + if (! set_noreorder) + new_block = NEW_BLOCK_LABEL; + + if (new_block == NEW_BLOCK_BRANCH) + /* Not yet, only *after* the branch-delay-slot ! */ + new_block = NEW_BLOCK_LABEL; + else + { + if (TARGET_IP28) + output_store_cache_barrier (file, insn); + new_block = 0; + } + } +#endif + /* Output this line note if it is the first or the last line note in a row. */ if (notice_source_line (insn)) @@ -2132,8 +2163,29 @@ clobbered by the function. */ if (GET_CODE (XVECEXP (body, 0, 0)) == CALL_INSN) { +#ifdef TARGET_IP28 + if (TARGET_IP28) + new_block = NEW_BLOCK_LABEL; +#endif CC_STATUS_INIT; } +#ifdef TARGET_IP28 + /* Following a conditional branch sequence, we have a new basic + block. */ + if (TARGET_IP28) + { + rtx insn = XVECEXP (body, 0, 0); + rtx body = PATTERN (insn); + + if ((GET_CODE (insn) == JUMP_INSN && GET_CODE (body) == SET + && GET_CODE (SET_SRC (body)) != LABEL_REF) + || (GET_CODE (insn) == JUMP_INSN + && GET_CODE (body) == PARALLEL + && GET_CODE (XVECEXP (body, 0, 0)) == SET + && GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) != LABEL_REF)) + new_block = NEW_BLOCK_LABEL; + } +#endif break; } @@ -2188,6 +2240,20 @@ } #endif +#ifdef TARGET_IP28 + /* Following a conditional branch, we have a new basic block. + But if we are inside a sequence, the new block starts after the + last insn of the sequence. */ + if (TARGET_IP28 && final_sequence == 0 + && (GET_CODE (insn) == CALL_INSN + || (GET_CODE (insn) == JUMP_INSN && GET_CODE (body) == SET + && GET_CODE (SET_SRC (body)) != LABEL_REF) + || (GET_CODE (insn) == JUMP_INSN && GET_CODE (body) == PARALLEL + && GET_CODE (XVECEXP (body, 0, 0)) == SET + && GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) != LABEL_REF))) + new_block = NEW_BLOCK_BRANCH; +#endif + #ifndef STACK_REGS /* Don't bother outputting obvious no-ops, even without -O. This optimization is fast and doesn't interfere with debugging. @@ -2402,6 +2468,7 @@ if (prev_nonnote_insn (insn) != last_ignored_compare) abort (); + new_block = 0; /* We have already processed the notes between the setter and the user. Make sure we don't process them again, this is @@ -2435,6 +2502,7 @@ abort (); #endif + new_block = 0; return new; } @@ -3866,3 +3934,254 @@ symbol_queue_size = 0; } } + + +#ifdef TARGET_IP28 + +/* Check, whether an instruction is a possibly harmful store instruction, + i.e. a store which might cause damage, if speculatively executed. */ + +static rtx +find_mem_expr (rtx xexp) +{ + if (xexp) + { + const char *fmt; + int i, j, lng; + rtx x; + RTX_CODE code = GET_CODE (xexp); + + if (MEM == code) + return xexp; + + fmt = GET_RTX_FORMAT (code); + lng = GET_RTX_LENGTH (code); + + for (i = 0; i < lng; ++i) + switch (fmt[i]) + { + case 'e': + x = find_mem_expr (XEXP (xexp, i)); + if (x) + return x; + break; + case 'E': + if (XVEC (xexp, i)) + for (j = 0; j < XVECLEN (xexp, i); ++j) + { + x = find_mem_expr (XVECEXP (xexp, i, j)); + if (x) + return x; + } + } + } + return 0; +} + +static int +check_mem_expr (rtx memx) +{ + /* Check the expression `memx' (with type GET_CODE(memx) == MEM) + for the most common stackpointer-addressing modes. + It's not worthwile to avoid a cache barrier also on the + remaining unfrequently used modes. */ + rtx x = XEXP (memx, 0); + switch (GET_CODE (x)) + { + case REG: + if (REGNO (x) == STACK_POINTER_REGNUM) + return 0; + default: + break; + case PLUS: case MINUS: /* always `SP + const' ? */ + if (GET_CODE (XEXP (x, 1)) == REG + && REGNO (XEXP (x, 1)) == STACK_POINTER_REGNUM) + return 0; + case NEG: case SIGN_EXTEND: case ZERO_EXTEND: + if (GET_CODE (XEXP (x, 0)) == REG + && REGNO (XEXP (x, 0)) == STACK_POINTER_REGNUM) + return 0; + } + + /* Stores/Loads to/from constant addresses can be considered + harmless, since: + 1) the address is always valid, even when taken speculatively. + 2a) the location is (hopefully) never used as a dma-target, thus + there is no danger of cache-inconsistency. + 2b) uncached loads/stores are guaranteed to be non-speculative. */ + if ( CONSTANT_P(x) ) + return 0; + + return 1; +} + +/* inline */ static int +check_pattern_for_store (rtx body) +{ + /* Check for (set (mem:M (non_stackpointer_address) ...)). Here we + assume, that addressing with the stackpointer accesses neither + uncached-aliased nor invalid memory. (May be, this applies to the + global pointer and frame pointer also, but its saver not to assume + it. And probably it's not worthwile to regard these registers) + + Speculative loads from invalid addresses also cause bus errors... + So check for (set (reg:M ...) (mem:M (non_stackpointer_address))) + too. */ + + if (body && GET_CODE (body) == SET) + { + rtx x = find_mem_expr (body); + + if (x && check_mem_expr (x)) + return 1; + } + return 0; +} + +static int +check_insn_for_store (int state, rtx insn) +{ + /* Check for (ins (set (mem:M (dangerous_address)) ...)) or end of the + current basic block. + Criteria to recognize end-of/next basic-block are reduplicated here + from final_scan_insn. */ + + rtx body; + int code; + + if (INSN_DELETED_P (insn)) + return 0; + + switch (code = GET_CODE (insn)) + { + case CODE_LABEL: + return -1; + case CALL_INSN: + case JUMP_INSN: + case INSN: + body = PATTERN (insn); + if (GET_CODE (body) == SEQUENCE) + { + /* A delayed-branch sequence */ + rtx ins0 = XVECEXP (body, 0, 0); + rtx pat0 = PATTERN (ins0); + int i; + for (i = 0; i < XVECLEN (body, 0); i++) + { + rtx insq = XVECEXP (body, 0, i); + if (! INSN_DELETED_P (insq)) + { + int j = check_insn_for_store (state|1, insq); + if (j) + return j; + } + } + /* Following a conditional branch sequence, we have a new + basic block. */ + if (GET_CODE (ins0) == JUMP_INSN) + if ((GET_CODE (pat0) == SET + && GET_CODE (SET_SRC (pat0)) != LABEL_REF) + || (GET_CODE (pat0) == PARALLEL + && GET_CODE (XVECEXP (pat0, 0, 0)) == SET + && GET_CODE (SET_SRC (XVECEXP (pat0, 0, 0))) != LABEL_REF)) + return -1; + /* Handle a call sequence like a conditional branch sequence */ + if (GET_CODE (ins0) == CALL_INSN) + return -1; + break; + } + if (GET_CODE (body) == PARALLEL) + { + int i; + for (i = 0; i < XVECLEN (body, 0); i++) + if (check_pattern_for_store (XVECEXP (body, 0, i))) + return 1; + } + /* Now, only a `simple' INSN or JUMP_INSN remains to be checked. */ + if (code == INSN) + { + /* Since we don't know, what's inside, we must take inline + assembly to be dangerous */ + if (GET_CODE (body) == ASM_INPUT) + return 1; + + if (check_pattern_for_store (body)) + return 1; + } + /* Handle a CALL_INSN instruction like a conditional branch */ + if (code == JUMP_INSN || code == CALL_INSN) + { + /* Following a conditional branch, we have a new basic block. */ + int ckds = 0; + if (code == CALL_INSN) + ckds = 1; + else + { + code = GET_CODE (body); + if ((code == SET + && GET_CODE (SET_SRC (body)) != LABEL_REF) + || (code == PARALLEL + && GET_CODE (XVECEXP (body, 0, 0)) == SET + && GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) != LABEL_REF)) + ckds = 1; + } + if (ckds) + { + /* But check insn(s) in delay-slot first. If we could know in + advance that this jump is in `.reorder' mode, where gas will + insert a `nop' into the delay-slot, we could skip this test. + Since we don't know, always assume `.noreorder', sometimes + emitting a cache-barrier, that isn't needed. */ + /* But if we are here recursively, already checking a (pseudo-) + delay-slot, we are done. */ + if ( !(state & 2) ) + for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn)) + switch (GET_CODE (insn)) + { + case INSN: + if (check_insn_for_store (state|1|2, insn) > 0) + return 1; + case CODE_LABEL: + case CALL_INSN: + case JUMP_INSN: + return -1; + default: + /* skip NOTE,... */; + } + return -1; + } + } + /*break*/ + } + return 0; +} + +/* Scan a basic block, starting with `insn', for a possibly harmful store + instruction. If found, output a cache barrier at the start of this + block. */ + +static int +output_store_cache_barrier (FILE *file, rtx insn) +{ + for (; insn; insn = NEXT_INSN (insn)) + { + int found = check_insn_for_store (0, insn); + if (found < 0) + break; + if (found > 0) + { + /* found critical store instruction */ + ASM_OUTPUT_R10K_CACHE_BARRIER(file); + return 1; + } + } + fprintf(file, "\t%s Cache Barrier omitted.\n", ASM_COMMENT_START); + return 0; +} + +#endif /* TARGET_IP28 */ + +/* + * final.c Sun Jan 18 23:39:57 2004 + * final.c Sat Sep 18 00:23:34 2004 ip28 + */