diff options
author | amylaar <amylaar@138bc75d-0d04-0410-961f-82ee72b054a4> | 2006-01-30 15:07:43 +0000 |
---|---|---|
committer | amylaar <amylaar@138bc75d-0d04-0410-961f-82ee72b054a4> | 2006-01-30 15:07:43 +0000 |
commit | 57d5535b031f85ada1a22059d8215cb349aa0e97 (patch) | |
tree | 2c0dc139501a3b56003fa7a9d0a9f3e5ae212d8e /gcc/config/sh | |
parent | cdd306bef327393903d2a2f3fe658feba87606e3 (diff) | |
download | gcc-57d5535b031f85ada1a22059d8215cb349aa0e97.tar.gz |
PR target/14798:
gcc:
* sh.c (pragma_interrupt, trap_exit, sp_switch): Remove variable.
(pragma_trap, pragma_nosave_low_regs): Likewise.
(current_function_anonymous_args): Likewise.
(sh_deferred_function_attributes): New variable.
(sh_deferred_function_attributes_tail): Likewise.
(print_operand): For '@', look up trap_exit attribute.
(calc_live_regs): Look up trapa_handler attribute. For trapa
handlers, save/restore fpscr, but don't do any other
interrupt-specific saves.
Don't save r0..r7 if the nosave_low_regs attribute is in effect.
Fix check for partially saved registers to check for SHmedia.
(sh_expand_prologue, sh_expand_epilogue): Look up sp_switch attribute.
(sh_output_function_epilogue): Don't clear any of the removed
variables.
(sh_insert_attributes): Don't check pragma_interrupt.
Insert deferred attributes. Check that interrupt attribute is
present for other attributes that require its presence.
(sh_attribute_table): Add new attributes trapa_handler and
nosave_low_regs.
(sh_handle_sp_switch_attribute, sh_handle_trap_exit_attribute):
Don't check for pragma_interrupt. Don't store argument.
* sh.h (pragma_interrupt, sp_switch): Don't declare.
(sh_deferred_function_attributes): Declare.
(sh_deferred_function_attributes_tail): Likewise.
* sh.md (sp_switch_1): Add operand. Change generator caller.
(sh_pr_interrupt, sh_pr_trapa, sh_pr_nosave_low_regs): Remove.
(*return_i): Don't use when trap_exit attribute is in effect.
(*return_trapa): New insn pattern.
* sh-c.c: New file.
* config.gcc (sh[123456ble]*-* | sh-*-*): New trailer stanza,
setting c_target_objs and cxx_target_objs.
* t-sh: Add rule for sh-c.o.
gcc/testsuite:
* gcc.dg/pragma-isr.c: Added target sh[1234ble]*-*-*.
* gcc.dg/pragma-isr2.c, gcc.dg/pragma-isr-trapa.c: New tests.
* gcc.dg/pragma-isr-trapa2.c: Likewise.
* gcc.dg/pragma-isr-nosave_low_regs.c: Likewise.
* gcc.dg/pragma-isr-trap_exit.c: Likewise.
* gcc.dg/attr-isr.c, gcc.dg/attr-isr-trapa.c: Likewise.
* gcc.dg/attr-isr-trap_exit.c: Likewise.
* gcc.dg/attr-isr-nosave_low_regs.c: Likewise.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@110398 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/config/sh')
-rw-r--r-- | gcc/config/sh/sh-c.c | 69 | ||||
-rw-r--r-- | gcc/config/sh/sh.c | 198 | ||||
-rw-r--r-- | gcc/config/sh/sh.h | 9 | ||||
-rw-r--r-- | gcc/config/sh/sh.md | 21 | ||||
-rw-r--r-- | gcc/config/sh/t-sh | 4 |
5 files changed, 195 insertions, 106 deletions
diff --git a/gcc/config/sh/sh-c.c b/gcc/config/sh/sh-c.c new file mode 100644 index 00000000000..7b9ec1c1e4d --- /dev/null +++ b/gcc/config/sh/sh-c.c @@ -0,0 +1,69 @@ +/* Pragma handling for GCC for Renesas / SuperH SH. + Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, + 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + Contributed by Joern Rennecke <joern.rennecke@st.com>. + +This file is part of GCC. + +GCC 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. + +GCC 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 GCC; see the file COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "tm_p.h" + +/* Handle machine specific pragmas to be semi-compatible with Renesas + compiler. */ + +/* Add ATTR to the attributes of the current function. If there is no + such function, save it to be added to the attributes of the next + function. */ +static void +sh_add_function_attribute (const char *attr) +{ + tree id = get_identifier (attr); + + if (current_function_decl) + decl_attributes (¤t_function_decl, + tree_cons (id, NULL_TREE, NULL_TREE), 0); + else + { + *sh_deferred_function_attributes_tail + = tree_cons (id, NULL_TREE, *sh_deferred_function_attributes_tail); + sh_deferred_function_attributes_tail + = &TREE_CHAIN (*sh_deferred_function_attributes_tail); + } +} + +void +sh_pr_interrupt (struct cpp_reader *pfile ATTRIBUTE_UNUSED) +{ + sh_add_function_attribute ("interrupt_handler"); +} + +void +sh_pr_trapa (struct cpp_reader *pfile ATTRIBUTE_UNUSED) +{ + sh_add_function_attribute ("trapa_handler"); +} + +void +sh_pr_nosave_low_regs (struct cpp_reader *pfile ATTRIBUTE_UNUSED) +{ + sh_add_function_attribute ("nosave_low_regs"); +} diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c index a9cafec612f..6dfe282f4d9 100644 --- a/gcc/config/sh/sh.c +++ b/gcc/config/sh/sh.c @@ -70,35 +70,8 @@ int code_for_indirect_jump_scratch = CODE_FOR_indirect_jump_scratch; /* Set to 1 by expand_prologue() when the function is an interrupt handler. */ int current_function_interrupt; -/* ??? The pragma interrupt support will not work for SH3. */ -/* This is set by #pragma interrupt and #pragma trapa, and causes gcc to - output code for the next function appropriate for an interrupt handler. */ -int pragma_interrupt; - -/* This is set by the trap_exit attribute for functions. It specifies - a trap number to be used in a trapa instruction at function exit - (instead of an rte instruction). */ -int trap_exit; - -/* This is used by the sp_switch attribute for functions. It specifies - a variable holding the address of the stack the interrupt function - should switch to/from at entry/exit. */ -rtx sp_switch; - -/* This is set by #pragma trapa, and is similar to the above, except that - the compiler doesn't emit code to preserve all registers. */ -static int pragma_trapa; - -/* This is set by #pragma nosave_low_regs. This is useful on the SH3, - which has a separate set of low regs for User and Supervisor modes. - This should only be used for the lowest level of interrupts. Higher levels - of interrupts must save the registers in case they themselves are - interrupted. */ -int pragma_nosave_low_regs; - -/* This is used for communication between TARGET_SETUP_INCOMING_VARARGS and - sh_expand_prologue. */ -int current_function_anonymous_args; +tree sh_deferred_function_attributes; +tree *sh_deferred_function_attributes_tail = &sh_deferred_function_attributes; /* Global variables for machine-dependent things. */ @@ -696,6 +669,8 @@ print_operand (FILE *stream, rtx x, int code) switch (code) { + tree trapa_attr; + case '.': if (final_sequence && ! INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0)) @@ -706,8 +681,11 @@ print_operand (FILE *stream, rtx x, int code) fprintf (stream, "%s", LOCAL_LABEL_PREFIX); break; case '@': - if (trap_exit) - fprintf (stream, "trapa #%d", trap_exit); + trapa_attr = lookup_attribute ("trap_exit", + DECL_ATTRIBUTES (current_function_decl)); + if (trapa_attr) + fprintf (stream, "trapa #%ld", + (long) TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (trapa_attr)))); else if (sh_cfun_interrupt_handler_p ()) fprintf (stream, "rte"); else @@ -5418,10 +5396,16 @@ calc_live_regs (HARD_REG_SET *live_regs_mask) { unsigned int reg; int count; - int interrupt_handler; + tree attrs; + bool interrupt_or_trapa_handler, trapa_handler, interrupt_handler; + bool nosave_low_regs; int pr_live, has_call; - interrupt_handler = sh_cfun_interrupt_handler_p (); + attrs = DECL_ATTRIBUTES (current_function_decl); + interrupt_or_trapa_handler = sh_cfun_interrupt_handler_p (); + trapa_handler = lookup_attribute ("trapa_handler", attrs) != NULL_TREE; + interrupt_handler = interrupt_or_trapa_handler && ! trapa_handler; + nosave_low_regs = lookup_attribute ("nosave_low_regs", attrs) != NULL_TREE; CLEAR_HARD_REG_SET (*live_regs_mask); if ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && TARGET_FMOVD && interrupt_handler @@ -5432,7 +5416,7 @@ calc_live_regs (HARD_REG_SET *live_regs_mask) for (count = 0, reg = FIRST_FP_REG; reg <= LAST_FP_REG; reg += 2) if (regs_ever_live[reg] && regs_ever_live[reg+1] && (! call_really_used_regs[reg] - || (interrupt_handler && ! pragma_trapa)) + || interrupt_handler) && ++count > 2) { target_flags &= ~MASK_FPU_SINGLE; @@ -5470,14 +5454,15 @@ calc_live_regs (HARD_REG_SET *live_regs_mask) { if (reg == (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG) ? pr_live - : (interrupt_handler && ! pragma_trapa) + : interrupt_handler ? (/* Need to save all the regs ever live. */ (regs_ever_live[reg] || (call_really_used_regs[reg] && (! fixed_regs[reg] || reg == MACH_REG || reg == MACL_REG || reg == PIC_OFFSET_TABLE_REGNUM) && has_call) - || (has_call && REGISTER_NATURAL_MODE (reg) == SImode + || (TARGET_SHMEDIA && has_call + && REGISTER_NATURAL_MODE (reg) == SImode && (GENERAL_REGISTER_P (reg) || TARGET_REGISTER_P (reg)))) && reg != STACK_POINTER_REGNUM && reg != ARG_POINTER_REGNUM && reg != RETURN_ADDRESS_POINTER_REGNUM @@ -5489,7 +5474,9 @@ calc_live_regs (HARD_REG_SET *live_regs_mask) && flag_pic && current_function_args_info.call_cookie && reg == PIC_OFFSET_TABLE_REGNUM) - || (regs_ever_live[reg] && ! call_really_used_regs[reg]) + || (regs_ever_live[reg] + && (!call_really_used_regs[reg] + || (trapa_handler && reg == FPSCR_REG && TARGET_FPU_ANY))) || (current_function_calls_eh_return && (reg == EH_RETURN_DATA_REGNO (0) || reg == EH_RETURN_DATA_REGNO (1) @@ -5521,6 +5508,8 @@ calc_live_regs (HARD_REG_SET *live_regs_mask) } } } + if (nosave_low_regs && reg == R8_REG) + break; } /* If we have a target register optimization pass after prologue / epilogue threading, we need to assume all target registers will be live even if @@ -5724,6 +5713,8 @@ sh_expand_prologue (void) int d_rounding = 0; int save_flags = target_flags; int pretend_args; + tree sp_switch_attr + = lookup_attribute ("sp_switch", DECL_ATTRIBUTES (current_function_decl)); current_function_interrupt = sh_cfun_interrupt_handler_p (); @@ -5813,8 +5804,16 @@ sh_expand_prologue (void) } /* If we're supposed to switch stacks at function entry, do so now. */ - if (sp_switch) - emit_insn (gen_sp_switch_1 ()); + if (sp_switch_attr) + { + /* The argument specifies a variable holding the address of the + stack the interrupt function should switch to/from at entry/exit. */ + const char *s + = ggc_strdup (TREE_STRING_POINTER (TREE_VALUE (sp_switch_attr))); + rtx sp_switch = gen_rtx_SYMBOL_REF (Pmode, s); + + emit_insn (gen_sp_switch_1 (sp_switch)); + } d = calc_live_regs (&live_regs_mask); /* ??? Maybe we could save some switching if we can move a mode switch @@ -6333,7 +6332,7 @@ sh_expand_epilogue (bool sibcall_p) EH_RETURN_STACKADJ_RTX)); /* Switch back to the normal stack if necessary. */ - if (sp_switch) + if (lookup_attribute ("sp_switch", DECL_ATTRIBUTES (current_function_decl))) emit_insn (gen_sp_switch_2 ()); /* Tell flow the insn that pops PR isn't dead. */ @@ -6435,9 +6434,7 @@ static void sh_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED, HOST_WIDE_INT size ATTRIBUTE_UNUSED) { - trap_exit = pragma_interrupt = pragma_trapa = pragma_nosave_low_regs = 0; sh_need_epilogue_known = 0; - sp_switch = NULL_RTX; } static rtx @@ -7446,42 +7443,69 @@ initial_elimination_offset (int from, int to) return total_auto_space; } -/* Handle machine specific pragmas to be semi-compatible with Renesas - compiler. */ - -void -sh_pr_interrupt (struct cpp_reader *pfile ATTRIBUTE_UNUSED) -{ - pragma_interrupt = 1; -} - -void -sh_pr_trapa (struct cpp_reader *pfile ATTRIBUTE_UNUSED) -{ - pragma_interrupt = pragma_trapa = 1; -} - -void -sh_pr_nosave_low_regs (struct cpp_reader *pfile ATTRIBUTE_UNUSED) -{ - pragma_nosave_low_regs = 1; -} - -/* Generate 'handle_interrupt' attribute for decls */ - +/* Insert any deferred function attributes from earlier pragmas. */ static void sh_insert_attributes (tree node, tree *attributes) { - if (! pragma_interrupt - || TREE_CODE (node) != FUNCTION_DECL) + tree attrs; + + if (TREE_CODE (node) != FUNCTION_DECL) return; /* We are only interested in fields. */ if (!DECL_P (node)) return; - /* Add a 'handle_interrupt' attribute. */ - * attributes = tree_cons (get_identifier ("interrupt_handler"), NULL, * attributes); + /* Append the attributes to the deferred attributes. */ + *sh_deferred_function_attributes_tail = *attributes; + attrs = sh_deferred_function_attributes; + if (!attrs) + return; + + /* Some attributes imply or require the interrupt attribute. */ + if (!lookup_attribute ("interrupt_handler", attrs) + && !lookup_attribute ("interrupt_handler", DECL_ATTRIBUTES (node))) + { + /* If we have a trapa_handler, but no interrupt_handler attribute, + insert an interrupt_handler attribute. */ + if (lookup_attribute ("trapa_handler", attrs) != NULL_TREE) + /* We can't use sh_pr_interrupt here because that's not in the + java frontend. */ + attrs + = tree_cons (get_identifier("interrupt_handler"), NULL_TREE, attrs); + /* However, for sp_switch, trap_exit and nosave_low_regs, if the + interrupt attribute is missing, we ignore the attribute and warn. */ + else if (lookup_attribute ("sp_switch", attrs) + || lookup_attribute ("trap_exit", attrs) + || lookup_attribute ("nosave_low_regs", attrs)) + { + tree *tail; + + for (tail = attributes; attrs; attrs = TREE_CHAIN (attrs)) + { + if (is_attribute_p ("sp_switch", TREE_PURPOSE (attrs)) + || is_attribute_p ("trap_exit", TREE_PURPOSE (attrs)) + || is_attribute_p ("nosave_low_regs", TREE_PURPOSE (attrs))) + warning (OPT_Wattributes, + "%qs attribute only applies to interrupt functions", + IDENTIFIER_POINTER (TREE_PURPOSE (attrs))); + else + { + *tail = tree_cons (TREE_PURPOSE (attrs), NULL_TREE, + NULL_TREE); + tail = &TREE_CHAIN (*tail); + } + } + attrs = *attributes; + } + } + + /* Install the processed list. */ + *attributes = attrs; + + /* Clear deferred attributes. */ + sh_deferred_function_attributes = NULL_TREE; + sh_deferred_function_attributes_tail = &sh_deferred_function_attributes; return; } @@ -7490,12 +7514,21 @@ sh_insert_attributes (tree node, tree *attributes) interrupt_handler -- specifies this function is an interrupt handler. + trapa_handler - like above, but don't save all registers. + sp_switch -- specifies an alternate stack for an interrupt handler to run on. trap_exit -- use a trapa to exit an interrupt function instead of an rte instruction. + nosave_low_regs - don't save r0..r7 in an interrupt handler. + This is useful on the SH3 and upwards, + which has a separate set of low regs for User and Supervisor modes. + This should only be used for the lowest level of interrupts. Higher levels + of interrupts must save the registers in case they themselves are + interrupted. + renesas -- use Renesas calling/layout conventions (functions and structures). @@ -7508,6 +7541,8 @@ const struct attribute_spec sh_attribute_table[] = { "sp_switch", 1, 1, true, false, false, sh_handle_sp_switch_attribute }, { "trap_exit", 1, 1, true, false, false, sh_handle_trap_exit_attribute }, { "renesas", 0, 0, false, true, false, sh_handle_renesas_attribute }, + { "trapa_handler", 0, 0, true, false, false, sh_handle_interrupt_handler_attribute }, + { "nosave_low_regs", 0, 0, true, false, false, sh_handle_interrupt_handler_attribute }, #ifdef SYMBIAN /* Symbian support adds three new attributes: dllexport - for exporting a function/variable that will live in a dll @@ -7557,13 +7592,6 @@ sh_handle_sp_switch_attribute (tree *node, tree name, tree args, IDENTIFIER_POINTER (name)); *no_add_attrs = true; } - else if (!pragma_interrupt) - { - /* The sp_switch attribute only has meaning for interrupt functions. */ - warning (OPT_Wattributes, "%qs attribute only applies to " - "interrupt functions", IDENTIFIER_POINTER (name)); - *no_add_attrs = true; - } else if (TREE_CODE (TREE_VALUE (args)) != STRING_CST) { /* The argument must be a constant string. */ @@ -7571,11 +7599,6 @@ sh_handle_sp_switch_attribute (tree *node, tree name, tree args, IDENTIFIER_POINTER (name)); *no_add_attrs = true; } - else - { - const char *s = ggc_strdup (TREE_STRING_POINTER (TREE_VALUE (args))); - sp_switch = gen_rtx_SYMBOL_REF (VOIDmode, s); - } return NULL_TREE; } @@ -7592,13 +7615,8 @@ sh_handle_trap_exit_attribute (tree *node, tree name, tree args, IDENTIFIER_POINTER (name)); *no_add_attrs = true; } - else if (!pragma_interrupt) - { - /* The trap_exit attribute only has meaning for interrupt functions. */ - warning (OPT_Wattributes, "%qs attribute only applies to " - "interrupt functions", IDENTIFIER_POINTER (name)); - *no_add_attrs = true; - } + /* The argument specifies a trap number to be used in a trapa instruction + at function exit (instead of an rte instruction). */ else if (TREE_CODE (TREE_VALUE (args)) != INTEGER_CST) { /* The argument must be a constant integer. */ @@ -7606,10 +7624,6 @@ sh_handle_trap_exit_attribute (tree *node, tree name, tree args, "integer constant", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } - else - { - trap_exit = TREE_INT_CST_LOW (TREE_VALUE (args)); - } return NULL_TREE; } diff --git a/gcc/config/sh/sh.h b/gcc/config/sh/sh.h index 87117f45b4c..72cd56391bb 100644 --- a/gcc/config/sh/sh.h +++ b/gcc/config/sh/sh.h @@ -3277,18 +3277,13 @@ extern enum mdep_reorg_phase_e mdep_reorg_phase; c_register_pragma (0, "nosave_low_regs", sh_pr_nosave_low_regs); \ } while (0) -/* Set when processing a function with pragma interrupt turned on. */ - -extern int pragma_interrupt; +extern tree sh_deferred_function_attributes; +extern tree *sh_deferred_function_attributes_tail; /* Set when processing a function with interrupt attribute. */ extern int current_function_interrupt; -/* Set to an RTX containing the address of the stack to switch to - for interrupt functions. */ -extern struct rtx_def *sp_switch; - /* Instructions with unfilled delay slots take up an extra two bytes for the nop in the delay slot. diff --git a/gcc/config/sh/sh.md b/gcc/config/sh/sh.md index 38cd3bd6c6e..e2e477f2a57 100644 --- a/gcc/config/sh/sh.md +++ b/gcc/config/sh/sh.md @@ -8781,11 +8781,21 @@ mov.l\\t1f,r0\\n\\ "TARGET_SH1 && ! (TARGET_SHCOMPACT && (current_function_args_info.call_cookie & CALL_COOKIE_RET_TRAMP (1))) - && reload_completed" + && reload_completed + && lookup_attribute (\"trap_exit\", + DECL_ATTRIBUTES (current_function_decl)) == NULL_TREE" "%@ %#" [(set_attr "type" "return") (set_attr "needs_delay_slot" "yes")]) +;; trapa has no delay slot. +(define_insn "*return_trapa" + [(return)] + "TARGET_SH1 && !TARGET_SHCOMPACT + && reload_completed" + "%@" + [(set_attr "type" "return")]) + (define_expand "shcompact_return_tramp" [(return)] "TARGET_SHCOMPACT @@ -11209,15 +11219,12 @@ mov.l\\t1f,r0\\n\\ ;; Switch to a new stack with its address in sp_switch (a SYMBOL_REF). */ (define_insn "sp_switch_1" - [(const_int 1)] + [(const_int 1) (match_operand:SI 0 "symbol_ref_operand" "s")] "TARGET_SH1" "* { - rtx xoperands[1]; - - xoperands[0] = sp_switch; - output_asm_insn (\"mov.l r0,@-r15\;mov.l %0,r0\", xoperands); - output_asm_insn (\"mov.l @r0,r0\;mov.l r15,@-r0\", xoperands); + output_asm_insn (\"mov.l r0,@-r15\;mov.l %0,r0\", operands); + output_asm_insn (\"mov.l @r0,r0\;mov.l r15,@-r0\", operands); return \"mov r0,r15\"; }" [(set_attr "length" "10")]) diff --git a/gcc/config/sh/t-sh b/gcc/config/sh/t-sh index c5eb397b142..db86ad18c1d 100644 --- a/gcc/config/sh/t-sh +++ b/gcc/config/sh/t-sh @@ -1,3 +1,7 @@ +sh-c.o: $(srcdir)/config/sh/sh-c.c \ + $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(TM_H) $(TM_P_H) coretypes.h + $(CC) -c $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $(srcdir)/config/sh/sh-c.c + LIB1ASMSRC = sh/lib1funcs.asm LIB1ASMFUNCS = _ashiftrt _ashiftrt_n _ashiftlt _lshiftrt _movmem \ _movmem_i4 _mulsi3 _sdivsi3 _sdivsi3_i4 _udivsi3 _udivsi3_i4 _set_fpscr \ |