Index: gcc/testsuite/gcc.target/i386/explicit-register-earlyclobber.c =================================================================== --- gcc/testsuite/gcc.target/i386/explicit-register-earlyclobber.c (revision 0) +++ gcc/testsuite/gcc.target/i386/explicit-register-earlyclobber.c (revision 0) @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ +/* { dg-final { scan-assembler-times "operand0:%eax" 2 } } */ + +static int test(int num) +{ + register int v0 __asm__("eax"); + // We want to put operand0 in eax and put operand1 elsewhere. + asm volatile ("# Result: operand0:%0 operand1:%1" :"=&r"(v0): "r"(num)); + return v0; +} + +int t2() +{ + int i = test(1234); + int j = test(i); + return j; +} Index: gcc/ira-int.h =================================================================== --- gcc/ira-int.h (revision 228019) +++ gcc/ira-int.h (working copy) @@ -380,6 +380,10 @@ struct ira_allocno int cheap_calls_crossed_num; /* Registers clobbered by intersected calls. */ HARD_REG_SET crossed_calls_clobbered_regs; + /* Registers that can not be allocated for this allocno, for example because + there is an ASM_OPERANDS with that register as an output and this pseudo + as an earlyclobber input. */ + HARD_REG_SET forbidden_regs; /* Array of usage costs (accumulated and the one updated during coloring) for each hard register of the allocno class. The member value can be NULL if all costs are the same and equal to @@ -440,6 +444,7 @@ struct ira_allocno #define ALLOCNO_WMODE(A) ((A)->wmode) #define ALLOCNO_PREFS(A) ((A)->allocno_prefs) #define ALLOCNO_COPIES(A) ((A)->allocno_copies) +#define ALLOCNO_FORBIDDEN_REGS(A) ((A)->forbidden_regs) #define ALLOCNO_HARD_REG_COSTS(A) ((A)->hard_reg_costs) #define ALLOCNO_UPDATED_HARD_REG_COSTS(A) ((A)->updated_hard_reg_costs) #define ALLOCNO_CONFLICT_HARD_REG_COSTS(A) \ Index: gcc/ira-color.c =================================================================== --- gcc/ira-color.c (revision 228019) +++ gcc/ira-color.c (working copy) @@ -1584,6 +1584,9 @@ check_hard_reg_p (ira_allocno_t a, int h /* Checking only profitable hard regs. */ if (! TEST_HARD_REG_BIT (profitable_regs, hard_regno)) return false; + /* Don't use a forbidden hard reg. */ + if (TEST_HARD_REG_BIT (ALLOCNO_FORBIDDEN_REGS (a), hard_regno)) + return false; nregs = hard_regno_nregs[hard_regno][mode]; nwords = ALLOCNO_NUM_OBJECTS (a); for (j = 0; j < nregs; j++) Index: gcc/recog.c =================================================================== --- gcc/recog.c (revision 228019) +++ gcc/recog.c (working copy) @@ -139,6 +139,19 @@ asm_labels_ok (rtx body) return true; } +bool +asm_constraint_earlyclobber (const char *constraint) +{ + while (*constraint != 0) + { + if (*constraint == '&') + return true; + ++constraint; + } + return false; +} + + /* Check that X is an insn-body for an `asm' with operands and that the operands mentioned in it are legitimate. */ @@ -179,6 +192,24 @@ check_asm_operands (rtx x) const char *c = constraints[i]; if (c[0] == '%') c++; + if (asm_constraint_earlyclobber (c)) + { + rtx op = operands[i]; + if (REG_P (op)) + { + unsigned int regno = REGNO (op); + if (regno < FIRST_PSEUDO_REGISTER) + for (int j = 0; j < noperands; j++) + { + rtx op2; + if (j == i) + continue; + op2 = operands[j]; + if (REG_P (op2) && REGNO (op2) == regno) + return 0; + } + } + } if (! asm_operand_ok (operands[i], c, constraints)) return 0; } Index: gcc/recog.h =================================================================== --- gcc/recog.h (revision 228019) +++ gcc/recog.h (working copy) @@ -84,6 +84,7 @@ alternative_class (const operand_alterna extern void init_recog (void); extern void init_recog_no_volatile (void); +extern bool asm_constraint_earlyclobber (const char *); extern int check_asm_operands (rtx); extern int asm_operand_ok (rtx, const char *, const char **); extern bool validate_change (rtx, rtx *, rtx, bool); Index: gcc/ira-build.c =================================================================== --- gcc/ira-build.c (revision 228019) +++ gcc/ira-build.c (working copy) @@ -512,6 +512,7 @@ ira_create_allocno (int regno, bool cap_ ALLOCNO_CALLS_CROSSED_NUM (a) = 0; ALLOCNO_CHEAP_CALLS_CROSSED_NUM (a) = 0; CLEAR_HARD_REG_SET (ALLOCNO_CROSSED_CALLS_CLOBBERED_REGS (a)); + CLEAR_HARD_REG_SET (ALLOCNO_FORBIDDEN_REGS (a)); #ifdef STACK_REGS ALLOCNO_NO_STACK_REG_P (a) = false; ALLOCNO_TOTAL_NO_STACK_REG_P (a) = false; Index: gcc/ira-costs.c =================================================================== --- gcc/ira-costs.c (revision 228019) +++ gcc/ira-costs.c (working copy) @@ -1983,6 +1983,62 @@ find_costs_and_classes (FILE *dump_file) +/* Subroutine of process_bb_node_for_asm_operands. Processes a single + asm_operands set. */ + +static void +process_asm_operands_set (rtx set) +{ + if (GET_CODE (set) != SET) + return; + rtx dst = SET_DEST (set); + if (! REG_P (dst)) + return; + int dst_regno = REGNO (dst); + if (dst_regno >= FIRST_PSEUDO_REGISTER) + return; + + int noperands = asm_noperands (set); + if (noperands < 1) + return; + rtx asmop = SET_SRC (set); + if (!asm_constraint_earlyclobber (ASM_OPERANDS_OUTPUT_CONSTRAINT (asmop))) + return; + for (int i = 0; i < ASM_OPERANDS_INPUT_LENGTH (asmop); i++) + { + rtx src = ASM_OPERANDS_INPUT (asmop, i); + if (! REG_P (src)) + continue; + int src_regno = REGNO (src); + if (src_regno < FIRST_PSEUDO_REGISTER) + continue; + ira_allocno_t a = ira_curr_regno_allocno_map[src_regno]; + SET_HARD_REG_BIT (ALLOCNO_FORBIDDEN_REGS (a), dst_regno); + } +} + +/* Process insns with asm_operands patterns to forbid allocations that would + violate earlyclobber rules. */ +static void +process_bb_node_for_asm_operands (ira_loop_tree_node_t loop_tree_node) +{ + basic_block bb = loop_tree_node->bb; + if (bb == NULL) + return; + rtx_insn *insn; + FOR_BB_INSNS (bb, insn) + { + if (!NONDEBUG_INSN_P (insn) || !INSN_P (insn)) + continue; + rtx body = PATTERN (insn); + if (GET_CODE (body) == PARALLEL) + for (int i = 0; i < XVECLEN (body, 0); i++) + process_asm_operands_set (XVECEXP (body, 0, i)); + else + process_asm_operands_set (body); + } +} + /* Process moves involving hard regs to modify allocno hard register costs. We can do this only after determining allocno class. If a hard register forms a register class, then moves with the hard @@ -2118,6 +2174,8 @@ setup_allocno_class_and_costs (void) } } } + ira_traverse_loop_tree (true, ira_loop_tree_root, + process_bb_node_for_asm_operands, NULL); if (optimize) ira_traverse_loop_tree (true, ira_loop_tree_root, process_bb_node_for_hard_reg_moves, NULL);