diff options
Diffstat (limited to 'contrib/gcc/config/sparc/sparc.c')
-rw-r--r-- | contrib/gcc/config/sparc/sparc.c | 156 |
1 files changed, 105 insertions, 51 deletions
diff --git a/contrib/gcc/config/sparc/sparc.c b/contrib/gcc/config/sparc/sparc.c index c959370..0b07e46 100644 --- a/contrib/gcc/config/sparc/sparc.c +++ b/contrib/gcc/config/sparc/sparc.c @@ -187,6 +187,8 @@ static bool sparc_function_ok_for_sibcall (tree, tree); static void sparc_init_libfuncs (void); static void sparc_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree); +static bool sparc_can_output_mi_thunk (tree, HOST_WIDE_INT, + HOST_WIDE_INT, tree); static struct machine_function * sparc_init_machine_status (void); static bool sparc_cannot_force_const_mem (rtx); static rtx sparc_tls_get_addr (void); @@ -270,7 +272,7 @@ enum processor_type sparc_cpu; #undef TARGET_ASM_OUTPUT_MI_THUNK #define TARGET_ASM_OUTPUT_MI_THUNK sparc_output_mi_thunk #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK -#define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall +#define TARGET_ASM_CAN_OUTPUT_MI_THUNK sparc_can_output_mi_thunk #undef TARGET_RTX_COSTS #define TARGET_RTX_COSTS sparc_rtx_costs @@ -1315,23 +1317,7 @@ input_operand (rtx op, enum machine_mode mode) /* Check for valid MEM forms. */ if (GET_CODE (op) == MEM) - { - rtx inside = XEXP (op, 0); - - if (GET_CODE (inside) == LO_SUM) - { - /* We can't allow these because all of the splits - (eventually as they trickle down into DFmode - splits) require offsettable memory references. */ - if (! TARGET_V9 - && GET_MODE (op) == TFmode) - return 0; - - return (register_operand (XEXP (inside, 0), Pmode) - && CONSTANT_P (XEXP (inside, 1))); - } - return memory_address_p (mode, inside); - } + return memory_address_p (mode, XEXP (op, 0)); return 0; } @@ -3334,7 +3320,7 @@ legitimate_pic_operand_p (rtx x) int legitimate_address_p (enum machine_mode mode, rtx addr, int strict) { - rtx rs1 = NULL, rs2 = NULL, imm1 = NULL, imm2; + rtx rs1 = NULL, rs2 = NULL, imm1 = NULL; if (REG_P (addr) || GET_CODE (addr) == SUBREG) rs1 = addr; @@ -3374,15 +3360,14 @@ legitimate_address_p (enum machine_mode mode, rtx addr, int strict) else if ((REG_P (rs1) || GET_CODE (rs1) == SUBREG) && (REG_P (rs2) || GET_CODE (rs2) == SUBREG)) { - /* We prohibit REG + REG for TFmode when there are no instructions - which accept REG+REG instructions. We do this because REG+REG - is not an offsetable address. If we get the situation in reload + /* We prohibit REG + REG for TFmode when there are no quad move insns + and we consequently need to split. We do this because REG+REG + is not an offsettable address. If we get the situation in reload where source and destination of a movtf pattern are both MEMs with REG+REG address, then only one of them gets converted to an - offsetable address. */ + offsettable address. */ if (mode == TFmode - && !(TARGET_FPU && TARGET_ARCH64 && TARGET_V9 - && TARGET_HARD_QUAD)) + && ! (TARGET_FPU && TARGET_ARCH64 && TARGET_HARD_QUAD)) return 0; /* We prohibit REG + REG on ARCH32 if not optimizing for @@ -3399,7 +3384,6 @@ legitimate_address_p (enum machine_mode mode, rtx addr, int strict) && ! TARGET_CM_MEDMID && RTX_OK_FOR_OLO10_P (rs2)) { - imm2 = rs2; rs2 = NULL; imm1 = XEXP (rs1, 1); rs1 = XEXP (rs1, 0); @@ -3415,9 +3399,9 @@ legitimate_address_p (enum machine_mode mode, rtx addr, int strict) if (! CONSTANT_P (imm1) || tls_symbolic_operand (rs1)) return 0; - /* We can't allow TFmode, because an offset greater than or equal to the - alignment (8) may cause the LO_SUM to overflow if !v9. */ - if (mode == TFmode && !TARGET_V9) + /* We can't allow TFmode in 32-bit mode, because an offset greater + than the alignment (8) may cause the LO_SUM to overflow. */ + if (mode == TFmode && TARGET_ARCH32) return 0; } else if (GET_CODE (addr) == CONST_INT && SMALL_INT (addr)) @@ -5101,7 +5085,7 @@ static void function_arg_record_value_2 static void function_arg_record_value_1 (tree, HOST_WIDE_INT, struct function_arg_record_value_parms *, bool); static rtx function_arg_record_value (tree, enum machine_mode, int, int, int); -static rtx function_arg_union_value (int, enum machine_mode, int); +static rtx function_arg_union_value (int, enum machine_mode, int, int); /* A subroutine of function_arg_record_value. Traverse the structure recursively and determine how many registers will be required. */ @@ -5445,11 +5429,19 @@ function_arg_record_value (tree type, enum machine_mode mode, REGNO is the hard register the union will be passed in. */ static rtx -function_arg_union_value (int size, enum machine_mode mode, int regno) +function_arg_union_value (int size, enum machine_mode mode, int slotno, + int regno) { int nwords = ROUND_ADVANCE (size), i; rtx regs; + /* See comment in previous function for empty structures. */ + if (nwords == 0) + return gen_rtx_REG (mode, regno); + + if (slotno == SPARC_INT_ARG_MAX - 1) + nwords = 1; + /* Unions are passed left-justified. */ regs = gen_rtx_PARALLEL (mode, rtvec_alloc (nwords)); @@ -5516,7 +5508,7 @@ function_arg (const struct sparc_args *cum, enum machine_mode mode, if (size > 16) abort (); /* shouldn't get here */ - return function_arg_union_value (size, mode, regno); + return function_arg_union_value (size, mode, slotno, regno); } /* v9 fp args in reg slots beyond the int reg slots get passed in regs but also have the slot allocated for them. @@ -5796,7 +5788,7 @@ function_value (tree type, enum machine_mode mode, int incoming_p) if (size > 32) abort (); /* shouldn't get here */ - return function_arg_union_value (size, mode, regbase); + return function_arg_union_value (size, mode, 0, regbase); } else if (AGGREGATE_TYPE_P (type)) { @@ -5819,7 +5811,7 @@ function_value (tree type, enum machine_mode mode, int incoming_p) try to be unduly clever, and simply follow the ABI for unions in that case. */ if (mode == BLKmode) - return function_arg_union_value (bytes, mode, regbase); + return function_arg_union_value (bytes, mode, 0, regbase); } else if (GET_MODE_CLASS (mode) == MODE_INT && GET_MODE_SIZE (mode) < UNITS_PER_WORD) @@ -9288,16 +9280,18 @@ sparc_rtx_costs (rtx x, int code, int outer_code, int *total) } } -/* Output code to add DELTA to the first argument, and then jump to FUNCTION. - Used for C++ multiple inheritance. */ +/* Output the assembler code for a thunk function. THUNK_DECL is the + declaration for the thunk function itself, FUNCTION is the decl for + the target function. DELTA is an immediate constant offset to be + added to THIS. If VCALL_OFFSET is nonzero, the word at address + (*THIS + VCALL_OFFSET) should be additionally added to THIS. */ static void sparc_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED, - HOST_WIDE_INT delta, - HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED, + HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset, tree function) { - rtx this, insn, funexp, delta_rtx, tmp; + rtx this, insn, funexp; reload_completed = 1; epilogue_completed = 1; @@ -9315,26 +9309,73 @@ sparc_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED, /* Add DELTA. When possible use a plain add, otherwise load it into a register first. */ - delta_rtx = GEN_INT (delta); - if (!SPARC_SIMM13_P (delta)) + if (delta) + { + rtx delta_rtx = GEN_INT (delta); + + if (! SPARC_SIMM13_P (delta)) + { + rtx scratch = gen_rtx_REG (Pmode, 1); + emit_move_insn (scratch, delta_rtx); + delta_rtx = scratch; + } + + /* THIS += DELTA. */ + emit_insn (gen_add2_insn (this, delta_rtx)); + } + + /* Add the word at address (*THIS + VCALL_OFFSET). */ + if (vcall_offset) { + rtx vcall_offset_rtx = GEN_INT (vcall_offset); rtx scratch = gen_rtx_REG (Pmode, 1); - if (input_operand (delta_rtx, GET_MODE (scratch))) - emit_insn (gen_rtx_SET (VOIDmode, scratch, delta_rtx)); + if (vcall_offset >= 0) + abort (); + + /* SCRATCH = *THIS. */ + emit_move_insn (scratch, gen_rtx_MEM (Pmode, this)); + + /* Prepare for adding VCALL_OFFSET. The difficulty is that we + may not have any available scratch register at this point. */ + if (SPARC_SIMM13_P (vcall_offset)) + ; + /* This is the case if ARCH64 (unless -ffixed-g5 is passed). */ + else if (! fixed_regs[5] + /* The below sequence is made up of at least 2 insns, + while the default method may need only one. */ + && vcall_offset < -8192) + { + rtx scratch2 = gen_rtx_REG (Pmode, 5); + emit_move_insn (scratch2, vcall_offset_rtx); + vcall_offset_rtx = scratch2; + } else { - if (TARGET_ARCH64) - sparc_emit_set_const64 (scratch, delta_rtx); - else - sparc_emit_set_const32 (scratch, delta_rtx); + rtx increment = GEN_INT (-4096); + + /* VCALL_OFFSET is a negative number whose typical range can be + estimated as -32768..0 in 32-bit mode. In almost all cases + it is therefore cheaper to emit multiple add insns than + spilling and loading the constant into a register (at least + 6 insns). */ + while (! SPARC_SIMM13_P (vcall_offset)) + { + emit_insn (gen_add2_insn (scratch, increment)); + vcall_offset += 4096; + } + vcall_offset_rtx = GEN_INT (vcall_offset); /* cannot be 0 */ } - delta_rtx = scratch; - } + /* SCRATCH = *(*THIS + VCALL_OFFSET). */ + emit_move_insn (scratch, gen_rtx_MEM (Pmode, + gen_rtx_PLUS (Pmode, + scratch, + vcall_offset_rtx))); - tmp = gen_rtx_PLUS (Pmode, this, delta_rtx); - emit_insn (gen_rtx_SET (VOIDmode, this, tmp)); + /* THIS += *(*THIS + VCALL_OFFSET). */ + emit_insn (gen_add2_insn (this, scratch)); + } /* Generate a tail call to the target function. */ if (! TREE_USED (function)) @@ -9364,6 +9405,19 @@ sparc_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED, no_new_pseudos = 0; } +/* Return true if sparc_output_mi_thunk would be able to output the + assembler code for the thunk function specified by the arguments + it is passed, and false otherwise. */ +static bool +sparc_can_output_mi_thunk (tree thunk_fndecl ATTRIBUTE_UNUSED, + HOST_WIDE_INT delta ATTRIBUTE_UNUSED, + HOST_WIDE_INT vcall_offset, + tree function ATTRIBUTE_UNUSED) +{ + /* Bound the loop used in the default method above. */ + return (vcall_offset >= -32768 || ! fixed_regs[5]); +} + /* How to allocate a 'struct machine_function'. */ static struct machine_function * |