From: Max Filippov Date: Tue, 3 Mar 2015 17:44:01 +0000 (+0000) Subject: Implement call0 ABI for xtensa X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=590e26360c16934958e37d65e019207425e23538;p=gcc.git Implement call0 ABI for xtensa call0 is an ABI that doesn't use register windows. 2015-03-03 Max Filippov gcc/ * config/xtensa/constraints.md ("a" constraint): Include stack pointer in case of call0 ABI. ("q" constraint): Make empty in case of call0 ABI. ("D" constraint): Include stack pointer in case of call0 ABI. * config/xtensa/xtensa-protos.h (xtensa_set_return_address, xtensa_expand_epilogue, xtensa_regno_to_class): Add new function prototypes. * config/xtensa/xtensa.c (xtensa_callee_save_size): New variable. (xtensa_regno_to_class): Make it a local variable in the function xtensa_regno_to_class. (xtensa_function_epilogue, TARGET_ASM_FUNCTION_EPILOGUE): Remove macro, function prototype and implementation. (reg_nonleaf_alloc_order): Make it a local variable in the function order_regs_for_local_alloc. (xtensa_conditional_register_usage): New function. (TARGET_CONDITIONAL_REGISTER_USAGE): Define macro. (xtensa_valid_move): Allow direct moves to stack pointer register in call0 ABI. (xtensa_setup_frame_addresses): Only spill register windows in windowed ABI. (xtensa_emit_call): Emit call(x)8 or call(x)0 in windowed and call0 ABI respectively. (xtensa_function_arg_1): Only mark a7 register for copying in windowed ABI. (xtensa_call_save_reg): New function. (compute_frame_size): Add space for callee saved register storage to the frame size in call0 ABI. (xtensa_expand_prologue): Generate code to set up stack frame and save callee-saved registers in call0 ABI. (xtensa_expand_epilogue): New function. (xtensa_set_return_address): New function. (xtensa_return_addr): Calculate return address in call0 ABI. (xtensa_builtin_saveregs): Only mark a7 register for copying and emit copying code in windowed ABI. (order_regs_for_local_alloc): Add preferred register allocation order for non-leaf function in call0 ABI. (xtensa_static_chain): Add atatic chain passing for call0 ABI. (xtensa_asm_trampoline_template): Add trampoline generation for call0 ABI. (xtensa_trampoline_init): Add trampoline initialization for call0 ABI. (xtensa_conditional_register_usage, xtensa_regno_to_class): New functions. * config/xtensa/xtensa.h (TARGET_WINDOWED_ABI): New macro. (TARGET_CPU_CPP_BUILTINS): Add built-in define for call0 ABI. (CALL_USED_REGISTERS): Modify to encode both windowed and call0 ABI call-used registers. (HARD_FRAME_POINTER_REGNUM): Add frame pointer for call0 ABI. (INCOMING_REGNO, OUTGOING_REGNO): Use argument unchanged in call0 ABI. (REG_CLASS_CONTENTS): Include all registers into the preferred reload registers set, adjust the set in the xtensa_conditional_register_usage. (xtensa_regno_to_class): Drop variable declaration. (REGNO_REG_CLASS): Redefine to use xtensa_regno_to_class function. (WINDOW_SIZE): Define as 8 or 0 for windowed and call0 ABI respectively. (FUNCTION_PROFILER): Add _mcount call for call0 ABI. (TRAMPOLINE_SIZE): Define trampoline size for call0 ABI. (RETURN_ADDR_IN_PREVIOUS_FRAME): Define to 0 in call0 ABI. (ASM_OUTPUT_POOL_PROLOGUE): Always generate literal pool location in call0 ABI. (EH_RETURN_STACKADJ_RTX): New definition, use a10 for passing stack adjustment size when handling exception. (CRT_CALL_STATIC_FUNCTION): Add definition for call0 ABI. * config/xtensa/xtensa.md (A9_REG, UNSPECV_BLOCKAGE): New definitions. ("return" pattern): Generate ret.n/ret in call0 ABI. ("epilogue" pattern): Expand epilogue. ("nonlocal_goto" pattern): Use default in call0 ABI. ("eh_return" pattern): Move implementation to eh_set_a0_windowed, emit eh_set_a0_* depending on ABI. ("eh_set_a0_windowed" pattern): Former eh_return pattern. ("eh_set_a0_call0", "blockage"): New patterns. libgcc/ * config/xtensa/lib2funcs.S (__xtensa_libgcc_window_spill, __xtensa_nonlocal_goto): Don't compile for call0 ABI. (__xtensa_sync_caches): Only use entry and retw in windowed ABI, use ret in call0 ABI. * config/xtensa/t-windowed: New file. * libgcc/config/xtensa/t-xtensa (LIB2ADDEH): Move to t-windowed. * libgcc/configure: Regenerated. * libgcc/configure.ac: Check if xtensa target is configured for windowed ABI and thus needs to use custom unwind code. From-SVN: r221158 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6cbeb89a0ce..5c1bb270193 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,83 @@ +2015-03-03 Max Filippov + + Implement call0 ABI for xtensa + * config/xtensa/constraints.md ("a" constraint): Include stack + pointer in case of call0 ABI. + ("q" constraint): Make empty in case of call0 ABI. + ("D" constraint): Include stack pointer in case of call0 ABI. + * config/xtensa/xtensa-protos.h (xtensa_set_return_address, + xtensa_expand_epilogue, xtensa_regno_to_class): Add new function + prototypes. + * config/xtensa/xtensa.c (xtensa_callee_save_size): New + variable. + (xtensa_regno_to_class): Make it a local variable in the + function xtensa_regno_to_class. + (xtensa_function_epilogue, TARGET_ASM_FUNCTION_EPILOGUE): Remove + macro, function prototype and implementation. + (reg_nonleaf_alloc_order): Make it a local variable in the + function order_regs_for_local_alloc. + (xtensa_conditional_register_usage): New function. + (TARGET_CONDITIONAL_REGISTER_USAGE): Define macro. + (xtensa_valid_move): Allow direct moves to stack pointer + register in call0 ABI. + (xtensa_setup_frame_addresses): Only spill register windows in + windowed ABI. + (xtensa_emit_call): Emit call(x)8 or call(x)0 in windowed and + call0 ABI respectively. + (xtensa_function_arg_1): Only mark a7 register for copying in + windowed ABI. + (xtensa_call_save_reg): New function. + (compute_frame_size): Add space for callee saved register + storage to the frame size in call0 ABI. + (xtensa_expand_prologue): Generate code to set up stack frame + and save callee-saved registers in call0 ABI. + (xtensa_expand_epilogue): New function. + (xtensa_set_return_address): New function. + (xtensa_return_addr): Calculate return address in call0 ABI. + (xtensa_builtin_saveregs): Only mark a7 register for copying and + emit copying code in windowed ABI. + (order_regs_for_local_alloc): Add preferred register allocation + order for non-leaf function in call0 ABI. + (xtensa_static_chain): Add atatic chain passing for call0 ABI. + (xtensa_asm_trampoline_template): Add trampoline generation for + call0 ABI. + (xtensa_trampoline_init): Add trampoline initialization for + call0 ABI. + (xtensa_conditional_register_usage, xtensa_regno_to_class): New + functions. + * config/xtensa/xtensa.h (TARGET_WINDOWED_ABI): New macro. + (TARGET_CPU_CPP_BUILTINS): Add built-in define for call0 ABI. + (CALL_USED_REGISTERS): Modify to encode both windowed and call0 + ABI call-used registers. + (HARD_FRAME_POINTER_REGNUM): Add frame pointer for call0 ABI. + (INCOMING_REGNO, OUTGOING_REGNO): Use argument unchanged in + call0 ABI. + (REG_CLASS_CONTENTS): Include all registers into the preferred + reload registers set, adjust the set in the + xtensa_conditional_register_usage. + (xtensa_regno_to_class): Drop variable declaration. + (REGNO_REG_CLASS): Redefine to use xtensa_regno_to_class + function. + (WINDOW_SIZE): Define as 8 or 0 for windowed and call0 ABI + respectively. + (FUNCTION_PROFILER): Add _mcount call for call0 ABI. + (TRAMPOLINE_SIZE): Define trampoline size for call0 ABI. + (RETURN_ADDR_IN_PREVIOUS_FRAME): Define to 0 in call0 ABI. + (ASM_OUTPUT_POOL_PROLOGUE): Always generate literal pool + location in call0 ABI. + (EH_RETURN_STACKADJ_RTX): New definition, use a10 for passing + stack adjustment size when handling exception. + (CRT_CALL_STATIC_FUNCTION): Add definition for call0 ABI. + * config/xtensa/xtensa.md (A9_REG, UNSPECV_BLOCKAGE): New + definitions. + ("return" pattern): Generate ret.n/ret in call0 ABI. + ("epilogue" pattern): Expand epilogue. + ("nonlocal_goto" pattern): Use default in call0 ABI. + ("eh_return" pattern): Move implementation to eh_set_a0_windowed, + emit eh_set_a0_* depending on ABI. + ("eh_set_a0_windowed" pattern): Former eh_return pattern. + ("eh_set_a0_call0", "blockage"): New patterns. + 2015-03-03 Martin Liska PR ipa/65287 diff --git a/gcc/config/xtensa/constraints.md b/gcc/config/xtensa/constraints.md index 74aca6c2edf..30f4c1f696d 100644 --- a/gcc/config/xtensa/constraints.md +++ b/gcc/config/xtensa/constraints.md @@ -19,7 +19,7 @@ ;; Register constraints. -(define_register_constraint "a" "GR_REGS" +(define_register_constraint "a" "TARGET_WINDOWED_ABI ? GR_REGS : AR_REGS" "General-purpose AR registers @code{a0}-@code{a15}, except @code{a1} (@code{sp}).") @@ -36,7 +36,7 @@ "Floating-point registers @code{f0}-@code{f15}; only available if the Xtensa Floating-Pointer Coprocessor is configured.") -(define_register_constraint "q" "SP_REG" +(define_register_constraint "q" "TARGET_WINDOWED_ABI ? SP_REG : NO_REGS" "@internal The stack pointer (register @code{a1}).") @@ -53,7 +53,7 @@ General-purpose AR registers, but only if the Xtensa 16-Bit Integer Multiply Option is configured.") -(define_register_constraint "D" "TARGET_DENSITY ? GR_REGS: NO_REGS" +(define_register_constraint "D" "TARGET_DENSITY ? (TARGET_WINDOWED_ABI ? GR_REGS : AR_REGS) : NO_REGS" "@internal General-purpose AR registers, but only if the Xtensa Code Density Option is configured.") diff --git a/gcc/config/xtensa/xtensa-protos.h b/gcc/config/xtensa/xtensa-protos.h index ad95db047d8..6a5362547d5 100644 --- a/gcc/config/xtensa/xtensa-protos.h +++ b/gcc/config/xtensa/xtensa-protos.h @@ -61,6 +61,7 @@ extern void init_cumulative_args (CUMULATIVE_ARGS *, int); extern void print_operand (FILE *, rtx, int); extern void print_operand_address (FILE *, rtx); extern void xtensa_output_literal (FILE *, rtx, machine_mode, int); +extern void xtensa_set_return_address (rtx, rtx); extern rtx xtensa_return_addr (int, rtx); #endif /* RTX_CODE */ @@ -68,6 +69,8 @@ extern void xtensa_setup_frame_addresses (void); extern int xtensa_dbx_register_number (int); extern long compute_frame_size (int); extern void xtensa_expand_prologue (void); +extern void xtensa_expand_epilogue (void); extern void order_regs_for_local_alloc (void); +extern enum reg_class xtensa_regno_to_class (int regno); #endif /* !__XTENSA_PROTOS_H__ */ diff --git a/gcc/config/xtensa/xtensa.c b/gcc/config/xtensa/xtensa.c index 6c289e53719..eb039bac56a 100644 --- a/gcc/config/xtensa/xtensa.c +++ b/gcc/config/xtensa/xtensa.c @@ -118,6 +118,8 @@ char xtensa_hard_regno_mode_ok[(int) MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER]; /* Current frame size calculated by compute_frame_size. */ unsigned xtensa_current_frame_size; +/* Callee-save area size in the current frame calculated by compute_frame_size. */ +int xtensa_callee_save_size; /* Largest block move to handle in-line. */ #define LARGEST_MOVE_RATIO 15 @@ -144,21 +146,6 @@ const char xtensa_leaf_regs[FIRST_PSEUDO_REGISTER] = 1 }; -/* Map hard register number to register class */ -const enum reg_class xtensa_regno_to_class[FIRST_PSEUDO_REGISTER] = -{ - RL_REGS, SP_REG, RL_REGS, RL_REGS, - RL_REGS, RL_REGS, RL_REGS, GR_REGS, - RL_REGS, RL_REGS, RL_REGS, RL_REGS, - RL_REGS, RL_REGS, RL_REGS, RL_REGS, - AR_REGS, AR_REGS, BR_REGS, - FP_REGS, FP_REGS, FP_REGS, FP_REGS, - FP_REGS, FP_REGS, FP_REGS, FP_REGS, - FP_REGS, FP_REGS, FP_REGS, FP_REGS, - FP_REGS, FP_REGS, FP_REGS, FP_REGS, - ACC_REG, -}; - static void xtensa_option_override (void); static enum internal_test map_test_to_internal_test (enum rtx_code); static rtx gen_int_relational (enum rtx_code, rtx, rtx, int *); @@ -171,7 +158,6 @@ static rtx xtensa_legitimize_address (rtx, rtx, machine_mode); static bool xtensa_mode_dependent_address_p (const_rtx, addr_space_t); static bool xtensa_return_in_msb (const_tree); static void printx (FILE *, signed int); -static void xtensa_function_epilogue (FILE *, HOST_WIDE_INT); static rtx xtensa_builtin_saveregs (void); static bool xtensa_legitimate_address_p (machine_mode, rtx, bool); static unsigned int xtensa_multibss_section_type_flags (tree, const char *, @@ -224,17 +210,9 @@ static const char *xtensa_invalid_within_doloop (const rtx_insn *); static bool xtensa_member_type_forces_blk (const_tree, machine_mode mode); -static const int reg_nonleaf_alloc_order[FIRST_PSEUDO_REGISTER] = - REG_ALLOC_ORDER; - - -/* This macro generates the assembly code for function exit, - on machines that need it. If FUNCTION_EPILOGUE is not defined - then individual return instructions are generated for each - return statement. Args are same as for FUNCTION_PROLOGUE. */ +static void xtensa_conditional_register_usage (void); -#undef TARGET_ASM_FUNCTION_EPILOGUE -#define TARGET_ASM_FUNCTION_EPILOGUE xtensa_function_epilogue + /* These hooks specify assembly directives for creating certain kinds of integer object. */ @@ -355,6 +333,9 @@ static const int reg_nonleaf_alloc_order[FIRST_PSEUDO_REGISTER] = #undef TARGET_INVALID_WITHIN_DOLOOP #define TARGET_INVALID_WITHIN_DOLOOP xtensa_invalid_within_doloop +#undef TARGET_CONDITIONAL_REGISTER_USAGE +#define TARGET_CONDITIONAL_REGISTER_USAGE xtensa_conditional_register_usage + struct gcc_target targetm = TARGET_INITIALIZER; @@ -522,9 +503,10 @@ xtensa_valid_move (machine_mode mode, rtx *operands) /* The stack pointer can only be assigned with a MOVSP opcode. */ if (dst_regnum == STACK_POINTER_REGNUM) - return (mode == SImode - && register_operand (operands[1], mode) - && !ACC_REG_P (xt_true_regnum (operands[1]))); + return !TARGET_WINDOWED_ABI + || (mode == SImode + && register_operand (operands[1], mode) + && !ACC_REG_P (xt_true_regnum (operands[1]))); if (!ACC_REG_P (dst_regnum)) return true; @@ -1665,9 +1647,10 @@ xtensa_setup_frame_addresses (void) /* Set flag to cause TARGET_FRAME_POINTER_REQUIRED to return true. */ cfun->machine->accesses_prev_frame = 1; - emit_library_call - (gen_rtx_SYMBOL_REF (Pmode, "__xtensa_libgcc_window_spill"), - LCT_NORMAL, VOIDmode, 0); + if (TARGET_WINDOWED_ABI) + emit_library_call + (gen_rtx_SYMBOL_REF (Pmode, "__xtensa_libgcc_window_spill"), + LCT_NORMAL, VOIDmode, 0); } @@ -1825,11 +1808,11 @@ xtensa_emit_call (int callop, rtx *operands) rtx tgt = operands[callop]; if (GET_CODE (tgt) == CONST_INT) - sprintf (result, "call8\t0x%lx", INTVAL (tgt)); + sprintf (result, "call%d\t0x%lx", WINDOW_SIZE, INTVAL (tgt)); else if (register_operand (tgt, VOIDmode)) - sprintf (result, "callx8\t%%%d", callop); + sprintf (result, "callx%d\t%%%d", WINDOW_SIZE, callop); else - sprintf (result, "call8\t%%%d", callop); + sprintf (result, "call%d\t%%%d", WINDOW_SIZE, callop); return result; } @@ -2174,7 +2157,7 @@ xtensa_function_arg_1 (cumulative_args_t cum_v, machine_mode mode, regno = regbase + *arg_words; if (cum->incoming && regno <= A7_REG && regno + words > A7_REG) - cfun->machine->need_a7_copy = true; + cfun->machine->need_a7_copy = TARGET_WINDOWED_ABI; return gen_rtx_REG (mode, regno); } @@ -2641,6 +2624,22 @@ xtensa_output_literal (FILE *file, rtx x, machine_mode mode, int labelno) } } +static bool +xtensa_call_save_reg(int regno) +{ + if (TARGET_WINDOWED_ABI) + return false; + + if (regno == A0_REG) + return crtl->profile || !crtl->is_leaf || crtl->calls_eh_return || + df_regs_ever_live_p (regno); + + if (crtl->calls_eh_return && regno >= 2 && regno < 4) + return true; + + return !fixed_regs[regno] && !call_used_regs[regno] && + df_regs_ever_live_p (regno); +} /* Return the bytes needed to compute the frame pointer from the current stack pointer. */ @@ -2651,14 +2650,25 @@ xtensa_output_literal (FILE *file, rtx x, machine_mode mode, int labelno) long compute_frame_size (int size) { + int regno; + /* Add space for the incoming static chain value. */ if (cfun->static_chain_decl != NULL) size += (1 * UNITS_PER_WORD); + xtensa_callee_save_size = 0; + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno) + { + if (xtensa_call_save_reg(regno)) + xtensa_callee_save_size += UNITS_PER_WORD; + } + xtensa_current_frame_size = XTENSA_STACK_ALIGN (size + + xtensa_callee_save_size + crtl->outgoing_args_size + (WINDOW_SIZE * UNITS_PER_WORD)); + xtensa_callee_save_size = XTENSA_STACK_ALIGN (xtensa_callee_save_size); return xtensa_current_frame_size; } @@ -2686,23 +2696,103 @@ void xtensa_expand_prologue (void) { HOST_WIDE_INT total_size; - rtx size_rtx; - rtx_insn *insn; + rtx_insn *insn = NULL; rtx note_rtx; + total_size = compute_frame_size (get_frame_size ()); - size_rtx = GEN_INT (total_size); - if (total_size < (1 << (12+3))) - insn = emit_insn (gen_entry (size_rtx)); + if (TARGET_WINDOWED_ABI) + { + if (total_size < (1 << (12+3))) + insn = emit_insn (gen_entry (GEN_INT (total_size))); + else + { + /* Use a8 as a temporary since a0-a7 may be live. */ + rtx tmp_reg = gen_rtx_REG (Pmode, A8_REG); + emit_insn (gen_entry (GEN_INT (MIN_FRAME_SIZE))); + emit_move_insn (tmp_reg, GEN_INT (total_size - MIN_FRAME_SIZE)); + emit_insn (gen_subsi3 (tmp_reg, stack_pointer_rtx, tmp_reg)); + insn = emit_insn (gen_movsi (stack_pointer_rtx, tmp_reg)); + } + } else { - /* Use a8 as a temporary since a0-a7 may be live. */ - rtx tmp_reg = gen_rtx_REG (Pmode, A8_REG); - emit_insn (gen_entry (GEN_INT (MIN_FRAME_SIZE))); - emit_move_insn (tmp_reg, GEN_INT (total_size - MIN_FRAME_SIZE)); - emit_insn (gen_subsi3 (tmp_reg, stack_pointer_rtx, tmp_reg)); - insn = emit_insn (gen_movsi (stack_pointer_rtx, tmp_reg)); + int regno; + HOST_WIDE_INT offset = 0; + + /* -128 is a limit of single addi instruction. */ + if (total_size > 0 && total_size <= 128) + { + insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (-total_size))); + RTX_FRAME_RELATED_P (insn) = 1; + note_rtx = gen_rtx_SET (VOIDmode, stack_pointer_rtx, + plus_constant (Pmode, stack_pointer_rtx, + -total_size)); + add_reg_note (insn, REG_FRAME_RELATED_EXPR, note_rtx); + offset = total_size - UNITS_PER_WORD; + } + else if (xtensa_callee_save_size) + { + /* 1020 is maximal s32i offset, if the frame is bigger than that + * we move sp to the end of callee-saved save area, save and then + * move it to its final location. */ + if (total_size > 1024) + { + insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (-xtensa_callee_save_size))); + RTX_FRAME_RELATED_P (insn) = 1; + note_rtx = gen_rtx_SET (VOIDmode, stack_pointer_rtx, + plus_constant (Pmode, stack_pointer_rtx, + -xtensa_callee_save_size)); + add_reg_note (insn, REG_FRAME_RELATED_EXPR, note_rtx); + offset = xtensa_callee_save_size - UNITS_PER_WORD; + } + else + { + rtx tmp_reg = gen_rtx_REG (Pmode, A9_REG); + emit_move_insn (tmp_reg, GEN_INT (total_size)); + insn = emit_insn (gen_subsi3 (stack_pointer_rtx, + stack_pointer_rtx, tmp_reg)); + RTX_FRAME_RELATED_P (insn) = 1; + note_rtx = gen_rtx_SET (VOIDmode, stack_pointer_rtx, + plus_constant (Pmode, stack_pointer_rtx, + -total_size)); + add_reg_note (insn, REG_FRAME_RELATED_EXPR, note_rtx); + offset = total_size - UNITS_PER_WORD; + } + } + + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno) + { + if (xtensa_call_save_reg(regno)) + { + rtx x = gen_rtx_PLUS (Pmode, stack_pointer_rtx, GEN_INT (offset)); + rtx mem = gen_frame_mem (SImode, x); + rtx reg = gen_rtx_REG (SImode, regno); + + offset -= UNITS_PER_WORD; + insn = emit_move_insn (mem, reg); + RTX_FRAME_RELATED_P (insn) = 1; + add_reg_note (insn, REG_FRAME_RELATED_EXPR, + gen_rtx_SET (VOIDmode, mem, reg)); + } + } + if (total_size > 1024) + { + rtx tmp_reg = gen_rtx_REG (Pmode, A9_REG); + emit_move_insn (tmp_reg, GEN_INT (total_size - + xtensa_callee_save_size)); + insn = emit_insn (gen_subsi3 (stack_pointer_rtx, + stack_pointer_rtx, tmp_reg)); + RTX_FRAME_RELATED_P (insn) = 1; + note_rtx = gen_rtx_SET (VOIDmode, stack_pointer_rtx, + plus_constant (Pmode, stack_pointer_rtx, + xtensa_callee_save_size - + total_size)); + add_reg_note (insn, REG_FRAME_RELATED_EXPR, note_rtx); + } } if (frame_pointer_needed) @@ -2731,38 +2821,147 @@ xtensa_expand_prologue (void) } } else - insn = emit_insn (gen_movsi (hard_frame_pointer_rtx, - stack_pointer_rtx)); - } - - /* Create a note to describe the CFA. Because this is only used to set - DW_AT_frame_base for debug info, don't bother tracking changes through - each instruction in the prologue. It just takes up space. */ - note_rtx = gen_rtx_SET (VOIDmode, (frame_pointer_needed - ? hard_frame_pointer_rtx - : stack_pointer_rtx), - plus_constant (Pmode, stack_pointer_rtx, - -total_size)); - RTX_FRAME_RELATED_P (insn) = 1; - add_reg_note (insn, REG_FRAME_RELATED_EXPR, note_rtx); -} - + { + insn = emit_insn (gen_movsi (hard_frame_pointer_rtx, + stack_pointer_rtx)); + if (!TARGET_WINDOWED_ABI) + { + note_rtx = gen_rtx_SET (VOIDmode, hard_frame_pointer_rtx, + stack_pointer_rtx); + RTX_FRAME_RELATED_P (insn) = 1; + add_reg_note (insn, REG_FRAME_RELATED_EXPR, note_rtx); + } + } + } -/* Clear variables at function end. */ + if (TARGET_WINDOWED_ABI) + { + /* Create a note to describe the CFA. Because this is only used to set + DW_AT_frame_base for debug info, don't bother tracking changes through + each instruction in the prologue. It just takes up space. */ + note_rtx = gen_rtx_SET (VOIDmode, (frame_pointer_needed + ? hard_frame_pointer_rtx + : stack_pointer_rtx), + plus_constant (Pmode, stack_pointer_rtx, + -total_size)); + RTX_FRAME_RELATED_P (insn) = 1; + add_reg_note (insn, REG_FRAME_RELATED_EXPR, note_rtx); + } +} void -xtensa_function_epilogue (FILE *file ATTRIBUTE_UNUSED, - HOST_WIDE_INT size ATTRIBUTE_UNUSED) +xtensa_expand_epilogue (void) { + if (!TARGET_WINDOWED_ABI) + { + int regno; + HOST_WIDE_INT offset; + + if (xtensa_current_frame_size > (frame_pointer_needed ? 127 : 1024)) + { + rtx tmp_reg = gen_rtx_REG (Pmode, A9_REG); + emit_move_insn (tmp_reg, GEN_INT (xtensa_current_frame_size - + xtensa_callee_save_size)); + emit_insn (gen_addsi3 (stack_pointer_rtx, frame_pointer_needed ? + hard_frame_pointer_rtx : stack_pointer_rtx, + tmp_reg)); + offset = xtensa_callee_save_size - UNITS_PER_WORD; + } + else + { + if (frame_pointer_needed) + emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx); + offset = xtensa_current_frame_size - UNITS_PER_WORD; + } + + /* Prevent reordering of saved a0 update and loading it back from + the save area. */ + if (crtl->calls_eh_return) + emit_insn (gen_blockage ()); + + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno) + { + if (xtensa_call_save_reg(regno)) + { + rtx x = gen_rtx_PLUS (Pmode, stack_pointer_rtx, GEN_INT (offset)); + + offset -= UNITS_PER_WORD; + emit_move_insn (gen_rtx_REG (SImode, regno), + gen_frame_mem (SImode, x)); + } + } + + if (xtensa_current_frame_size > 0) + { + if (frame_pointer_needed || /* always reachable with addi */ + xtensa_current_frame_size > 1024 || + xtensa_current_frame_size <= 127) + { + if (xtensa_current_frame_size <= 127) + offset = xtensa_current_frame_size; + else + offset = xtensa_callee_save_size; + + emit_insn (gen_addsi3 (stack_pointer_rtx, + stack_pointer_rtx, + GEN_INT (offset))); + } + else + { + rtx tmp_reg = gen_rtx_REG (Pmode, A9_REG); + emit_move_insn (tmp_reg, GEN_INT (xtensa_current_frame_size)); + emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + tmp_reg)); + } + } + + if (crtl->calls_eh_return) + emit_insn (gen_add3_insn (stack_pointer_rtx, + stack_pointer_rtx, + EH_RETURN_STACKADJ_RTX)); + } xtensa_current_frame_size = 0; + xtensa_callee_save_size = 0; + emit_jump_insn (gen_return ()); } +void +xtensa_set_return_address (rtx address, rtx scratch) +{ + HOST_WIDE_INT total_size = compute_frame_size (get_frame_size ()); + rtx frame = frame_pointer_needed ? + hard_frame_pointer_rtx : stack_pointer_rtx; + rtx a0_addr = plus_constant (Pmode, frame, + total_size - UNITS_PER_WORD); + rtx note = gen_rtx_SET (VOIDmode, + gen_frame_mem (SImode, a0_addr), + gen_rtx_REG (SImode, A0_REG)); + rtx insn; + + if (total_size > 1024) { + emit_move_insn (scratch, GEN_INT (total_size - UNITS_PER_WORD)); + emit_insn (gen_addsi3 (scratch, frame, scratch)); + a0_addr = scratch; + } + + insn = emit_move_insn (gen_frame_mem (SImode, a0_addr), address); + RTX_FRAME_RELATED_P (insn) = 1; + add_reg_note (insn, REG_FRAME_RELATED_EXPR, note); +} rtx xtensa_return_addr (int count, rtx frame) { rtx result, retaddr, curaddr, label; + if (!TARGET_WINDOWED_ABI) + { + if (count != 0) + return const0_rtx; + + return get_hard_reg_initial_val (Pmode, A0_REG); + } + if (count == -1) retaddr = gen_rtx_REG (Pmode, A0_REG); else @@ -2879,14 +3078,14 @@ xtensa_builtin_saveregs (void) set_mem_alias_set (gp_regs, get_varargs_alias_set ()); /* Now store the incoming registers. */ - cfun->machine->need_a7_copy = true; + cfun->machine->need_a7_copy = TARGET_WINDOWED_ABI; cfun->machine->vararg_a7 = true; move_block_from_reg (GP_ARG_FIRST + arg_words, adjust_address (gp_regs, BLKmode, arg_words * UNITS_PER_WORD), gp_left); - gcc_assert (cfun->machine->vararg_a7_copy != 0); - emit_insn_before (cfun->machine->vararg_a7_copy, get_insns ()); + if (cfun->machine->vararg_a7_copy != 0) + emit_insn_before (cfun->machine->vararg_a7_copy, get_insns ()); return XEXP (gp_regs, 0); } @@ -3272,7 +3471,19 @@ order_regs_for_local_alloc (void) { if (!leaf_function_p ()) { - memcpy (reg_alloc_order, reg_nonleaf_alloc_order, + static const int reg_nonleaf_alloc_order[FIRST_PSEUDO_REGISTER] = + REG_ALLOC_ORDER; + static const int reg_nonleaf_alloc_order_call0[FIRST_PSEUDO_REGISTER] = + { + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 12, 13, 14, 15, + 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 0, 1, 16, 17, + 35, + }; + + memcpy (reg_alloc_order, TARGET_WINDOWED_ABI ? + reg_nonleaf_alloc_order : reg_nonleaf_alloc_order_call0, FIRST_PSEUDO_REGISTER * sizeof (int)); } else @@ -3642,9 +3853,14 @@ xtensa_function_value_regno_p (const unsigned int regno) static rtx xtensa_static_chain (const_tree ARG_UNUSED (fndecl_or_type), bool incoming_p) { - rtx base = incoming_p ? arg_pointer_rtx : stack_pointer_rtx; - return gen_frame_mem (Pmode, plus_constant (Pmode, base, - -5 * UNITS_PER_WORD)); + if (TARGET_WINDOWED_ABI) + { + rtx base = incoming_p ? arg_pointer_rtx : stack_pointer_rtx; + return gen_frame_mem (Pmode, plus_constant (Pmode, base, + -5 * UNITS_PER_WORD)); + } + else + return gen_rtx_REG (Pmode, A8_REG); } @@ -3662,65 +3878,109 @@ xtensa_asm_trampoline_template (FILE *stream) bool use_call0 = (TARGET_CONST16 || TARGET_ABSOLUTE_LITERALS); fprintf (stream, "\t.begin no-transform\n"); - fprintf (stream, "\tentry\tsp, %d\n", MIN_FRAME_SIZE); - if (use_call0) + if (TARGET_WINDOWED_ABI) { - /* Save the return address. */ - fprintf (stream, "\tmov\ta10, a0\n"); + fprintf (stream, "\tentry\tsp, %d\n", MIN_FRAME_SIZE); - /* Use a CALL0 instruction to skip past the constants and in the - process get the PC into A0. This allows PC-relative access to - the constants without relying on L32R. */ - fprintf (stream, "\tcall0\t.Lskipconsts\n"); - } - else - fprintf (stream, "\tj\t.Lskipconsts\n"); + if (use_call0) + { + /* Save the return address. */ + fprintf (stream, "\tmov\ta10, a0\n"); - fprintf (stream, "\t.align\t4\n"); - fprintf (stream, ".Lchainval:%s0\n", integer_asm_op (4, TRUE)); - fprintf (stream, ".Lfnaddr:%s0\n", integer_asm_op (4, TRUE)); - fprintf (stream, ".Lskipconsts:\n"); + /* Use a CALL0 instruction to skip past the constants and in the + process get the PC into A0. This allows PC-relative access to + the constants without relying on L32R. */ + fprintf (stream, "\tcall0\t.Lskipconsts\n"); + } + else + fprintf (stream, "\tj\t.Lskipconsts\n"); - /* Load the static chain and function address from the trampoline. */ - if (use_call0) - { - fprintf (stream, "\taddi\ta0, a0, 3\n"); - fprintf (stream, "\tl32i\ta9, a0, 0\n"); - fprintf (stream, "\tl32i\ta8, a0, 4\n"); + fprintf (stream, "\t.align\t4\n"); + fprintf (stream, ".Lchainval:%s0\n", integer_asm_op (4, TRUE)); + fprintf (stream, ".Lfnaddr:%s0\n", integer_asm_op (4, TRUE)); + fprintf (stream, ".Lskipconsts:\n"); + + /* Load the static chain and function address from the trampoline. */ + if (use_call0) + { + fprintf (stream, "\taddi\ta0, a0, 3\n"); + fprintf (stream, "\tl32i\ta9, a0, 0\n"); + fprintf (stream, "\tl32i\ta8, a0, 4\n"); + } + else + { + fprintf (stream, "\tl32r\ta9, .Lchainval\n"); + fprintf (stream, "\tl32r\ta8, .Lfnaddr\n"); + } + + /* Store the static chain. */ + fprintf (stream, "\ts32i\ta9, sp, %d\n", MIN_FRAME_SIZE - 20); + + /* Set the proper stack pointer value. */ + fprintf (stream, "\tl32i\ta9, a8, 0\n"); + fprintf (stream, "\textui\ta9, a9, %d, 12\n", + TARGET_BIG_ENDIAN ? 8 : 12); + fprintf (stream, "\tslli\ta9, a9, 3\n"); + fprintf (stream, "\taddi\ta9, a9, %d\n", -MIN_FRAME_SIZE); + fprintf (stream, "\tsub\ta9, sp, a9\n"); + fprintf (stream, "\tmovsp\tsp, a9\n"); + + if (use_call0) + /* Restore the return address. */ + fprintf (stream, "\tmov\ta0, a10\n"); + + /* Jump to the instruction following the ENTRY. */ + fprintf (stream, "\taddi\ta8, a8, 3\n"); + fprintf (stream, "\tjx\ta8\n"); + + /* Pad size to a multiple of TRAMPOLINE_ALIGNMENT. */ + if (use_call0) + fprintf (stream, "\t.byte\t0\n"); + else + fprintf (stream, "\tnop\n"); } else { - fprintf (stream, "\tl32r\ta9, .Lchainval\n"); - fprintf (stream, "\tl32r\ta8, .Lfnaddr\n"); - } - - /* Store the static chain. */ - fprintf (stream, "\ts32i\ta9, sp, %d\n", MIN_FRAME_SIZE - 20); - - /* Set the proper stack pointer value. */ - fprintf (stream, "\tl32i\ta9, a8, 0\n"); - fprintf (stream, "\textui\ta9, a9, %d, 12\n", - TARGET_BIG_ENDIAN ? 8 : 12); - fprintf (stream, "\tslli\ta9, a9, 3\n"); - fprintf (stream, "\taddi\ta9, a9, %d\n", -MIN_FRAME_SIZE); - fprintf (stream, "\tsub\ta9, sp, a9\n"); - fprintf (stream, "\tmovsp\tsp, a9\n"); + if (use_call0) + { + /* Save the return address. */ + fprintf (stream, "\tmov\ta10, a0\n"); - if (use_call0) - /* Restore the return address. */ - fprintf (stream, "\tmov\ta0, a10\n"); + /* Use a CALL0 instruction to skip past the constants and in the + process get the PC into A0. This allows PC-relative access to + the constants without relying on L32R. */ + fprintf (stream, "\tcall0\t.Lskipconsts\n"); + } + else + fprintf (stream, "\tj\t.Lskipconsts\n"); - /* Jump to the instruction following the ENTRY. */ - fprintf (stream, "\taddi\ta8, a8, 3\n"); - fprintf (stream, "\tjx\ta8\n"); + fprintf (stream, "\t.align\t4\n"); + fprintf (stream, ".Lchainval:%s0\n", integer_asm_op (4, TRUE)); + fprintf (stream, ".Lfnaddr:%s0\n", integer_asm_op (4, TRUE)); + fprintf (stream, ".Lskipconsts:\n"); - /* Pad size to a multiple of TRAMPOLINE_ALIGNMENT. */ - if (use_call0) - fprintf (stream, "\t.byte\t0\n"); - else - fprintf (stream, "\tnop\n"); + /* Load the static chain and function address from the trampoline. */ + if (use_call0) + { + fprintf (stream, "\taddi\ta0, a0, 3\n"); + fprintf (stream, "\tl32i\ta8, a0, 0\n"); + fprintf (stream, "\tl32i\ta9, a0, 4\n"); + fprintf (stream, "\tmov\ta0, a10\n"); + } + else + { + fprintf (stream, "\tl32r\ta8, .Lchainval\n"); + fprintf (stream, "\tl32r\ta9, .Lfnaddr\n"); + } + fprintf (stream, "\tjx\ta9\n"); + /* Pad size to a multiple of TRAMPOLINE_ALIGNMENT. */ + if (use_call0) + fprintf (stream, "\t.byte\t0\n"); + else + fprintf (stream, "\tnop\n"); + } fprintf (stream, "\t.end no-transform\n"); } @@ -3729,8 +3989,19 @@ xtensa_trampoline_init (rtx m_tramp, tree fndecl, rtx chain) { rtx func = XEXP (DECL_RTL (fndecl), 0); bool use_call0 = (TARGET_CONST16 || TARGET_ABSOLUTE_LITERALS); - int chain_off = use_call0 ? 12 : 8; - int func_off = use_call0 ? 16 : 12; + int chain_off; + int func_off; + + if (TARGET_WINDOWED_ABI) + { + chain_off = use_call0 ? 12 : 8; + func_off = use_call0 ? 16 : 12; + } + else + { + chain_off = use_call0 ? 8 : 4; + func_off = use_call0 ? 12 : 8; + } emit_block_move (m_tramp, assemble_trampoline_template (), GEN_INT (TRAMPOLINE_SIZE), BLOCK_OP_NORMAL); @@ -3989,4 +4260,50 @@ xtensa_reorg (void) xtensa_reorg_loops (); } +/* Update register usage after having seen the compiler flags. */ + +static void +xtensa_conditional_register_usage (void) +{ + unsigned i, c_mask; + + c_mask = TARGET_WINDOWED_ABI ? (1 << 1) : (1 << 2); + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + /* Set/reset conditionally defined registers from + CALL_USED_REGISTERS initializer. */ + if (call_used_regs[i] > 1) + call_used_regs[i] = !!(call_used_regs[i] & c_mask); + } + + /* Remove hard FP register from the preferred reload registers set. */ + CLEAR_HARD_REG_BIT (reg_class_contents[(int)RL_REGS], + HARD_FRAME_POINTER_REGNUM); +} + +/* Map hard register number to register class */ + +enum reg_class xtensa_regno_to_class (int regno) +{ + static const enum reg_class regno_to_class[FIRST_PSEUDO_REGISTER] = + { + RL_REGS, SP_REG, RL_REGS, RL_REGS, + RL_REGS, RL_REGS, RL_REGS, RL_REGS, + RL_REGS, RL_REGS, RL_REGS, RL_REGS, + RL_REGS, RL_REGS, RL_REGS, RL_REGS, + AR_REGS, AR_REGS, BR_REGS, + FP_REGS, FP_REGS, FP_REGS, FP_REGS, + FP_REGS, FP_REGS, FP_REGS, FP_REGS, + FP_REGS, FP_REGS, FP_REGS, FP_REGS, + FP_REGS, FP_REGS, FP_REGS, FP_REGS, + ACC_REG, + }; + + if (regno == HARD_FRAME_POINTER_REGNUM) + return GR_REGS; + else + return regno_to_class[regno]; +} + #include "gt-xtensa.h" diff --git a/gcc/config/xtensa/xtensa.h b/gcc/config/xtensa/xtensa.h index 14fe4bb56ea..011411c09bf 100644 --- a/gcc/config/xtensa/xtensa.h +++ b/gcc/config/xtensa/xtensa.h @@ -66,6 +66,7 @@ extern unsigned xtensa_current_frame_size; #define TARGET_ABSOLUTE_LITERALS XSHAL_USE_ABSOLUTE_LITERALS #define TARGET_THREADPTR XCHAL_HAVE_THREADPTR #define TARGET_LOOPS XCHAL_HAVE_LOOPS +#define TARGET_WINDOWED_ABI (XSHAL_ABI == XTHAL_ABI_WINDOWED) #define TARGET_DEFAULT \ ((XCHAL_HAVE_L32R ? 0 : MASK_CONST16) | \ @@ -83,7 +84,8 @@ extern unsigned xtensa_current_frame_size; builtin_assert ("machine=xtensa"); \ builtin_define ("__xtensa__"); \ builtin_define ("__XTENSA__"); \ - builtin_define ("__XTENSA_WINDOWED_ABI__"); \ + builtin_define (TARGET_WINDOWED_ABI ? \ + "__XTENSA_WINDOWED_ABI__" : "__XTENSA_CALL0_ABI__");\ builtin_define (TARGET_BIG_ENDIAN ? "__XTENSA_EB__" : "__XTENSA_EL__"); \ if (!TARGET_HARD_FLOAT) \ builtin_define ("__XTENSA_SOFT_FLOAT__"); \ @@ -238,10 +240,18 @@ extern unsigned xtensa_current_frame_size; registers that can be used without being saved. The latter must include the registers where values are returned and the register where structure-value addresses are passed. - Aside from that, you can include as many other registers as you like. */ + Aside from that, you can include as many other registers as you like. + + The value encoding is the following: + 1: register is used by all ABIs; + bit 1 is set: register is used by windowed ABI; + bit 2 is set: register is used by call0 ABI. + + Proper values are computed in TARGET_CONDITIONAL_REGISTER_USAGE. */ + #define CALL_USED_REGISTERS \ { \ - 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 4, 4, 4, 4, 4, 4, 1, 1, 1, 1, 2, 2, 2, 2, \ 1, 1, 1, \ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ 1, \ @@ -341,7 +351,8 @@ extern char xtensa_hard_regno_mode_ok[][FIRST_PSEUDO_REGISTER]; #define STACK_POINTER_REGNUM (GP_REG_FIRST + 1) /* Base register for access to local variables of the function. */ -#define HARD_FRAME_POINTER_REGNUM (GP_REG_FIRST + 7) +#define HARD_FRAME_POINTER_REGNUM (GP_REG_FIRST + \ + (TARGET_WINDOWED_ABI ? 7 : 15)) /* The register number of the frame pointer register, which is used to access automatic variables in the stack frame. For Xtensa, this @@ -366,14 +377,16 @@ extern char xtensa_hard_regno_mode_ok[][FIRST_PSEUDO_REGISTER]; we use a fixed window size of 8. */ #define INCOMING_REGNO(OUT) \ - ((GP_REG_P (OUT) && \ - ((unsigned) ((OUT) - GP_REG_FIRST) >= WINDOW_SIZE)) ? \ - (OUT) - WINDOW_SIZE : (OUT)) + (TARGET_WINDOWED_ABI ? \ + ((GP_REG_P (OUT) && \ + ((unsigned) ((OUT) - GP_REG_FIRST) >= WINDOW_SIZE)) ? \ + (OUT) - WINDOW_SIZE : (OUT)) : (OUT)) #define OUTGOING_REGNO(IN) \ - ((GP_REG_P (IN) && \ - ((unsigned) ((IN) - GP_REG_FIRST) < WINDOW_SIZE)) ? \ - (IN) + WINDOW_SIZE : (IN)) + (TARGET_WINDOWED_ABI ? \ + ((GP_REG_P (IN) && \ + ((unsigned) ((IN) - GP_REG_FIRST) < WINDOW_SIZE)) ? \ + (IN) + WINDOW_SIZE : (IN)) : (IN)) /* Define the classes of registers for register constraints in the @@ -422,7 +435,7 @@ enum reg_class { 0xfff80000, 0x00000007 }, /* floating-point registers */ \ { 0x00000000, 0x00000008 }, /* MAC16 accumulator */ \ { 0x00000002, 0x00000000 }, /* stack pointer register */ \ - { 0x0000ff7d, 0x00000000 }, /* preferred reload registers */ \ + { 0x0000fffd, 0x00000000 }, /* preferred reload registers */ \ { 0x0000fffd, 0x00000000 }, /* general-purpose registers */ \ { 0x0003ffff, 0x00000000 }, /* integer registers */ \ { 0xffffffff, 0x0000000f } /* all registers */ \ @@ -432,9 +445,7 @@ enum reg_class register REGNO. In general there is more that one such class; choose a class which is "minimal", meaning that no smaller class also contains the register. */ -extern const enum reg_class xtensa_regno_to_class[FIRST_PSEUDO_REGISTER]; - -#define REGNO_REG_CLASS(REGNO) xtensa_regno_to_class[ (REGNO) ] +#define REGNO_REG_CLASS(REGNO) xtensa_regno_to_class (REGNO) /* Use the Xtensa AR register file for base registers. No index registers. */ @@ -497,7 +508,7 @@ extern const enum reg_class xtensa_regno_to_class[FIRST_PSEUDO_REGISTER]; #define STACK_BOUNDARY 128 /* Use a fixed register window size of 8. */ -#define WINDOW_SIZE 8 +#define WINDOW_SIZE (TARGET_WINDOWED_ABI ? 8 : 0) /* Symbolic macros for the registers used to return integer, floating point, and values of coprocessor and user-defined modes. */ @@ -561,11 +572,11 @@ typedef struct xtensa_args fprintf (FILE, "\t%s\ta10, a0\n", TARGET_DENSITY ? "mov.n" : "mov"); \ if (flag_pic) \ { \ - fprintf (FILE, "\tmovi\ta8, _mcount@PLT\n"); \ - fprintf (FILE, "\tcallx8\ta8\n"); \ + fprintf (FILE, "\tmovi\ta%d, _mcount@PLT\n", WINDOW_SIZE); \ + fprintf (FILE, "\tcallx%d\ta%d\n", WINDOW_SIZE, WINDOW_SIZE); \ } \ else \ - fprintf (FILE, "\tcall8\t_mcount\n"); \ + fprintf (FILE, "\tcall%d\t_mcount\n", WINDOW_SIZE); \ } while (0) /* Stack pointer value doesn't matter at exit. */ @@ -573,7 +584,11 @@ typedef struct xtensa_args /* Size in bytes of the trampoline, as an integer. Make sure this is a multiple of TRAMPOLINE_ALIGNMENT to avoid -Wpadded warnings. */ -#define TRAMPOLINE_SIZE (TARGET_CONST16 || TARGET_ABSOLUTE_LITERALS ? 60 : 52) +#define TRAMPOLINE_SIZE (TARGET_WINDOWED_ABI ? \ + (TARGET_CONST16 || TARGET_ABSOLUTE_LITERALS ? \ + 60 : 52) : \ + (TARGET_CONST16 || TARGET_ABSOLUTE_LITERALS ? \ + 32 : 24)) /* Alignment required for trampolines, in bits. */ #define TRAMPOLINE_ALIGNMENT 32 @@ -615,7 +630,7 @@ typedef struct xtensa_args /* Define this if the return address of a particular stack frame is accessed from the frame pointer of the previous stack frame. */ -#define RETURN_ADDR_IN_PREVIOUS_FRAME 1 +#define RETURN_ADDR_IN_PREVIOUS_FRAME TARGET_WINDOWED_ABI /* A C expression whose value is RTL representing the value of the return address for the frame COUNT steps up from the current @@ -770,7 +785,7 @@ typedef struct xtensa_args /* Define output to appear before the constant pool. */ #define ASM_OUTPUT_POOL_PROLOGUE(FILE, FUNNAME, FUNDECL, SIZE) \ do { \ - if ((SIZE) > 0) \ + if ((SIZE) > 0 || !TARGET_WINDOWED_ABI) \ { \ resolve_unique_section ((FUNDECL), 0, flag_function_sections); \ switch_to_section (function_section (FUNDECL)); \ @@ -805,6 +820,8 @@ typedef struct xtensa_args | DW_EH_PE_pcrel | DW_EH_PE_sdata4) \ : DW_EH_PE_absptr) +#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (Pmode, GP_REG_FIRST + 10) + /* Emit a PC-relative relocation. */ #define ASM_OUTPUT_DWARF_PCREL(FILE, SIZE, LABEL) \ do { \ @@ -818,8 +835,16 @@ typedef struct xtensa_args a MOVI and let the assembler relax it -- for the .init and .fini sections, the assembler knows to put the literal in the right place. */ +#if defined(__XTENSA_WINDOWED_ABI__) #define CRT_CALL_STATIC_FUNCTION(SECTION_OP, FUNC) \ asm (SECTION_OP "\n\ movi\ta8, " USER_LABEL_PREFIX #FUNC "\n\ callx8\ta8\n" \ TEXT_SECTION_ASM_OP); +#elif defined(__XTENSA_CALL0_ABI__) +#define CRT_CALL_STATIC_FUNCTION(SECTION_OP, FUNC) \ + asm (SECTION_OP "\n\ + movi\ta0, " USER_LABEL_PREFIX #FUNC "\n\ + callx0\ta0\n" \ + TEXT_SECTION_ASM_OP); +#endif diff --git a/gcc/config/xtensa/xtensa.md b/gcc/config/xtensa/xtensa.md index ccaa2b2f560..6d84384c950 100644 --- a/gcc/config/xtensa/xtensa.md +++ b/gcc/config/xtensa/xtensa.md @@ -24,6 +24,7 @@ (A1_REG 1) (A7_REG 7) (A8_REG 8) + (A9_REG 9) (UNSPEC_NOP 2) (UNSPEC_PLT 3) @@ -44,6 +45,7 @@ (UNSPECV_S32C1I 5) (UNSPECV_EH_RETURN 6) (UNSPECV_SET_TP 7) + (UNSPECV_BLOCKAGE 8) ]) ;; This code iterator allows signed and unsigned widening multiplications @@ -1658,9 +1660,11 @@ (define_insn "return" [(return) (use (reg:SI A0_REG))] - "reload_completed" + "(TARGET_WINDOWED_ABI || !xtensa_current_frame_size) && reload_completed" { - return (TARGET_DENSITY ? "retw.n" : "retw"); + return TARGET_WINDOWED_ABI ? + (TARGET_DENSITY ? "retw.n" : "retw") : + (TARGET_DENSITY ? "ret.n" : "ret"); } [(set_attr "type" "jump") (set_attr "mode" "none") @@ -1681,7 +1685,7 @@ [(return)] "" { - emit_jump_insn (gen_return ()); + xtensa_expand_epilogue (); DONE; }) @@ -1700,7 +1704,7 @@ (match_operand:SI 1 "general_operand" "") (match_operand:SI 2 "general_operand" "") (match_operand:SI 3 "" "")] - "" + "TARGET_WINDOWED_ABI" { xtensa_expand_nonlocal_goto (operands); DONE; @@ -1713,7 +1717,18 @@ ;; already been applied to the handler, but the generic version doesn't ;; allow us to frob it quite enough, so we just frob here. -(define_insn_and_split "eh_return" +(define_expand "eh_return" + [(use (match_operand 0 "general_operand"))] + "" +{ + if (TARGET_WINDOWED_ABI) + emit_insn (gen_eh_set_a0_windowed (operands[0])); + else + emit_insn (gen_eh_set_a0_call0 (operands[0])); + DONE; +}) + +(define_insn_and_split "eh_set_a0_windowed" [(set (reg:SI A0_REG) (unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")] UNSPECV_EH_RETURN)) @@ -1726,6 +1741,29 @@ (set (reg:SI A0_REG) (rotatert:SI (match_dup 1) (const_int 2)))] "") +(define_insn_and_split "eh_set_a0_call0" + [(unspec_volatile [(match_operand:SI 0 "register_operand" "r")] + UNSPECV_EH_RETURN) + (clobber (match_scratch:SI 1 "=r"))] + "" + "#" + "reload_completed" + [(const_int 0)] +{ + xtensa_set_return_address (operands[0], operands[1]); + DONE; +}) + +;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and +;; all of memory. This blocks insns from being moved across this point. + +(define_insn "blockage" + [(unspec_volatile [(const_int 0)] UNSPECV_BLOCKAGE)] + "" + "" + [(set_attr "length" "0") + (set_attr "type" "nop")]) + ;; Setting up a frame pointer is tricky for Xtensa because GCC doesn't ;; know if a frame pointer is required until the reload pass, and ;; because there may be an incoming argument value in the hard frame diff --git a/libgcc/ChangeLog b/libgcc/ChangeLog index 35e8f858f9b..49eee7a24a4 100644 --- a/libgcc/ChangeLog +++ b/libgcc/ChangeLog @@ -1,3 +1,16 @@ +2015-03-03 Max Filippov + + Implement call0 ABI for xtensa + * config/xtensa/lib2funcs.S (__xtensa_libgcc_window_spill, + __xtensa_nonlocal_goto): Don't compile for call0 ABI. + (__xtensa_sync_caches): Only use entry and retw in windowed ABI, + use ret in call0 ABI. + * config/xtensa/t-windowed: New file. + * libgcc/config/xtensa/t-xtensa (LIB2ADDEH): Move to t-windowed. + * libgcc/configure: Regenerated. + * libgcc/configure.ac: Check if xtensa target is configured for + windowed ABI and thus needs to use custom unwind code. + 2015-02-12 Jonathan Wakely PR libgcc/64885 diff --git a/libgcc/config/xtensa/lib2funcs.S b/libgcc/config/xtensa/lib2funcs.S index 1014a19732a..4d451c8c71c 100644 --- a/libgcc/config/xtensa/lib2funcs.S +++ b/libgcc/config/xtensa/lib2funcs.S @@ -29,6 +29,7 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see current register window. This is used to set up the stack so that arbitrary frames can be accessed. */ +#if XCHAL_HAVE_WINDOWED && !__XTENSA_CALL0_ABI__ .align 4 .global __xtensa_libgcc_window_spill .type __xtensa_libgcc_window_spill,@function @@ -38,6 +39,7 @@ __xtensa_libgcc_window_spill: syscall retw .size __xtensa_libgcc_window_spill, .-__xtensa_libgcc_window_spill +#endif /* __xtensa_nonlocal_goto: This code does all the hard work of a @@ -51,6 +53,7 @@ __xtensa_libgcc_window_spill: This function never returns to its caller but instead goes directly to the address of the specified goto handler. */ +#if XCHAL_HAVE_WINDOWED && !__XTENSA_CALL0_ABI__ .align 4 .global __xtensa_nonlocal_goto .type __xtensa_nonlocal_goto,@function @@ -128,6 +131,7 @@ __xtensa_nonlocal_goto: retw .size __xtensa_nonlocal_goto, .-__xtensa_nonlocal_goto +#endif /* __xtensa_sync_caches: This function is called after writing a trampoline @@ -154,7 +158,9 @@ __xtensa_nonlocal_goto: .global __xtensa_sync_caches .type __xtensa_sync_caches,@function __xtensa_sync_caches: +#if XCHAL_HAVE_WINDOWED && !__XTENSA_CALL0_ABI__ entry sp, 32 +#endif #if XCHAL_DCACHE_SIZE > 0 /* Flush the trampoline from the data cache. */ extui a4, a2, 0, XCHAL_DCACHE_LINEWIDTH @@ -182,5 +188,9 @@ __xtensa_sync_caches: bnez a4, .Licache_loop #endif isync +#if XCHAL_HAVE_WINDOWED && !__XTENSA_CALL0_ABI__ retw +#else + ret +#endif .size __xtensa_sync_caches, .-__xtensa_sync_caches diff --git a/libgcc/config/xtensa/linux-unwind.h b/libgcc/config/xtensa/linux-unwind.h index eed3be5cdce..9daf738ff57 100644 --- a/libgcc/config/xtensa/linux-unwind.h +++ b/libgcc/config/xtensa/linux-unwind.h @@ -52,6 +52,7 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see #define ENTRY_BYTE 0x36 #endif +#ifdef __XTENSA_WINDOWED_ABI__ #define MD_FALLBACK_FRAME_STATE_FOR xtensa_fallback_frame_state static _Unwind_Reason_Code @@ -94,4 +95,6 @@ xtensa_fallback_frame_state (struct _Unwind_Context *context, return _URC_NO_REASON; } +#endif /* __XTENSA_WINDOWED_ABI__ */ + #endif /* ifdef inhibit_libc */ diff --git a/libgcc/config/xtensa/t-windowed b/libgcc/config/xtensa/t-windowed new file mode 100644 index 00000000000..7d9e9db0487 --- /dev/null +++ b/libgcc/config/xtensa/t-windowed @@ -0,0 +1,2 @@ +LIB2ADDEH = $(srcdir)/config/xtensa/unwind-dw2-xtensa.c \ + $(srcdir)/unwind-dw2-fde.c $(srcdir)/unwind-sjlj.c $(srcdir)/unwind-c.c diff --git a/libgcc/config/xtensa/t-xtensa b/libgcc/config/xtensa/t-xtensa index 27399e67fa0..ed3eb84a71b 100644 --- a/libgcc/config/xtensa/t-xtensa +++ b/libgcc/config/xtensa/t-xtensa @@ -11,6 +11,3 @@ LIB1ASMFUNCS = _mulsi3 _divsi3 _modsi3 _udivsi3 _umodsi3 \ _truncdfsf2 _extendsfdf2 LIB2ADD = $(srcdir)/config/xtensa/lib2funcs.S - -LIB2ADDEH = $(srcdir)/config/xtensa/unwind-dw2-xtensa.c \ - $(srcdir)/unwind-dw2-fde.c $(srcdir)/unwind-sjlj.c $(srcdir)/unwind-c.c diff --git a/libgcc/configure b/libgcc/configure index a88259da325..9d567f9d48c 100644 --- a/libgcc/configure +++ b/libgcc/configure @@ -4810,6 +4810,27 @@ EOF ;; esac +# Check if xtensa target is configured for windowed ABI and thus needs to use +# custom unwind code. +# This is after config.host so we can augment tmake_file. +case ${host} in +xtensa*-*) + cat > conftest.c <&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + tmake_file="${tmake_file} xtensa/t-windowed" + fi + ;; +esac + # Check for visibility support. This is after config.host so that # we can check for asm_hidden_op. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__((visibility(\"hidden\")))" >&5 diff --git a/libgcc/configure.ac b/libgcc/configure.ac index 5edcbde69dd..1c405e8dfe5 100644 --- a/libgcc/configure.ac +++ b/libgcc/configure.ac @@ -436,6 +436,22 @@ EOF ;; esac +# Check if xtensa target is configured for windowed ABI and thus needs to use +# custom unwind code. +# This is after config.host so we can augment tmake_file. +case ${host} in +xtensa*-*) + cat > conftest.c <&AS_MESSAGE_LOG_FD); then + tmake_file="${tmake_file} xtensa/t-windowed" + fi + ;; +esac + # Check for visibility support. This is after config.host so that # we can check for asm_hidden_op. AC_CACHE_CHECK([for __attribute__((visibility("hidden")))],