From: Claudiu Zissulescu Date: Wed, 24 Jul 2019 12:21:38 +0000 (+0200) Subject: [ARC] Fix and refurbish the interrupts. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=ce9dbf20f4e1394121d5d875e502731a9db4f7a1;p=gcc.git [ARC] Fix and refurbish the interrupts. When entering an interrupt, not only the call save registers needs to be place on stack but also the call clobbers one. More over, the ARC700 return from interrupt instruction needs to be rtie, the same like ARCv2 CPUs. While the ARC6xx family uses j.f [ilinkX] instruction. Additionally, we need to save the state of the ZOL machinery, namely the lp_count, lp_end and lp_start registers. For architectures which are using extension registers (i.e., HS48) we need to save/restore them as well. gcc/ xxxx-xx-xx Claudiu Zissulescu * config/arc/arc-protos.h (arc_output_function_epilogue): Delete declaration. (arc_compute_frame_size): Millicode is disabled when compiling ISR. (arc_return_address_register): Likewise. (arc_compute_function_type): Likewise. (arc_compute_frame_size): Likewise. (secondary_reload_info): Likewise. (arc_get_unalign): Likewise. (arc_can_use_return_insn): Declare. * config/arc/arc.c (AUX_LP_START): Define (AUX_LP_END): Likewise. (arc_frame_info): Update gmask member to 64-bit datum. (GMASK_LEN): Update. (arc_compute_function_type): Make it static, move it forward. (arc_must_save_register): Update, consider the extra regs. (arc_compute_millicode_save_restore_regs): Update to use the 64 bit gmask. (arc_compute_frame_size): Likewise. (arc_enter_leave_p): Likewise. (arc_save_callee_saves): Likewise. (arc_restore_callee_saves): Likewise. (arc_save_callee_enter): Likewise. (arc_restore_callee_leave): Likewise. (arc_save_callee_milli): Likewise. (arc_restore_callee_milli): Likewise. (arc_expand_prologue): Add new interrupt handling. (arc_return_address_register): Make it static, move it forward. (arc_expand_epilogue): Add new interrupt handling. (arc_get_unalign): Delete. (arc_epilogue_uses): Make sure we do not remove the extra saved/restored registers when interrupt. (arc_can_use_return_insn): New function. (push_reg): Likewise. (pop_reg): Likewise. (arc_save_callee_saves): Add ZOL and FPX aux registers saving procedures. (arc_restore_callee_saves): Likewise, but restoring. * config/arc/arc.md (VUNSPEC_ARC_ARC600_RTIE): Define. (R33_REG): Likewise. (R34_REG): Likewise. (R35_REG): Likewise. (R36_REG): Likewise. (R37_REG): Likewise. (R38_REG): Likewise. (R39_REG): Likewise. (R45_REG): Likewise. (R46_REG): Likewise. (R47_REG): Likewise. (R48_REG): Likewise. (R49_REG): Likewise. (R50_REG): Likewise. (R51_REG): Likewise. (R52_REG): Likewise. (R53_REG): Likewise. (R54_REG): Likewise. (R55_REG): Likewise. (R56_REG): Likewise. (R58_REG): Likewise. (type): Add rtie attribute. (in_call_delay_slot): Use RETURN_ADDR_REGNUM. (movsi_insn): Accept moves to lp_count. (rtie): Update pattern. (simple_return): Simplify it, don't use this pattern as a return from an interrupt. (arc600_rtie): New pattern. (p_return_i): Clean up. (return): Likewise. * config/arc/builtins.def (rtie): Only available for non ARC6xx family CPUs. * config/arc/predicates.md (move_src_operand): Consider lp_count as a register. gcc/testsuite xxxx-xx-xx Claudiu Zissulescu * gcc.target/arc/arc.exp (check_effective_target_accregs): New predicate. * gcc.target/arc/builtin_special.c: Update test/ * gcc.target/arc/interrupt-1.c: Likewise. * gcc.target/arc/interrupt-10.c: New test. * gcc.target/arc/interrupt-11.c: Likewise. * gcc.target/arc/interrupt-12.c: Likewise. update From-SVN: r273761 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index c4633837799..5a94c2fdd6a 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,78 @@ +2019-07-24 Claudiu Zissulescu + + * config/arc/arc-protos.h (arc_output_function_epilogue): Delete + declaration. + (arc_compute_frame_size): Millicode is disabled when compiling + ISR. + (arc_return_address_register): Likewise. + (arc_compute_function_type): Likewise. + (arc_compute_frame_size): Likewise. + (secondary_reload_info): Likewise. + (arc_get_unalign): Likewise. + (arc_can_use_return_insn): Declare. + * config/arc/arc.c (AUX_LP_START): Define + (AUX_LP_END): Likewise. + (arc_frame_info): Update gmask member to 64-bit datum. + (GMASK_LEN): Update. + (arc_compute_function_type): Make it static, move it forward. + (arc_must_save_register): Update, consider the extra regs. + (arc_compute_millicode_save_restore_regs): Update to use the 64 + bit gmask. + (arc_compute_frame_size): Likewise. + (arc_enter_leave_p): Likewise. + (arc_save_callee_saves): Likewise. + (arc_restore_callee_saves): Likewise. + (arc_save_callee_enter): Likewise. + (arc_restore_callee_leave): Likewise. + (arc_save_callee_milli): Likewise. + (arc_restore_callee_milli): Likewise. + (arc_expand_prologue): Add new interrupt handling. + (arc_return_address_register): Make it static, move it forward. + (arc_expand_epilogue): Add new interrupt handling. + (arc_get_unalign): Delete. + (arc_epilogue_uses): Make sure we do not remove the extra + saved/restored registers when interrupt. + (arc_can_use_return_insn): New function. + (push_reg): Likewise. + (pop_reg): Likewise. + (arc_save_callee_saves): Add ZOL and FPX aux registers saving + procedures. + (arc_restore_callee_saves): Likewise, but restoring. + * config/arc/arc.md (VUNSPEC_ARC_ARC600_RTIE): Define. + (R33_REG): Likewise. + (R34_REG): Likewise. + (R35_REG): Likewise. + (R36_REG): Likewise. + (R37_REG): Likewise. + (R38_REG): Likewise. + (R39_REG): Likewise. + (R45_REG): Likewise. + (R46_REG): Likewise. + (R47_REG): Likewise. + (R48_REG): Likewise. + (R49_REG): Likewise. + (R50_REG): Likewise. + (R51_REG): Likewise. + (R52_REG): Likewise. + (R53_REG): Likewise. + (R54_REG): Likewise. + (R55_REG): Likewise. + (R56_REG): Likewise. + (R58_REG): Likewise. + (type): Add rtie attribute. + (in_call_delay_slot): Use RETURN_ADDR_REGNUM. + (movsi_insn): Accept moves to lp_count. + (rtie): Update pattern. + (simple_return): Simplify it, don't use this pattern as a return + from an interrupt. + (arc600_rtie): New pattern. + (p_return_i): Clean up. + (return): Likewise. + * config/arc/builtins.def (rtie): Only available for non ARC6xx + family CPUs. + * config/arc/predicates.md (move_src_operand): Consider lp_count + as a register. + 2019-07-24 Andreas Krebbel * config/s390/predicates.md (addv_const_operand): New predicate. diff --git a/gcc/config/arc/arc-protos.h b/gcc/config/arc/arc-protos.h index 74e52475066..1220e77206d 100644 --- a/gcc/config/arc/arc-protos.h +++ b/gcc/config/arc/arc-protos.h @@ -25,7 +25,6 @@ extern machine_mode arc_select_cc_mode (enum rtx_code, rtx, rtx); extern struct rtx_def *gen_compare_reg (rtx, machine_mode); /* Declarations for various fns used in the .md file. */ -extern void arc_output_function_epilogue (FILE *, HOST_WIDE_INT, int); extern const char *output_shift (rtx *); extern bool compact_sda_memory_operand (rtx, machine_mode, bool); extern bool arc_double_limm_p (rtx); @@ -42,8 +41,6 @@ extern void arc_expand_atomic_op (enum rtx_code, rtx, rtx, rtx, rtx, rtx); extern void arc_split_compare_and_swap (rtx *); extern void arc_expand_compare_and_swap (rtx *); extern bool compact_memory_operand_p (rtx, machine_mode, bool, bool); -extern int arc_return_address_register (unsigned int); -extern unsigned int arc_compute_function_type (struct function *); extern bool arc_is_uncached_mem_p (rtx); extern bool gen_operands_ldd_std (rtx *operands, bool load, bool commute); extern bool arc_check_multi (rtx, bool); @@ -52,9 +49,9 @@ extern bool arc_check_ior_const (HOST_WIDE_INT ); extern void arc_split_ior (rtx *); extern bool arc_check_mov_const (HOST_WIDE_INT ); extern bool arc_split_mov_const (rtx *); +extern bool arc_can_use_return_insn (void); #endif /* RTX_CODE */ -extern unsigned int arc_compute_frame_size (int); extern bool arc_ccfsm_branch_deleted_p (void); extern void arc_ccfsm_record_branch_deleted (void); @@ -71,7 +68,6 @@ extern bool arc_is_longcall_p (rtx); extern bool arc_is_shortcall_p (rtx); extern bool valid_brcc_with_delay_p (rtx *); extern bool arc_ccfsm_cond_exec_p (void); -struct secondary_reload_info; extern rtx disi_highpart (rtx); extern int arc_adjust_insn_length (rtx_insn *, int, bool); extern int arc_corereg_hazard (rtx, rtx); @@ -89,7 +85,6 @@ extern void arc_expand_prologue (void); extern void arc_expand_epilogue (int); extern void arc_init_expanders (void); extern int arc_check_millicode (rtx op, int offset, int load_p); -extern int arc_get_unalign (void); extern void arc_clear_unalign (void); extern void arc_toggle_unalign (void); extern void split_addsi (rtx *); diff --git a/gcc/config/arc/arc.c b/gcc/config/arc/arc.c index 71e65767ee1..3c4d89cdd08 100644 --- a/gcc/config/arc/arc.c +++ b/gcc/config/arc/arc.c @@ -206,6 +206,13 @@ static int rgf_banked_register_count; this to be no less than the 1/p */ #define MAX_INSNS_SKIPPED 3 +/* ZOL control registers. */ +#define AUX_LP_START 0x02 +#define AUX_LP_END 0x03 + +/* FPX AUX registers. */ +#define AUX_DPFP_START 0x301 + /* A nop is needed between a 4 byte insn that sets the condition codes and a branch that uses them (the same isn't true for an 8 byte insn that sets the condition codes). Set by arc_ccfsm_advance. Used by @@ -298,6 +305,129 @@ static bool arc_use_by_pieces_infrastructure_p (unsigned HOST_WIDE_INT, /* Globally visible information about currently selected cpu. */ const arc_cpu_t *arc_selected_cpu; +/* Traditionally, we push saved registers first in the prologue, + then we allocate the rest of the frame - and reverse in the epilogue. + This has still its merits for ease of debugging, or saving code size + or even execution time if the stack frame is so large that some accesses + can't be encoded anymore with offsets in the instruction code when using + a different scheme. + Also, it would be a good starting point if we got instructions to help + with register save/restore. + + However, often stack frames are small, and the pushing / popping has + some costs: + - the stack modification prevents a lot of scheduling. + - frame allocation / deallocation may need extra instructions. + - we need to place a memory barrier after frame allocation to avoid + the delay slot scheduler to reschedule a frame related info and + messing up with dwarf unwinding. The barrier before deallocation + is for flushing all pending sp operations. + + Thus, for small frames, we'd like to use a different scheme: + - The frame is allocated in full with the first prologue instruction, + and deallocated in full with the last epilogue instruction. + Thus, the instructions in-between can be freely scheduled. + - If the function has no outgoing arguments on the stack, we can allocate + one register save slot at the top of the stack. This register can then + be saved simultaneously with frame allocation, and restored with + frame deallocation. + This register can be picked depending on scheduling considerations, + although same though should go into having some set of registers + to be potentially lingering after a call, and others to be available + immediately - i.e. in the absence of interprocedual optimization, we + can use an ABI-like convention for register allocation to reduce + stalls after function return. */ + +/* ARCompact stack frames look like: + + Before call After call + high +-----------------------+ +-----------------------+ + mem | reg parm save area | | reg parm save area | + | only created for | | only created for | + | variable arg fns | | variable arg fns | + AP +-----------------------+ +-----------------------+ + | return addr register | | return addr register | + | (if required) | | (if required) | + +-----------------------+ +-----------------------+ + | | | | + | reg save area | | reg save area | + | | | | + +-----------------------+ +-----------------------+ + | frame pointer | | frame pointer | + | (if required) | | (if required) | + FP +-----------------------+ +-----------------------+ + | | | | + | local/temp variables | | local/temp variables | + | | | | + +-----------------------+ +-----------------------+ + | | | | + | arguments on stack | | arguments on stack | + | | | | + SP +-----------------------+ +-----------------------+ + | reg parm save area | + | only created for | + | variable arg fns | + AP +-----------------------+ + | return addr register | + | (if required) | + +-----------------------+ + | | + | reg save area | + | | + +-----------------------+ + | frame pointer | + | (if required) | + FP +-----------------------+ + | | + | local/temp variables | + | | + +-----------------------+ + | | + | arguments on stack | + low | | + mem SP +-----------------------+ + +Notes: +1) The "reg parm save area" does not exist for non variable argument fns. + The "reg parm save area" can be eliminated completely if we created our + own va-arc.h, but that has tradeoffs as well (so it's not done). */ + +/* Structure to be filled in by arc_compute_frame_size with register + save masks, and offsets for the current function. */ +struct GTY (()) arc_frame_info +{ + unsigned int total_size; /* # bytes that the entire frame takes up. */ + unsigned int extra_size; /* # bytes of extra stuff. */ + unsigned int pretend_size; /* # bytes we push and pretend caller did. */ + unsigned int args_size; /* # bytes that outgoing arguments take up. */ + unsigned int reg_size; /* # bytes needed to store regs. */ + unsigned int var_size; /* # bytes that variables take up. */ + uint64_t gmask; /* Mask of saved gp registers. */ + bool initialized; /* FALSE if frame size already calculated. */ + short millicode_start_reg; + short millicode_end_reg; + bool save_return_addr; +}; + +/* GMASK bit length -1. */ +#define GMASK_LEN 63 + +/* Defining data structures for per-function information. */ + +typedef struct GTY (()) machine_function +{ + unsigned int fn_type; + struct arc_frame_info frame_info; + /* To keep track of unalignment caused by short insns. */ + int unalign; + struct arc_ccfsm ccfsm_current; + /* Map from uid to ccfsm state during branch shortening. */ + rtx ccfsm_current_insn; + char arc_reorg_started; + char prescan_initialized; +} machine_function; + + /* Given a symbol RTX (const (symb <+ const_int>), returns its alignment. */ @@ -1996,6 +2126,50 @@ arc_handle_fndecl_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, return NULL_TREE; } +/* Type of function DECL. + + The result is cached. To reset the cache at the end of a function, + call with DECL = NULL_TREE. */ + +static unsigned int +arc_compute_function_type (struct function *fun) +{ + tree attr, decl = fun->decl; + unsigned int fn_type = fun->machine->fn_type; + + if (fn_type != ARC_FUNCTION_UNKNOWN) + return fn_type; + + /* Check if it is a naked function. */ + if (lookup_attribute ("naked", DECL_ATTRIBUTES (decl)) != NULL_TREE) + fn_type |= ARC_FUNCTION_NAKED; + else + fn_type |= ARC_FUNCTION_NORMAL; + + /* Now see if this is an interrupt handler. */ + attr = lookup_attribute ("interrupt", DECL_ATTRIBUTES (decl)); + if (attr != NULL_TREE) + { + tree value, args = TREE_VALUE (attr); + + gcc_assert (list_length (args) == 1); + value = TREE_VALUE (args); + gcc_assert (TREE_CODE (value) == STRING_CST); + + if (!strcmp (TREE_STRING_POINTER (value), "ilink1") + || !strcmp (TREE_STRING_POINTER (value), "ilink")) + fn_type |= ARC_FUNCTION_ILINK1; + else if (!strcmp (TREE_STRING_POINTER (value), "ilink2")) + fn_type |= ARC_FUNCTION_ILINK2; + else if (!strcmp (TREE_STRING_POINTER (value), "firq")) + fn_type |= ARC_FUNCTION_FIRQ; + else + gcc_unreachable (); + } + + return fun->machine->fn_type = fn_type; +} + /* Implement `TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS' */ static bool @@ -2402,174 +2576,6 @@ frame_stack_add (HOST_WIDE_INT offset) return frame_add (stack_pointer_rtx, offset); } -/* Traditionally, we push saved registers first in the prologue, - then we allocate the rest of the frame - and reverse in the epilogue. - This has still its merits for ease of debugging, or saving code size - or even execution time if the stack frame is so large that some accesses - can't be encoded anymore with offsets in the instruction code when using - a different scheme. - Also, it would be a good starting point if we got instructions to help - with register save/restore. - - However, often stack frames are small, and the pushing / popping has - some costs: - - the stack modification prevents a lot of scheduling. - - frame allocation / deallocation needs extra instructions. - - unless we know that we compile ARC700 user code, we need to put - a memory barrier after frame allocation / before deallocation to - prevent interrupts clobbering our data in the frame. - In particular, we don't have any such guarantees for library functions, - which tend to, on the other hand, to have small frames. - - Thus, for small frames, we'd like to use a different scheme: - - The frame is allocated in full with the first prologue instruction, - and deallocated in full with the last epilogue instruction. - Thus, the instructions in-betwen can be freely scheduled. - - If the function has no outgoing arguments on the stack, we can allocate - one register save slot at the top of the stack. This register can then - be saved simultanously with frame allocation, and restored with - frame deallocation. - This register can be picked depending on scheduling considerations, - although same though should go into having some set of registers - to be potentially lingering after a call, and others to be available - immediately - i.e. in the absence of interprocedual optimization, we - can use an ABI-like convention for register allocation to reduce - stalls after function return. */ -/* Function prologue/epilogue handlers. */ - -/* ARCompact stack frames look like: - - Before call After call - high +-----------------------+ +-----------------------+ - mem | reg parm save area | | reg parm save area | - | only created for | | only created for | - | variable arg fns | | variable arg fns | - AP +-----------------------+ +-----------------------+ - | return addr register | | return addr register | - | (if required) | | (if required) | - +-----------------------+ +-----------------------+ - | | | | - | reg save area | | reg save area | - | | | | - +-----------------------+ +-----------------------+ - | frame pointer | | frame pointer | - | (if required) | | (if required) | - FP +-----------------------+ +-----------------------+ - | | | | - | local/temp variables | | local/temp variables | - | | | | - +-----------------------+ +-----------------------+ - | | | | - | arguments on stack | | arguments on stack | - | | | | - SP +-----------------------+ +-----------------------+ - | reg parm save area | - | only created for | - | variable arg fns | - AP +-----------------------+ - | return addr register | - | (if required) | - +-----------------------+ - | | - | reg save area | - | | - +-----------------------+ - | frame pointer | - | (if required) | - FP +-----------------------+ - | | - | local/temp variables | - | | - +-----------------------+ - | | - | arguments on stack | - low | | - mem SP +-----------------------+ - -Notes: -1) The "reg parm save area" does not exist for non variable argument fns. - The "reg parm save area" can be eliminated completely if we created our - own va-arc.h, but that has tradeoffs as well (so it's not done). */ - -/* Structure to be filled in by arc_compute_frame_size with register - save masks, and offsets for the current function. */ -struct GTY (()) arc_frame_info -{ - unsigned int total_size; /* # bytes that the entire frame takes up. */ - unsigned int extra_size; /* # bytes of extra stuff. */ - unsigned int pretend_size; /* # bytes we push and pretend caller did. */ - unsigned int args_size; /* # bytes that outgoing arguments take up. */ - unsigned int reg_size; /* # bytes needed to store regs. */ - unsigned int var_size; /* # bytes that variables take up. */ - unsigned int gmask; /* Mask of saved gp registers. */ - bool initialized; /* FALSE if frame size already calculated. */ - short millicode_start_reg; - short millicode_end_reg; - bool save_return_addr; -}; - -/* GMASK bit length -1. */ -#define GMASK_LEN 31 - -/* Defining data structures for per-function information. */ - -typedef struct GTY (()) machine_function -{ - unsigned int fn_type; - struct arc_frame_info frame_info; - /* To keep track of unalignment caused by short insns. */ - int unalign; - struct arc_ccfsm ccfsm_current; - /* Map from uid to ccfsm state during branch shortening. */ - rtx ccfsm_current_insn; - char arc_reorg_started; - char prescan_initialized; -} machine_function; - -/* Type of function DECL. - - The result is cached. To reset the cache at the end of a function, - call with DECL = NULL_TREE. */ - -unsigned int -arc_compute_function_type (struct function *fun) -{ - tree attr, decl = fun->decl; - unsigned int fn_type = fun->machine->fn_type; - - if (fn_type != ARC_FUNCTION_UNKNOWN) - return fn_type; - - /* Check if it is a naked function. */ - if (lookup_attribute ("naked", DECL_ATTRIBUTES (decl)) != NULL_TREE) - fn_type |= ARC_FUNCTION_NAKED; - else - fn_type |= ARC_FUNCTION_NORMAL; - - /* Now see if this is an interrupt handler. */ - attr = lookup_attribute ("interrupt", DECL_ATTRIBUTES (decl)); - if (attr != NULL_TREE) - { - tree value, args = TREE_VALUE (attr); - - gcc_assert (list_length (args) == 1); - value = TREE_VALUE (args); - gcc_assert (TREE_CODE (value) == STRING_CST); - - if (!strcmp (TREE_STRING_POINTER (value), "ilink1") - || !strcmp (TREE_STRING_POINTER (value), "ilink")) - fn_type |= ARC_FUNCTION_ILINK1; - else if (!strcmp (TREE_STRING_POINTER (value), "ilink2")) - fn_type |= ARC_FUNCTION_ILINK2; - else if (!strcmp (TREE_STRING_POINTER (value), "firq")) - fn_type |= ARC_FUNCTION_FIRQ; - else - gcc_unreachable (); - } - - return fun->machine->fn_type = fn_type; -} - /* Helper function to wrap FRAME_POINTER_NEEDED. We do this as FRAME_POINTER_NEEDED will not be true until the IRA (Integrated Register Allocator) pass, while we want to get the frame size @@ -2619,14 +2625,12 @@ arc_frame_pointer_needed (void) } /* Tell prologue and epilogue if register REGNO should be saved / - restored. The return address, stack pointer and frame pointer are - treated separately. Don't consider them here. Addition for pic: - The gp register needs to be saved if the current function changes - it to access gotoff variables. FIXME: This will not be needed if - we used some arbitrary register instead of r26. */ + restored. The SPECIAL_P is true when the register may need special + ld/st sequence. The return address, and stack pointer are treated + separately. Don't consider them here. */ static bool -arc_must_save_register (int regno, struct function *func) +arc_must_save_register (int regno, struct function *func, bool special_p) { unsigned int fn_type = arc_compute_function_type (func); bool irq_auto_save_p = ((irq_ctrl_saved.irq_save_last_reg >= regno) @@ -2656,8 +2660,69 @@ arc_must_save_register (int regno, struct function *func) switch (regno) { + case ILINK1_REG: case RETURN_ADDR_REGNUM: case STACK_POINTER_REGNUM: + /* The stack pointer and the return address are handled + separately. */ + return false; + + case R30_REG: + /* r30 is either used as ilink2 by ARCv1 or as a free register + by ARCv2. */ + if (!TARGET_V2) + return false; + break; + + case R40_REG: + case R41_REG: + case R42_REG: + case R43_REG: + case R44_REG: + /* If those ones are used by the FPX machinery, we handle them + separately. */ + if (TARGET_DPFP && !special_p) + return false; + /* FALLTHRU. */ + + case R32_REG: + case R33_REG: + case R34_REG: + case R35_REG: + case R36_REG: + case R37_REG: + case R38_REG: + case R39_REG: + case R45_REG: + case R46_REG: + case R47_REG: + case R48_REG: + case R49_REG: + case R50_REG: + case R51_REG: + case R52_REG: + case R53_REG: + case R54_REG: + case R55_REG: + case R56_REG: + case R57_REG: + case R58_REG: + case R59_REG: + /* The Extension Registers. */ + if (ARC_INTERRUPT_P (fn_type) + && (df_regs_ever_live_p (RETURN_ADDR_REGNUM) + || df_regs_ever_live_p (regno)) + /* Not all extension registers are available, choose the + real ones. */ + && !fixed_regs[regno]) + return true; + return false; + + case 61: + case 62: + case 63: + /* Fixed/control register, nothing to do. LP_COUNT is + different. */ return false; case HARD_FRAME_POINTER_REGNUM: @@ -2665,18 +2730,21 @@ arc_must_save_register (int regno, struct function *func) regular reg. */ if (arc_frame_pointer_needed ()) return false; + break; - /* FALLTHRU */ default: - if (df_regs_ever_live_p (regno) - && (!call_used_regs[regno] - || ARC_INTERRUPT_P (fn_type)) - /* Do not emit code for auto saved regs. */ - && !irq_auto_save_p - && !firq_auto_save_p) - return true; + break; } + if (((df_regs_ever_live_p (regno) && !call_used_regs[regno]) + /* In an interrupt save everything. */ + || (ARC_INTERRUPT_P (fn_type) + && (df_regs_ever_live_p (RETURN_ADDR_REGNUM) + || df_regs_ever_live_p (regno)))) + /* Do not emit code for auto saved regs. */ + && !irq_auto_save_p + && !firq_auto_save_p) + return true; return false; } @@ -2700,14 +2768,14 @@ arc_must_save_return_addr (struct function *func) of registers to be saved / restored with a millicode call. */ static int -arc_compute_millicode_save_restore_regs (unsigned int gmask, +arc_compute_millicode_save_restore_regs (uint64_t gmask, struct arc_frame_info *frame) { int regno; int start_reg = 13, end_reg = 25; - for (regno = start_reg; regno <= end_reg && (gmask & (1L << regno));) + for (regno = start_reg; regno <= end_reg && (gmask & (1ULL << regno));) regno++; end_reg = regno - 1; /* There is no point in using millicode thunks if we don't save/restore @@ -2731,7 +2799,7 @@ arc_compute_frame_size (void) int regno; unsigned int total_size, var_size, args_size, pretend_size, extra_size; unsigned int reg_size; - unsigned int gmask; + uint64_t gmask; struct arc_frame_info *frame_info; int size; unsigned int extra_plus_reg_size; @@ -2759,12 +2827,13 @@ arc_compute_frame_size (void) reg_size = 0; gmask = 0; - for (regno = 0; regno <= 31; regno++) + /* The last 4 regs are special, avoid them. */ + for (regno = 0; regno <= (GMASK_LEN - 4); regno++) { - if (arc_must_save_register (regno, cfun)) + if (arc_must_save_register (regno, cfun, false)) { reg_size += UNITS_PER_WORD; - gmask |= 1L << regno; + gmask |= 1ULL << regno; } } @@ -2779,7 +2848,7 @@ arc_compute_frame_size (void) for (regno = 0; EH_RETURN_DATA_REGNO (regno) != INVALID_REGNUM; regno++) { reg_size += UNITS_PER_WORD; - gmask |= 1L << regno; + gmask |= 1ULL << regno; } /* Check if we need to save the return address. */ @@ -2789,12 +2858,25 @@ arc_compute_frame_size (void) /* Saving blink reg for millicode thunk calls. */ if (TARGET_MILLICODE_THUNK_SET - && !crtl->calls_eh_return) + && !crtl->calls_eh_return + && !ARC_INTERRUPT_P (arc_compute_function_type (cfun))) { if (arc_compute_millicode_save_restore_regs (gmask, frame_info)) frame_info->save_return_addr = true; } + /* Save lp_count, lp_start and lp_end. */ + if (arc_lpcwidth != 0 && arc_must_save_register (LP_COUNT, cfun, true)) + reg_size += UNITS_PER_WORD * 3; + + /* Check for the special R40-R44 regs used by FPX extension. */ + if (arc_must_save_register (TARGET_BIG_ENDIAN ? R41_REG : R40_REG, + cfun, TARGET_DPFP)) + reg_size += UNITS_PER_WORD * 2; + if (arc_must_save_register (TARGET_BIG_ENDIAN ? R43_REG : R42_REG, + cfun, TARGET_DPFP)) + reg_size += UNITS_PER_WORD * 2; + /* 4) Calculate extra size made up of the blink + fp size. */ extra_size = 0; if (arc_must_save_return_addr (cfun)) @@ -2929,6 +3011,22 @@ frame_save_reg (rtx reg, HOST_WIDE_INT offset) return GET_MODE_SIZE (GET_MODE (reg)) - offset; } +/* Helper used when saving AUX regs during ISR. */ + +static int +push_reg (rtx reg) +{ + rtx stkslot = gen_rtx_MEM (GET_MODE (reg), gen_rtx_PRE_DEC (Pmode, + stack_pointer_rtx)); + rtx insn = emit_move_insn (stkslot, reg); + RTX_FRAME_RELATED_P (insn) = 1; + add_reg_note (insn, REG_CFA_ADJUST_CFA, + gen_rtx_SET (stack_pointer_rtx, + plus_constant (Pmode, stack_pointer_rtx, + -GET_MODE_SIZE (GET_MODE (reg))))); + return GET_MODE_SIZE (GET_MODE (reg)); +} + /* Helper for epilogue: emit frame load with post_modify or post_inc to restore register REG from stack. The initial offset is passed via OFFSET. */ @@ -2967,12 +3065,29 @@ frame_restore_reg (rtx reg, HOST_WIDE_INT offset) return GET_MODE_SIZE (GET_MODE (reg)) + offset; } +/* Helper used when restoring AUX regs during ISR. */ + +static int +pop_reg (rtx reg) +{ + rtx stkslot = gen_rtx_MEM (GET_MODE (reg), gen_rtx_POST_INC (Pmode, + stack_pointer_rtx)); + rtx insn = emit_move_insn (reg, stkslot); + RTX_FRAME_RELATED_P (insn) = 1; + add_reg_note (insn, REG_CFA_ADJUST_CFA, + gen_rtx_SET (stack_pointer_rtx, + plus_constant (Pmode, stack_pointer_rtx, + GET_MODE_SIZE (GET_MODE (reg))))); + return GET_MODE_SIZE (GET_MODE (reg)); +} + + /* Check if we have a continous range to be save/restored with the help of enter/leave instructions. A vaild register range starts from $r13 and is up to (including) $r26. */ static bool -arc_enter_leave_p (unsigned int gmask) +arc_enter_leave_p (uint64_t gmask) { int regno; unsigned int rmask = 0; @@ -2981,8 +3096,8 @@ arc_enter_leave_p (unsigned int gmask) return false; for (regno = ENTER_LEAVE_START_REG; - regno <= ENTER_LEAVE_END_REG && (gmask & (1L << regno)); regno++) - rmask |= 1L << regno; + regno <= ENTER_LEAVE_END_REG && (gmask & (1ULL << regno)); regno++) + rmask |= 1ULL << regno; if (rmask ^ gmask) return false; @@ -2995,13 +3110,14 @@ arc_enter_leave_p (unsigned int gmask) instructions. */ static int -arc_save_callee_saves (unsigned int gmask, +arc_save_callee_saves (uint64_t gmask, bool save_blink, bool save_fp, HOST_WIDE_INT offset) { rtx reg; int frame_allocated = 0; + int i; /* The home-grown ABI says link register is saved first. */ if (save_blink) @@ -3013,19 +3129,19 @@ arc_save_callee_saves (unsigned int gmask, /* N.B. FRAME_POINTER_MASK and RETURN_ADDR_MASK are cleared in gmask. */ if (gmask) - for (int i = 31; i >= 0; i--) + for (i = GMASK_LEN; i >= 0; i--) { machine_mode save_mode = SImode; if (TARGET_LL64 && ((i - 1) % 2 == 0) - && ((gmask & (1L << i)) != 0) - && ((gmask & (1L << (i - 1))) != 0)) + && ((gmask & (1ULL << i)) != 0) + && ((gmask & (1ULL << (i - 1))) != 0)) { save_mode = DImode; --i; } - else if ((gmask & (1L << i)) == 0) + else if ((gmask & (1ULL << i)) == 0) continue; reg = gen_rtx_REG (save_mode, i); @@ -3033,6 +3149,41 @@ arc_save_callee_saves (unsigned int gmask, offset = 0; } + /* Check if we need to save the ZOL machinery. */ + if (arc_lpcwidth != 0 && arc_must_save_register (LP_COUNT, cfun, true)) + { + rtx reg0 = gen_rtx_REG (SImode, R0_REG); + emit_insn (gen_rtx_SET (reg0, + gen_rtx_UNSPEC_VOLATILE + (Pmode, gen_rtvec (1, GEN_INT (AUX_LP_START)), + VUNSPEC_ARC_LR))); + frame_allocated += push_reg (reg0); + emit_insn (gen_rtx_SET (reg0, + gen_rtx_UNSPEC_VOLATILE + (Pmode, gen_rtvec (1, GEN_INT (AUX_LP_END)), + VUNSPEC_ARC_LR))); + frame_allocated += push_reg (reg0); + emit_move_insn (reg0, gen_rtx_REG (SImode, LP_COUNT)); + frame_allocated += push_reg (reg0); + } + + /* Save AUX regs used by FPX machinery. */ + if (arc_must_save_register (TARGET_BIG_ENDIAN ? R41_REG : R40_REG, + cfun, TARGET_DPFP)) + { + rtx reg0 = gen_rtx_REG (SImode, R0_REG); + + for (i = 0; i < 4; i++) + { + emit_insn (gen_rtx_SET (reg0, + gen_rtx_UNSPEC_VOLATILE + (Pmode, gen_rtvec (1, GEN_INT (AUX_DPFP_START + + i)), + VUNSPEC_ARC_LR))); + frame_allocated += push_reg (reg0); + } + } + /* Save frame pointer if needed. First save the FP on stack, if not autosaved. Unfortunately, I cannot add it to gmask and use the above loop to save fp because our ABI states fp goes aftert all @@ -3054,7 +3205,7 @@ arc_save_callee_saves (unsigned int gmask, if it is for an interrupt handler) using LD/LDD instructions. */ static int -arc_restore_callee_saves (unsigned int gmask, +arc_restore_callee_saves (uint64_t gmask, bool restore_blink, bool restore_fp, HOST_WIDE_INT offset, @@ -3064,6 +3215,7 @@ arc_restore_callee_saves (unsigned int gmask, int frame_deallocated = 0; HOST_WIDE_INT offs = cfun->machine->frame_info.reg_size; bool early_blink_restore; + int i; /* Emit mov fp,sp. */ if (arc_frame_pointer_needed () && offset) @@ -3080,6 +3232,43 @@ arc_restore_callee_saves (unsigned int gmask, frame_deallocated += frame_restore_reg (hard_frame_pointer_rtx, 0); } + /* Restore AUX-regs used by FPX machinery. */ + if (arc_must_save_register (TARGET_BIG_ENDIAN ? R41_REG : R40_REG, + cfun, TARGET_DPFP)) + { + rtx reg0 = gen_rtx_REG (SImode, R0_REG); + + gcc_assert (offset == 0); + for (i = 0; i < 4; i++) + { + frame_deallocated += pop_reg (reg0); + emit_insn (gen_rtx_UNSPEC_VOLATILE + (VOIDmode, gen_rtvec (2, reg0, GEN_INT (AUX_DPFP_START + + i)), + VUNSPEC_ARC_SR)); + } + } + + /* Check if we need to restore the ZOL machinery. */ + if (arc_lpcwidth !=0 && arc_must_save_register (LP_COUNT, cfun, true)) + { + rtx reg0 = gen_rtx_REG (SImode, R0_REG); + + gcc_assert (offset == 0); + frame_deallocated += pop_reg (reg0); + emit_move_insn (gen_rtx_REG (SImode, LP_COUNT), reg0); + + frame_deallocated += pop_reg (reg0); + emit_insn (gen_rtx_UNSPEC_VOLATILE + (VOIDmode, gen_rtvec (2, reg0, GEN_INT (AUX_LP_END)), + VUNSPEC_ARC_SR)); + + frame_deallocated += pop_reg (reg0); + emit_insn (gen_rtx_UNSPEC_VOLATILE + (VOIDmode, gen_rtvec (2, reg0, GEN_INT (AUX_LP_START)), + VUNSPEC_ARC_SR)); + } + if (offset) { /* No $fp involved, we need to do an add to set the $sp to the @@ -3090,7 +3279,8 @@ arc_restore_callee_saves (unsigned int gmask, } /* When we do not optimize for size, restore first blink. */ - early_blink_restore = restore_blink && !optimize_size && offs; + early_blink_restore = restore_blink && !optimize_size && offs + && !ARC_INTERRUPT_P (arc_compute_function_type (cfun)); if (early_blink_restore) { rtx addr = plus_constant (Pmode, stack_pointer_rtx, offs); @@ -3103,16 +3293,16 @@ arc_restore_callee_saves (unsigned int gmask, /* N.B. FRAME_POINTER_MASK and RETURN_ADDR_MASK are cleared in gmask. */ if (gmask) - for (int i = 0; i <= GMASK_LEN; i++) + for (i = 0; i <= GMASK_LEN; i++) { machine_mode restore_mode = SImode; if (TARGET_LL64 && ((i % 2) == 0) - && ((gmask & (1L << i)) != 0) - && ((gmask & (1L << (i + 1))) != 0)) + && ((gmask & (1ULL << i)) != 0) + && ((gmask & (1ULL << (i + 1))) != 0)) restore_mode = DImode; - else if ((gmask & (1L << i)) == 0) + else if ((gmask & (1ULL << i)) == 0) continue; reg = gen_rtx_REG (restore_mode, i); @@ -3120,12 +3310,12 @@ arc_restore_callee_saves (unsigned int gmask, switch (restore_mode) { case E_DImode: - if ((GMASK_LEN - __builtin_clz (gmask)) == (i + 1) + if ((GMASK_LEN - __builtin_clzll (gmask)) == (i + 1) && early_blink_restore) offs = 4; break; case E_SImode: - if ((GMASK_LEN - __builtin_clz (gmask)) == i + if ((GMASK_LEN - __builtin_clzll (gmask)) == i && early_blink_restore) offs = 4; break; @@ -3158,7 +3348,7 @@ arc_restore_callee_saves (unsigned int gmask, register. */ static int -arc_save_callee_enter (unsigned int gmask, +arc_save_callee_enter (uint64_t gmask, bool save_blink, bool save_fp, HOST_WIDE_INT offset) @@ -3169,7 +3359,7 @@ arc_save_callee_enter (unsigned int gmask, rtx insn, reg, mem; int frame_allocated = 0; - for (regno = start_reg; regno <= end_reg && (gmask & (1L << regno));) + for (regno = start_reg; regno <= end_reg && (gmask & (1ULL << regno));) regno++; end_reg = regno - 1; @@ -3214,7 +3404,7 @@ arc_save_callee_enter (unsigned int gmask, off)); XVECEXP (insn, 0, indx) = gen_rtx_SET (mem, reg); RTX_FRAME_RELATED_P (XVECEXP (insn, 0, indx)) = 1; - gmask = gmask & ~(1L << regno); + gmask = gmask & ~(1ULL << regno); } if (save_fp) @@ -3249,7 +3439,7 @@ arc_save_callee_enter (unsigned int gmask, (RESTORE_FP), and can automatic return (RETURN_P). */ static int -arc_restore_callee_leave (unsigned int gmask, +arc_restore_callee_leave (uint64_t gmask, bool restore_blink, bool restore_fp, bool return_p, @@ -3261,7 +3451,7 @@ arc_restore_callee_leave (unsigned int gmask, rtx insn, reg, mem; int frame_allocated = 0; - for (regno = start_reg; regno <= end_reg && (gmask & (1L << regno));) + for (regno = start_reg; regno <= end_reg && (gmask & (1ULL << regno));) regno++; end_reg = regno - 1; @@ -3322,7 +3512,7 @@ arc_restore_callee_leave (unsigned int gmask, off)); XVECEXP (insn, 0, indx) = gen_rtx_SET (reg, mem); RTX_FRAME_RELATED_P (XVECEXP (insn, 0, indx)) = 1; - gmask = gmask & ~(1L << regno); + gmask = gmask & ~(1ULL << regno); } if (restore_fp) @@ -3385,7 +3575,7 @@ arc_restore_callee_leave (unsigned int gmask, */ static int -arc_save_callee_milli (unsigned int gmask, +arc_save_callee_milli (uint64_t gmask, bool save_blink, bool save_fp, HOST_WIDE_INT offset, @@ -3397,7 +3587,7 @@ arc_save_callee_milli (unsigned int gmask, rtx insn, reg, mem; int frame_allocated = 0; - for (regno = start_reg; regno <= end_reg && (gmask & (1L << regno));) + for (regno = start_reg; regno <= end_reg && (gmask & (1ULL << regno));) regno++; end_reg = regno - 1; @@ -3440,7 +3630,7 @@ arc_save_callee_milli (unsigned int gmask, off)); XVECEXP (insn, 0, indx) = gen_rtx_SET (mem, reg); RTX_FRAME_RELATED_P (XVECEXP (insn, 0, indx)) = 1; - gmask = gmask & ~(1L << regno); + gmask = gmask & ~(1ULL << regno); } insn = frame_insn (insn); @@ -3468,9 +3658,9 @@ arc_save_callee_milli (unsigned int gmask, } /* Save remaining registers using st instructions. */ - for (regno = 0; regno <= 31; regno++) + for (regno = 0; regno <= GMASK_LEN; regno++) { - if ((gmask & (1L << regno)) == 0) + if ((gmask & (1ULL << regno)) == 0) continue; reg = gen_rtx_REG (SImode, regno); @@ -3499,7 +3689,7 @@ arc_save_callee_milli (unsigned int gmask, /* Like the previous function but restore. */ static int -arc_restore_callee_milli (unsigned int gmask, +arc_restore_callee_milli (uint64_t gmask, bool restore_blink, bool restore_fp, bool return_p, @@ -3511,7 +3701,7 @@ arc_restore_callee_milli (unsigned int gmask, rtx insn, reg, mem; int frame_allocated = 0; - for (regno = start_reg; regno <= end_reg && (gmask & (1L << regno));) + for (regno = start_reg; regno <= end_reg && (gmask & (1ULL << regno));) regno++; end_reg = regno - 1; @@ -3571,13 +3761,13 @@ arc_restore_callee_milli (unsigned int gmask, off)); XVECEXP (insn, 0, indx) = gen_rtx_SET (reg, mem); RTX_FRAME_RELATED_P (XVECEXP (insn, 0, indx)) = 1; - gmask = gmask & ~(1L << regno); + gmask = gmask & ~(1ULL << regno); } /* Restore remaining registers using LD instructions. */ - for (regno = 0; regno <= 31; regno++) + for (regno = 0; regno <= GMASK_LEN; regno++) { - if ((gmask & (1L << regno)) == 0) + if ((gmask & (1ULL << regno)) == 0) continue; reg = gen_rtx_REG (SImode, regno); @@ -3627,7 +3817,7 @@ void arc_expand_prologue (void) { int size; - unsigned int gmask = cfun->machine->frame_info.gmask; + uint64_t gmask = cfun->machine->frame_info.gmask; struct arc_frame_info *frame = &cfun->machine->frame_info; unsigned int frame_size_to_allocate; int first_offset = 0; @@ -3700,6 +3890,30 @@ arc_expand_prologue (void) emit_insn (gen_blockage ()); } +/* Return the register number of the register holding the return address + for a function of type TYPE. */ + +static int +arc_return_address_register (unsigned int fn_type) +{ + int regno = 0; + + if (ARC_INTERRUPT_P (fn_type)) + { + if ((fn_type & (ARC_FUNCTION_ILINK1 | ARC_FUNCTION_FIRQ)) != 0) + regno = ILINK1_REG; + else if ((fn_type & ARC_FUNCTION_ILINK2) != 0) + regno = ILINK2_REG; + else + gcc_unreachable (); + } + else if (ARC_NORMAL_P (fn_type) || ARC_NAKED_P (fn_type)) + regno = RETURN_ADDR_REGNUM; + + gcc_assert (regno != 0); + return regno; +} + /* Do any necessary cleanup after a function to restore stack, frame, and regs. */ @@ -3715,12 +3929,11 @@ arc_expand_epilogue (int sibcall_p) bool restore_fp = arc_frame_pointer_needed () && !ARC_AUTOFP_IRQ_P (fn_type); bool restore_blink = arc_must_save_return_addr (cfun) && !ARC_AUTOBLINK_IRQ_P (fn_type); - unsigned int gmask = cfun->machine->frame_info.gmask; + uint64_t gmask = cfun->machine->frame_info.gmask; bool return_p = !sibcall_p && fn_type == ARC_FUNCTION_NORMAL && !cfun->machine->frame_info.pretend_size; struct arc_frame_info *frame = &cfun->machine->frame_info; - /* Naked functions don't have epilogue. */ if (ARC_NAKED_P (fn_type)) return; @@ -3795,7 +4008,18 @@ arc_expand_epilogue (int sibcall_p) EH_RETURN_STACKADJ_RTX)); /* Emit the return instruction. */ - if (sibcall_p == FALSE) + if (ARC_INTERRUPT_P (fn_type)) + { + rtx ra = gen_rtx_REG (Pmode, arc_return_address_register (fn_type)); + + if (TARGET_V2) + emit_jump_insn (gen_rtie ()); + else if (TARGET_ARC700) + emit_jump_insn (gen_rtie ()); + else + emit_jump_insn (gen_arc600_rtie (ra)); + } + else if (sibcall_p == FALSE) emit_jump_insn (gen_simple_return ()); } @@ -9702,12 +9926,6 @@ arc_check_millicode (rtx op, int offset, int load_p) /* Accessor functions for cfun->machine->unalign. */ -int -arc_get_unalign (void) -{ - return cfun->machine->unalign; -} - void arc_clear_unalign (void) { @@ -10142,29 +10360,6 @@ arc_can_follow_jump (const rtx_insn *follower, const rtx_insn *followee) return true; } -/* Return the register number of the register holding the return address - for a function of type TYPE. */ - -int -arc_return_address_register (unsigned int fn_type) -{ - int regno = 0; - - if (ARC_INTERRUPT_P (fn_type)) - { - if ((fn_type & (ARC_FUNCTION_ILINK1 | ARC_FUNCTION_FIRQ)) != 0) - regno = ILINK1_REG; - else if ((fn_type & ARC_FUNCTION_ILINK2) != 0) - regno = ILINK2_REG; - else - gcc_unreachable (); - } - else if (ARC_NORMAL_P (fn_type) || ARC_NAKED_P (fn_type)) - regno = RETURN_ADDR_REGNUM; - - gcc_assert (regno != 0); - return regno; -} /* Implement EPILOGUE_USES. Return true if REGNO should be added to the deemed uses of the epilogue. @@ -10177,25 +10372,25 @@ bool arc_epilogue_uses (int regno) { unsigned int fn_type; + fn_type = arc_compute_function_type (cfun); if (regno == arc_tp_regno) return true; - fn_type = arc_compute_function_type (cfun); - if (reload_completed) + if (regno == RETURN_ADDR_REGNUM) + return true; + + if (regno == arc_return_address_register (fn_type)) + return true; + + if (epilogue_completed && ARC_INTERRUPT_P (fn_type)) { - if (ARC_INTERRUPT_P (cfun->machine->fn_type)) - { - if (!fixed_regs[regno]) - return true; - return ((regno == arc_return_address_register (fn_type)) - || (regno == RETURN_ADDR_REGNUM)); - } - else - return regno == RETURN_ADDR_REGNUM; + /* An interrupt function restores more registers. */ + if (df_regs_ever_live_p (regno) || call_used_regs[regno]) + return true; } - else - return regno == arc_return_address_register (fn_type); + + return false; } /* Helper for EH_USES macro. */ @@ -11491,6 +11686,16 @@ arc_check_mov_const (HOST_WIDE_INT ival) return false; } +/* Return nonzero if this function is known to have a null epilogue. + This allows the optimizer to omit jumps to jumps if no stack + was created. */ + +bool +arc_can_use_return_insn (void) +{ + return (reload_completed && cfun->machine->frame_info.total_size == 0 + && !ARC_INTERRUPT_P (arc_compute_function_type (cfun))); +} #undef TARGET_USE_ANCHORS_FOR_SYMBOL_P #define TARGET_USE_ANCHORS_FOR_SYMBOL_P arc_use_anchors_for_symbol_p diff --git a/gcc/config/arc/arc.md b/gcc/config/arc/arc.md index b793e5b07a5..7cd47338ec2 100644 --- a/gcc/config/arc/arc.md +++ b/gcc/config/arc/arc.md @@ -163,6 +163,7 @@ VUNSPEC_ARC_LL VUNSPEC_ARC_BLOCKAGE VUNSPEC_ARC_EH_RETURN + VUNSPEC_ARC_ARC600_RTIE ]) (define_constants @@ -187,17 +188,37 @@ (R30_REG 30) (RETURN_ADDR_REGNUM 31) (R32_REG 32) + (R33_REG 33) + (R34_REG 34) + (R35_REG 35) + (R36_REG 36) + (R37_REG 37) + (R38_REG 38) + (R39_REG 39) (R40_REG 40) (R41_REG 41) (R42_REG 42) (R43_REG 43) (R44_REG 44) + (R45_REG 45) + (R46_REG 46) + (R47_REG 47) + (R48_REG 48) + (R49_REG 49) + (R50_REG 50) + (R51_REG 51) + (R52_REG 52) + (R53_REG 53) + (R54_REG 54) + (R55_REG 55) + (R56_REG 56) (R57_REG 57) + (R58_REG 58) + (R59_REG 59) + (MUL64_OUT_REG 58) (MUL32x16_REG 56) (ARCV2_ACC 58) - (R59_REG 59) - (LP_COUNT 60) (CC_REG 61) (PCL_REG 63) @@ -214,7 +235,7 @@ (define_attr "type" "move,load,store,cmove,unary,binary,compare,shift,uncond_branch,jump,branch, - brcc,brcc_no_delay_slot,call,sfunc,call_no_delay_slot, + brcc,brcc_no_delay_slot,call,sfunc,call_no_delay_slot,rtie, multi,umulti, two_cycle_core,lr,sr,divaw,loop_setup,loop_end,return, misc,spfp,dpfp_mult,dpfp_addsub,mulmac_600,cc_arith, simd_vload, simd_vload128, simd_vstore, simd_vmove, simd_vmove_else_zero, @@ -531,9 +552,7 @@ (cond [(eq_attr "in_delay_slot" "false") (const_string "no") (match_test "regno_clobbered_p - (arc_return_address_register - (arc_compute_function_type (cfun)), - insn, SImode, 1)") + (RETURN_ADDR_REGNUM, insn, SImode, 1)") (const_string "no")] (const_string "yes"))) @@ -757,9 +776,9 @@ core_3, archs4x, archs4xd, archs4xd_slow" ; execution must reflect this, lest out-of-range branches are created. ; the iscompact attribute allows the epilogue expander to know for which ; insns it should lengthen the return insn. -(define_insn_and_split "*movsi_insn" ; 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 - [(set (match_operand:SI 0 "move_dest_operand" "=q, q,r,q, h,rl, r, r, r, r, ?r, r, q, h, rl, q, S, Us<,RcqRck,!*x, r,!*Rsd,!*Rcd,r,Ucm, Usd,m, m,VUsc") - (match_operand:SI 1 "move_src_operand" "rL,rP,q,P,hCm1,rL, I,Clo,Chi,Cbi,Cpc,Clb,Cax,Cal,Cal,Uts,Rcq,RcqRck, Us>,Usd,Ucm, Usd, Ucd,m, r,!*Rzd,r,Cm3, C32"))] +(define_insn_and_split "*movsi_insn" ; 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 + [(set (match_operand:SI 0 "move_dest_operand" "=q, q,r,q, h, rl,r, r, r, r, ?r, r, q, h, rl, q, S, Us<,RcqRck,!*x, r,!*Rsd,!*Rcd,r,Ucm, Usd,m, m,VUsc") + (match_operand:SI 1 "move_src_operand" "rL,rP,q,P,hCm1,rLl,I,Clo,Chi,Cbi,Cpc,Clb,Cax,Cal,Cal,Uts,Rcq,RcqRck, Us>,Usd,Ucm, Usd, Ucd,m, r,!*Rzd,r,Cm3, C32"))] "register_operand (operands[0], SImode) || register_operand (operands[1], SImode) || (CONSTANT_P (operands[1]) @@ -775,8 +794,8 @@ core_3, archs4x, archs4xd, archs4xd_slow" mov%?\\t%0,%1 ;4 mov%?\\t%0,%1 ;5 mov%?\\t%0,%1 ;6 - movl.cl\\t %0,%1 ;7 - movh.cl\\t %0,%L1>>16 ;8 + movl.cl\\t%0,%1 ;7 + movh.cl\\t%0,%L1>>16 ;8 * return INTVAL (operands[1]) & 0xffffff ? \"movbi.cl\\t%0,%1 >> %p1,%p1,8;9\" : \"movbi.cl\\t%0,%L1 >> 24,24,8;9\"; add\\t%0,%1 ;10 add\\t%0,pcl,%1@pcl ;11 @@ -784,7 +803,7 @@ core_3, archs4x, archs4xd, archs4xd_slow" mov%?\\t%0,%j1 ;13 mov%?\\t%0,%j1 ;14 ld%?\\t%0,%1 ;15 - st%?\\t %1,%0 ;16 + st%?\\t%1,%0 ;16 * return arc_short_long (insn, \"push%?\\t%1%&\", \"st%U0\\t%1,%0%&\"); * return arc_short_long (insn, \"pop%?\\t%0%&\", \"ld%U1\\t%0,%1%&\"); ld%?\\t%0,%1 ;19 @@ -4560,13 +4579,13 @@ core_3, archs4x, archs4xd, archs4xd_slow" (set_attr "type" "misc")]) (define_insn "rtie" - [(unspec_volatile [(match_operand:SI 0 "immediate_operand" "N")] - VUNSPEC_ARC_RTIE)] - "" + [(return) + (unspec_volatile [(const_int 0)] VUNSPEC_ARC_RTIE)] + "!TARGET_ARC600_FAMILY" "rtie" [(set_attr "length" "4") - (set_attr "type" "misc") - (set_attr "cond" "clob")]) + (set_attr "type" "rtie") + (set_attr "cond" "clob")]) (define_insn "sync" [(unspec_volatile [(match_operand:SI 0 "immediate_operand" "N")] @@ -4788,88 +4807,52 @@ core_3, archs4x, archs4xd, archs4xd_slow" ; forbid instructions that change blink in the return / sibcall delay slot. (define_insn "simple_return" [(simple_return)] - "reload_completed" -{ - rtx reg - = gen_rtx_REG (Pmode, - arc_return_address_register (arc_compute_function_type - (cfun))); + "" + "j%!%*\\t[blink]" + [(set_attr "type" "return") + (set_attr "cond" "canuse") + (set_attr "iscompact" "maybe") + (set_attr "length" "*")]) - if (TARGET_V2 - && ARC_INTERRUPT_P (arc_compute_function_type (cfun))) - { - return \"rtie\"; - } - output_asm_insn (\"j%!%* [%0]%&\", ®); - return \"\"; -} - [(set (attr "type") - (cond [(and (match_test "ARC_INTERRUPT_P (arc_compute_function_type (cfun))") - (match_test "TARGET_V2")) - (const_string "brcc_no_delay_slot")] - (const_string "return"))) - ; predicable won't help here since the canonical rtl looks different - ; for branches. - (set (attr "cond") - (cond [(and (eq (symbol_ref "arc_compute_function_type (cfun)") - (symbol_ref "ARC_FUNCTION_ILINK1")) - (match_test "TARGET_V2")) - (const_string "nocond")] - (const_string "canuse"))) - (set (attr "iscompact") - (cond [(eq (symbol_ref "arc_compute_function_type (cfun)") - (symbol_ref "ARC_FUNCTION_NORMAL")) - (const_string "maybe")] - (const_string "false"))) - (set (attr "length") - (cond [(ne (symbol_ref "arc_compute_function_type (cfun)") - (symbol_ref "ARC_FUNCTION_NORMAL")) - (const_int 4)] - (const_int 2)))]) +(define_insn "arc600_rtie" + [(return) + (unspec_volatile [(match_operand 0 "pmode_register_operand" "")] + VUNSPEC_ARC_ARC600_RTIE)] + "TARGET_ARC600_FAMILY" + "j.f\\t[%0]" + [(set_attr "length" "4") + (set_attr "type" "rtie") + (set_attr "cond" "clob")]) (define_insn "p_return_i" [(set (pc) (if_then_else (match_operator 0 "proper_comparison_operator" [(reg CC_REG) (const_int 0)]) (simple_return) (pc)))] - "reload_completed - && !(TARGET_V2 - && ARC_INTERRUPT_P (arc_compute_function_type (cfun)))" + "reload_completed" { - rtx xop[2]; - xop[0] = operands[0]; - xop[1] - = gen_rtx_REG (Pmode, - arc_return_address_register (arc_compute_function_type - (cfun))); - - output_asm_insn (\"j%d0%!%# [%1]%&\", xop); + output_asm_insn (\"j%d0%!%#\\t[blink]\", operands); /* record the condition in case there is a delay insn. */ - arc_ccfsm_record_condition (xop[0], false, insn, 0); + arc_ccfsm_record_condition (operands[0], false, insn, 0); return \"\"; } [(set_attr "type" "return") (set_attr "cond" "use") - (set (attr "iscompact") - (cond [(eq (symbol_ref "arc_compute_function_type (cfun)") - (symbol_ref "ARC_FUNCTION_NORMAL")) - (const_string "maybe")] - (const_string "false"))) + (set_attr "iscompact" "maybe" ) (set (attr "length") - (cond [(ne (symbol_ref "arc_compute_function_type (cfun)") - (symbol_ref "ARC_FUNCTION_NORMAL")) - (const_int 4) - (not (match_operand 0 "equality_comparison_operator" "")) + (cond [(not (match_operand 0 "equality_comparison_operator" "")) (const_int 4) (eq_attr "delay_slot_filled" "yes") (const_int 4)] (const_int 2)))]) -;; ??? #ifdefs in function.c require the presence of this pattern, with a -;; non-constant predicate. +;; Return nonzero if this function is known to have a null epilogue. +;; This allows the optimizer to omit jumps to jumps if no stack +;; was created. (define_expand "return" [(return)] - "optimize < 0") + "arc_can_use_return_insn ()" + "") ;; Comment in final.c (insn_current_reference_address) says ;; forward branch addresses are calculated from the next insn after branch diff --git a/gcc/config/arc/builtins.def b/gcc/config/arc/builtins.def index 2ab43f64de8..f1c59d5e7bb 100644 --- a/gcc/config/arc/builtins.def +++ b/gcc/config/arc/builtins.def @@ -33,7 +33,7 @@ /* Special builtins. */ DEF_BUILTIN (NOP, 0, void_ftype_void, nothing, 1) -DEF_BUILTIN (RTIE, 0, void_ftype_void, rtie, 1) +DEF_BUILTIN (RTIE, 0, void_ftype_void, rtie, !TARGET_ARC600_FAMILY) DEF_BUILTIN (SYNC, 0, void_ftype_void, sync, 1) DEF_BUILTIN (BRK, 0, void_ftype_void, brk, 1) DEF_BUILTIN (SWI, 0, void_ftype_void, swi, 1) diff --git a/gcc/config/arc/predicates.md b/gcc/config/arc/predicates.md index 72fbf2a8528..e0013b32f0f 100644 --- a/gcc/config/arc/predicates.md +++ b/gcc/config/arc/predicates.md @@ -285,6 +285,8 @@ return GET_MODE (op) == SFmode; return 0; case REG : + if (REGNO (op) == LP_COUNT) + return 1; return register_operand (op, mode); case SUBREG : /* (subreg (mem ...) ...) can occur here if the inner part was once a diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 6da56e0dd86..621eda94bf0 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,13 @@ +2019-07-24 Claudiu Zissulescu + + * gcc.target/arc/arc.exp (check_effective_target_accregs): New + predicate. + * gcc.target/arc/builtin_special.c: Update test/ + * gcc.target/arc/interrupt-1.c: Likewise. + * gcc.target/arc/interrupt-10.c: New test. + * gcc.target/arc/interrupt-11.c: Likewise. + * gcc.target/arc/interrupt-12.c: Likewise. + 2019-07-24 Andreas Krebbel * gcc.target/s390/addsub-signed-overflow-1.c: New test. diff --git a/gcc/testsuite/gcc.target/arc/arc.exp b/gcc/testsuite/gcc.target/arc/arc.exp index a1b86b5b1d5..55e12137a2a 100644 --- a/gcc/testsuite/gcc.target/arc/arc.exp +++ b/gcc/testsuite/gcc.target/arc/arc.exp @@ -94,6 +94,24 @@ proc check_effective_target_barrelshifter { } { }] } +#return 1 if we use ARCv2 Accumulator registers +proc check_effective_target_accregs { } { + return [check_no_compiler_messages accregs assembly { + #if !defined(__ARC_MPY_DMPY__) \ + && !defined(__ARC_MPY_MACD__) && !defined(__ARC_MPY_QMACW__) + #error No accumulator available for this config + #endif + }] +} + +proc check_effective_target_dpfp { } { + return [check_no_compiler_messages dpfp assembly { + #if !defined(__ARC_FPX_DP__) && !defined(__ARC_FPU_ASSIST__) + #error No FPX available for this config + #endif + }] +} + # If a testcase doesn't have special options, use these. global DEFAULT_CFLAGS if ![info exists DEFAULT_CFLAGS] then { diff --git a/gcc/testsuite/gcc.target/arc/builtin_special.c b/gcc/testsuite/gcc.target/arc/builtin_special.c index 7590b174982..829528ceb55 100644 --- a/gcc/testsuite/gcc.target/arc/builtin_special.c +++ b/gcc/testsuite/gcc.target/arc/builtin_special.c @@ -21,7 +21,9 @@ NORET (nop) +#if !defined (__ARC600__) && !defined (__ARC601__) NORET (rtie) +#endif #ifdef __A7__ NORET (sync) diff --git a/gcc/testsuite/gcc.target/arc/interrupt-1.c b/gcc/testsuite/gcc.target/arc/interrupt-1.c index 8a2002bf1c0..5a5139a2470 100644 --- a/gcc/testsuite/gcc.target/arc/interrupt-1.c +++ b/gcc/testsuite/gcc.target/arc/interrupt-1.c @@ -6,5 +6,5 @@ void __attribute__ ((interrupt("ilink1"))) handler1 (void) { } -/* { dg-final { scan-assembler-times "j.*\[ilink1\]" 1 { target { arc700 || arc6xx } } } } */ -/* { dg-final { scan-assembler-times "rtie" 1 { target { arcem || archs } } } } */ +/* { dg-final { scan-assembler-times "j.*\[ilink1\]" 1 { target { arc6xx } } } } */ +/* { dg-final { scan-assembler-times "rtie" 1 { target { ! { arc6xx } } } } } */ diff --git a/gcc/testsuite/gcc.target/arc/interrupt-10.c b/gcc/testsuite/gcc.target/arc/interrupt-10.c new file mode 100644 index 00000000000..605c19f5797 --- /dev/null +++ b/gcc/testsuite/gcc.target/arc/interrupt-10.c @@ -0,0 +1,36 @@ +/* { dg-options "-O2" } */ +extern void will_trig_exception(void); + +#if defined (__ARCHS__) || defined (__ARCEM__) +__attribute__ ((interrupt("ilink"))) +#else +__attribute__ ((interrupt("ilink1"))) +#endif +void isr_0 (void) +{ + will_trig_exception(); +} + +/* { dg-final { scan-assembler-times "j.*\[ilink1\]" 1 { target { arc6xx } } } } */ +/* { dg-final { scan-assembler-times "rtie" 1 { target { ! { arc6xx } } } } } */ +/* { dg-final { scan-assembler-times "blink" 2 } } */ +/* { dg-final { scan-assembler-times "fp" 2 { target { ! { archs } } } } } */ +/* { dg-final { scan-assembler-times "r30" 2 { target { archs || arcem } } } } */ +/* { dg-final { scan-assembler-times "r24" 2 } } */ +/* { dg-final { scan-assembler-times "r22" 2 } } */ +/* { dg-final { scan-assembler-times "r20" 2 } } */ +/* { dg-final { scan-assembler-times "r18" 2 } } */ +/* { dg-final { scan-assembler-times "r16" 2 } } */ +/* { dg-final { scan-assembler-times "r14" 2 } } */ +/* { dg-final { scan-assembler-times "r12" 2 } } */ +/* { dg-final { scan-assembler-times "r10" 2 } } */ +/* { dg-final { scan-assembler-times "r8" 2 } } */ +/* { dg-final { scan-assembler-times "r6" 2 } } */ +/* { dg-final { scan-assembler-times "r4" 2 } } */ +/* { dg-final { scan-assembler-times "r2\[,\n\]" 2 } } */ +/* { dg-final { scan-assembler-times "lp_count" 2 } } */ +/* { dg-final { scan-assembler-times "sr\\s+r\[0-9\]," 2 { target { ! { dpfp } } } } } */ +/* { dg-final { scan-assembler-times "lr\\s+r\[0-9\]" 2 { target { ! { dpfp } } } } } */ +/* { dg-final { scan-assembler-times "sr\\s+r\[0-9\]," 6 { target { dpfp } } } } */ +/* { dg-final { scan-assembler-times "lr\\s+r\[0-9\]" 6 { target { dpfp } } } } */ +/* { dg-final { scan-assembler-times "r58" 2 { target { accregs } } } } */ diff --git a/gcc/testsuite/gcc.target/arc/interrupt-11.c b/gcc/testsuite/gcc.target/arc/interrupt-11.c new file mode 100644 index 00000000000..ca340ee7c74 --- /dev/null +++ b/gcc/testsuite/gcc.target/arc/interrupt-11.c @@ -0,0 +1,16 @@ +extern int a; + +#if defined (__ARCHS__) || defined (__ARCEM__) +__attribute__ ((interrupt("ilink"))) +#else +__attribute__ ((interrupt("ilink2"))) +#endif +void isr_1 (void) +{ + a++; +} + +/* { dg-final { scan-assembler-times "j.*\[ilink2\]" 1 { target { arc6xx } } } } */ +/* { dg-final { scan-assembler-times "rtie" 1 { target { ! { arc6xx } } } } } */ +/* { dg-final { scan-assembler-times "push_s\\s+r\[0-9\]" 1 } } */ +/* { dg-final { scan-assembler-times "pop_s\\s+r\[0-9\]" 1 } } */ diff --git a/gcc/testsuite/gcc.target/arc/interrupt-12.c b/gcc/testsuite/gcc.target/arc/interrupt-12.c new file mode 100644 index 00000000000..da3e26cf0bb --- /dev/null +++ b/gcc/testsuite/gcc.target/arc/interrupt-12.c @@ -0,0 +1,16 @@ +/* { dg-options "-O0 -g" } */ + +typedef void (*isr_routine)(void); +isr_routine will_trig_exception; + + #if defined (__ARCHS__) || defined (__ARCEM__) +void __attribute__ ((interrupt("ilink"))) +#else +void __attribute__ ((interrupt("ilink1"))) +#endif +isr_template(void) +{ + will_trig_exception(); +} + +/* { dg-final { scan-assembler-times "\\\.cfi_offset 0" 1 } } */