diff options
Diffstat (limited to 'gcc/function.cc')
-rw-r--r-- | gcc/function.cc | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/gcc/function.cc b/gcc/function.cc index 48167b0c..2b77bbd 100644 --- a/gcc/function.cc +++ b/gcc/function.cc @@ -7009,6 +7009,115 @@ match_asm_constraints_1 (rtx_insn *insn, rtx *p_sets, int noutputs) df_insn_rescan (insn); } +/* It is expected and desired that optimizations coalesce multiple pseudos into + one whenever possible. However, in case of hard register constraints we may + have to undo this and introduce copies since otherwise we could constraint a + single pseudo to different hard registers. For example, during register + allocation the following insn would be unsatisfiable since pseudo 60 is + constrained to hard register r5 and r6 at the same time. + + (insn 7 5 0 2 (asm_operands/v ("foo") ("") 0 [ + (reg:DI 60) repeated x2 + ] + [ + (asm_input:DI ("{r5}") t.c:4) + (asm_input:DI ("{r6}") t.c:4) + ] + [] t.c:4) "t.c":4:3 -1 + (expr_list:REG_DEAD (reg:DI 60) + (nil))) + + Therefore, introduce a copy of pseudo 60 and transform it into + + (insn 10 5 7 2 (set (reg:DI 62) + (reg:DI 60)) "t.c":4:3 1503 {*movdi_64} + (nil)) + (insn 7 10 11 2 (asm_operands/v ("foo") ("") 0 [ + (reg:DI 60) + (reg:DI 62) + ] + [ + (asm_input:DI ("{r5}") t.c:4) + (asm_input:DI ("{r6}") t.c:4) + ] + [] t.c:4) "t.c":4:3 -1 + (expr_list:REG_DEAD (reg:DI 62) + (expr_list:REG_DEAD (reg:DI 60) + (nil)))) + + Now, LRA can assign pseudo 60 to r5, and pseudo 62 to r6. + + TODO: The current implementation is conservative and we could do a bit + better in case of alternatives. For example + + (insn 7 5 0 2 (asm_operands/v ("foo") ("") 0 [ + (reg:DI 60) repeated x2 + ] + [ + (asm_input:DI ("r,{r5}") t.c:4) + (asm_input:DI ("{r6},r") t.c:4) + ] + [] t.c:4) "t.c":4:3 -1 + (expr_list:REG_DEAD (reg:DI 60) + (nil))) + + For this insn we wouldn't need to come up with a copy of pseudo 60 since in + each alternative pseudo 60 is constrained exactly one time. */ + +static void +match_asm_constraints_2 (rtx_insn *insn, rtx pat) +{ + rtx op; + if (GET_CODE (pat) == SET && GET_CODE (SET_SRC (pat)) == ASM_OPERANDS) + op = SET_SRC (pat); + else if (GET_CODE (pat) == ASM_OPERANDS) + op = pat; + else + return; + int ninputs = ASM_OPERANDS_INPUT_LENGTH (op); + rtvec inputs = ASM_OPERANDS_INPUT_VEC (op); + bool changed = false; + auto_bitmap constrained_regs; + + for (int i = 0; i < ninputs; ++i) + { + rtx input = RTVEC_ELT (inputs, i); + const char *constraint = ASM_OPERANDS_INPUT_CONSTRAINT (op, i); + if ((!REG_P (input) && !SUBREG_P (input)) + || (REG_P (input) && HARD_REGISTER_P (input)) + || strchr (constraint, '{') == nullptr) + continue; + int regno; + if (SUBREG_P (input)) + { + if (REG_P (SUBREG_REG (input))) + regno = REGNO (SUBREG_REG (input)); + else + continue; + } + else + regno = REGNO (input); + /* Keep the first usage of a constrained pseudo as is and only + introduce copies for subsequent usages. */ + if (! bitmap_bit_p (constrained_regs, regno)) + { + bitmap_set_bit (constrained_regs, regno); + continue; + } + rtx tmp = gen_reg_rtx (GET_MODE (input)); + start_sequence (); + emit_move_insn (tmp, input); + rtx_insn *insns = get_insns (); + end_sequence (); + emit_insn_before (insns, insn); + RTVEC_ELT (inputs, i) = tmp; + changed = true; + } + + if (changed) + df_insn_rescan (insn); +} + /* Add the decl D to the local_decls list of FUN. */ void @@ -7065,6 +7174,13 @@ pass_match_asm_constraints::execute (function *fun) continue; pat = PATTERN (insn); + + if (GET_CODE (pat) == PARALLEL) + for (int i = XVECLEN (pat, 0) - 1; i >= 0; --i) + match_asm_constraints_2 (insn, XVECEXP (pat, 0, i)); + else + match_asm_constraints_2 (insn, pat); + if (GET_CODE (pat) == PARALLEL) p_sets = &XVECEXP (pat, 0, 0), noutputs = XVECLEN (pat, 0); else if (GET_CODE (pat) == SET) |