From d8d72314c4667d88f62059c0964ea37756676f29 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 5 Jul 2007 20:04:36 +0000 Subject: [PATCH] function.c (match_asm_constraints_1, [...]): New. 2007-07-05 Paolo Bonzini * function.c (match_asm_constraints_1, rest_of_match_asm_constraints, pass_match_asm_constraints): New. * passes.c (init_optimization_passes): Add new pass. * stmt.c (expand_asm_operands): Set cfun->has_asm_statement. * function.h (struct function): Add has_asm_statement bit. (current_function_has_asm_statement): New. * tree-pass.h (pass_match_asm_constraints): New. From-SVN: r126385 --- gcc/ChangeLog | 10 ++ gcc/function.c | 137 ++++++++++++++++++++++++ gcc/function.h | 4 + gcc/passes.c | 1 + gcc/stmt.c | 1 + gcc/testsuite/gcc.target/i386/pr21291.c | 2 +- gcc/tree-pass.h | 1 + 7 files changed, 155 insertions(+), 1 deletion(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index a8ff7c063c7..30e5648febc 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,13 @@ +2007-07-05 Paolo Bonzini + + * function.c (match_asm_constraints_1, rest_of_match_asm_constraints, + pass_match_asm_constraints): New. + * passes.c (init_optimization_passes): Add new pass. + * stmt.c (expand_asm_operands): Set cfun->has_asm_statement. + * function.h (struct function): Add has_asm_statement bit. + (current_function_has_asm_statement): New. + * tree-pass.h (pass_match_asm_constraints): New. + 2007-07-05 Richard Sandiford * config/mips/mips.c (mips_file_start): Avoid declaration diff --git a/gcc/function.c b/gcc/function.c index 10fe7febe6c..a01bd9367bb 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -5504,6 +5504,143 @@ struct tree_opt_pass pass_thread_prologue_and_epilogue = TODO_ggc_collect, /* todo_flags_finish */ 'w' /* letter */ }; + + +/* This mini-pass fixes fall-out from SSA in asm statements that have + in-out constraints. Say you start with + + orig = inout; + asm ("": "+mr" (inout)); + use (orig); + + which is transformed very early to use explicit output and match operands: + + orig = inout; + asm ("": "=mr" (inout) : "0" (inout)); + use (orig); + + Or, after SSA and copyprop, + + asm ("": "=mr" (inout_2) : "0" (inout_1)); + use (inout_1); + + Clearly inout_2 and inout_1 can't be coalesced easily anymore, as + they represent two separate values, so they will get different pseudo + registers during expansion. Then, since the two operands need to match + per the constraints, but use different pseudo registers, reload can + only register a reload for these operands. But reloads can only be + satisfied by hardregs, not by memory, so we need a register for this + reload, just because we are presented with non-matching operands. + So, even though we allow memory for this operand, no memory can be + used for it, just because the two operands don't match. This can + cause reload failures on register-starved targets. + + So it's a symptom of reload not being able to use memory for reloads + or, alternatively it's also a symptom of both operands not coming into + reload as matching (in which case the pseudo could go to memory just + fine, as the alternative allows it, and no reload would be necessary). + We fix the latter problem here, by transforming + + asm ("": "=mr" (inout_2) : "0" (inout_1)); + + back to + + inout_2 = inout_1; + asm ("": "=mr" (inout_2) : "0" (inout_2)); */ + +static void +match_asm_constraints_1 (rtx insn, rtx *p_sets, int noutputs) +{ + int i; + bool changed = false; + rtx op = SET_SRC (p_sets[0]); + int ninputs = ASM_OPERANDS_INPUT_LENGTH (op); + rtvec inputs = ASM_OPERANDS_INPUT_VEC (op); + + for (i = 0; i < ninputs; i++) + { + rtx input, output, insns; + const char *constraint = ASM_OPERANDS_INPUT_CONSTRAINT (op, i); + char *end; + int match; + + match = strtoul (constraint, &end, 10); + if (end == constraint) + continue; + + gcc_assert (match < noutputs); + output = SET_DEST (p_sets[match]); + input = RTVEC_ELT (inputs, i); + if (rtx_equal_p (output, input) + || (GET_MODE (input) != VOIDmode + && GET_MODE (input) != GET_MODE (output))) + continue; + + start_sequence (); + emit_move_insn (copy_rtx (output), input); + RTVEC_ELT (inputs, i) = copy_rtx (output); + insns = get_insns (); + end_sequence (); + + emit_insn_before (insns, insn); + changed = true; + } + + if (changed) + df_insn_rescan (insn); +} + +static unsigned +rest_of_match_asm_constraints (void) +{ + basic_block bb; + rtx insn, pat, *p_sets; + int noutputs; + + if (!cfun->has_asm_statement) + return 0; + + df_set_flags (DF_DEFER_INSN_RESCAN); + FOR_EACH_BB (bb) + { + FOR_BB_INSNS (bb, insn) + { + if (!INSN_P (insn)) + continue; + + pat = PATTERN (insn); + if (GET_CODE (pat) == PARALLEL) + p_sets = &XVECEXP (pat, 0, 0), noutputs = XVECLEN (pat, 0); + else if (GET_CODE (pat) == SET) + p_sets = &PATTERN (insn), noutputs = 1; + else + continue; + + if (GET_CODE (*p_sets) == SET + && GET_CODE (SET_SRC (*p_sets)) == ASM_OPERANDS) + match_asm_constraints_1 (insn, p_sets, noutputs); + } + } + + return TODO_df_finish; +} + +struct tree_opt_pass pass_match_asm_constraints = +{ + "asmcons", /* name */ + NULL, /* gate */ + rest_of_match_asm_constraints, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func, /* todo_flags_finish */ + 0 /* letter */ +}; #include "gt-function.h" diff --git a/gcc/function.h b/gcc/function.h index bc3378b951d..0353983ac64 100644 --- a/gcc/function.h +++ b/gcc/function.h @@ -414,6 +414,9 @@ struct function GTY(()) /* Nonzero if function being compiled has nonlocal gotos to parent function. */ unsigned int has_nonlocal_goto : 1; + + /* Nonzero if function being compiled has an asm statement. */ + unsigned int has_asm_statement : 1; /* Nonzero if the current function is a thunk, i.e., a lightweight function implemented by the output_mi_thunk hook) that just @@ -517,6 +520,7 @@ extern int trampolines_created; #define current_function_has_nonlocal_label (cfun->has_nonlocal_label) #define current_function_calls_unwind_init (cfun->calls_unwind_init) #define current_function_has_nonlocal_goto (cfun->has_nonlocal_goto) +#define current_function_has_asm_statement (cfun->has_asm_statement) #define return_label (cfun->x_return_label) #define naked_return_label (cfun->x_naked_return_label) diff --git a/gcc/passes.c b/gcc/passes.c index c954847b82d..48ea68e0b8c 100644 --- a/gcc/passes.c +++ b/gcc/passes.c @@ -740,6 +740,7 @@ init_optimization_passes (void) NEXT_PASS (pass_stack_ptr_mod); NEXT_PASS (pass_mode_switching); NEXT_PASS (pass_see); + NEXT_PASS (pass_match_asm_constraints); NEXT_PASS (pass_sms); NEXT_PASS (pass_sched); NEXT_PASS (pass_subregs_of_mode_init); diff --git a/gcc/stmt.c b/gcc/stmt.c index 11cae96088a..2194a56f8cc 100644 --- a/gcc/stmt.c +++ b/gcc/stmt.c @@ -1078,6 +1078,7 @@ expand_asm_operands (tree string, tree outputs, tree inputs, if (real_output_rtx[i]) emit_move_insn (real_output_rtx[i], output_rtx[i]); + cfun->has_asm_statement = 1; free_temp_slots (); } diff --git a/gcc/testsuite/gcc.target/i386/pr21291.c b/gcc/testsuite/gcc.target/i386/pr21291.c index f7348bdf6e1..b597509854b 100644 --- a/gcc/testsuite/gcc.target/i386/pr21291.c +++ b/gcc/testsuite/gcc.target/i386/pr21291.c @@ -3,7 +3,7 @@ 6 registers that must not conflict. Add to that the PIC register, the frame pointer, and the stack pointer, and we've run out of registers on 32-bit targets. */ -/* { dg-do compile { target { { ! ilp32 } || nonpic } } } */ +/* { dg-do compile } */ /* { dg-options "-O" } */ typedef unsigned long bngdigit; diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 333ec41b853..8838127a440 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -390,6 +390,7 @@ extern struct tree_opt_pass pass_initialize_regs; extern struct tree_opt_pass pass_combine; extern struct tree_opt_pass pass_if_after_combine; extern struct tree_opt_pass pass_partition_blocks; +extern struct tree_opt_pass pass_match_asm_constraints; extern struct tree_opt_pass pass_regmove; extern struct tree_opt_pass pass_split_all_insns; extern struct tree_opt_pass pass_lower_subreg2;