(define_insn "*movsi_1"
[(set (match_operand:SI 0 "nonimmediate_operand" "=*?a,r,*?a,m,!*y,!rm,!*y,!*Y,!rm,!*Y")
- (match_operand:SI 1 "general_operand" "im,rinm,rinm,rin,rm,*y,*y,rm,*Y,*Y"))]
- "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+ (match_operand:SI 1 "general_operand" "im,rinm,rinm,rin,*y,*y,rm,*Y,*Y,rm"))]
+ "(TARGET_INTER_UNIT_MOVES || optimize_size)
+ && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)"
+{
+ switch (get_attr_type (insn))
+ {
+ case TYPE_SSEMOV:
+ if (get_attr_mode (insn) == TImode)
+ return "movdqa\t{%1, %0|%0, %1}";
+ return "movd\t{%1, %0|%0, %1}";
+
+ case TYPE_MMXMOV:
+ if (get_attr_mode (insn) == DImode)
+ return "movq\t{%1, %0|%0, %1}";
+ return "movd\t{%1, %0|%0, %1}";
+
+ case TYPE_LEA:
+ return "lea{l}\t{%1, %0|%0, %1}";
+
+ default:
+ if (flag_pic && !LEGITIMATE_PIC_OPERAND_P (operands[1]))
+ abort();
+ return "mov{l}\t{%1, %0|%0, %1}";
+ }
+}
+ [(set (attr "type")
+ (cond [(eq_attr "alternative" "4,5,6")
+ (const_string "mmxmov")
+ (eq_attr "alternative" "7,8,9")
+ (const_string "ssemov")
+ (and (ne (symbol_ref "flag_pic") (const_int 0))
+ (match_operand:SI 1 "symbolic_operand" ""))
+ (const_string "lea")
+ ]
+ (const_string "imov")))
+ (set_attr "modrm" "0,*,0,*,*,*,*,*,*,*")
+ (set_attr "mode" "SI,SI,SI,SI,DI,SI,SI,TI,SI,SI")])
+
+(define_insn "*movsi_1_nointernunit"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=*?a,r,*?a,m,!*y,!m,!*y,!*Y,!m,!*Y")
+ (match_operand:SI 1 "general_operand" "im,rinm,rinm,rin,*y,*y,m,*Y,*Y,m"))]
+ "(!TARGET_INTER_UNIT_MOVES && !optimize_size)
+ && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)"
{
switch (get_attr_type (insn))
{
]
(const_string "imov")))
(set_attr "modrm" "0,*,0,*,*,*,*,*,*,*")
- (set_attr "mode" "SI,SI,SI,SI,SI,SI,DI,TI,SI,SI")])
+ (set_attr "mode" "SI,SI,SI,SI,DI,SI,SI,TI,SI,SI")])
;; Stores and loads of ax to arbitrary constant address.
;; We fake an second form of instruction to force reload to load address
"ix86_split_long_move (operands); DONE;")
(define_insn "*movdi_1_rex64"
- [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,r,mr,!mr,!m*y,!*y,!*Y,!m,!*Y")
- (match_operand:DI 1 "general_operand" "Z,rem,i,re,n,*y,m,*Y,*Y,*m"))]
+ [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,r,mr,!mr,!*y,!rm,!*y,!*Y,!rm,!*Y")
+ (match_operand:DI 1 "general_operand" "Z,rem,i,re,n,*y,*y,rm,*Y,*Y,rm"))]
"TARGET_64BIT
+ && (TARGET_INTER_UNIT_MOVES || optimize_size)
&& (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)"
{
switch (get_attr_type (insn))
{
case TYPE_SSEMOV:
- if (register_operand (operands[0], DImode)
- && register_operand (operands[1], DImode))
+ if (get_attr_mode (insn) == MODE_TI)
return "movdqa\t{%1, %0|%0, %1}";
+ /* Moves from and into integer register is done using movd opcode with
+ REX prefix. */
+ if (GENERAL_REG_P (operands[0]) || GENERAL_REG_P (operands[1]))
+ return "movd\t{%1, %0|%0, %1}";
/* FALLTHRU */
case TYPE_MMXMOV:
return "movq\t{%1, %0|%0, %1}";
}
}
[(set (attr "type")
- (cond [(eq_attr "alternative" "5,6")
+ (cond [(eq_attr "alternative" "5,6,7")
(const_string "mmxmov")
- (eq_attr "alternative" "7,8")
+ (eq_attr "alternative" "8,9,10")
(const_string "ssemov")
(eq_attr "alternative" "4")
(const_string "multi")
(const_string "lea")
]
(const_string "imov")))
- (set_attr "modrm" "*,0,0,*,*,*,*,*,*,*")
- (set_attr "length_immediate" "*,4,8,*,*,*,*,*,*,*")
- (set_attr "mode" "SI,DI,DI,DI,SI,DI,DI,DI,TI,DI")])
+ (set_attr "modrm" "*,0,0,*,*,*,*,*,*,*,*")
+ (set_attr "length_immediate" "*,4,8,*,*,*,*,*,*,*,*")
+ (set_attr "mode" "SI,DI,DI,DI,SI,DI,DI,DI,TI,DI,DI")])
+
+(define_insn "*movdi_1_rex64_nointerunit"
+ [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,r,mr,!mr,!*y,!m,!*y,!*Y,!m,!*Y")
+ (match_operand:DI 1 "general_operand" "Z,rem,i,re,n,*y,*y,m,*Y,*Y,m"))]
+ "TARGET_64BIT
+ && (!TARGET_INTER_UNIT_MOVES && !optimize_size)
+ && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)"
+{
+ switch (get_attr_type (insn))
+ {
+ case TYPE_SSEMOV:
+ if (get_attr_mode (insn) == MODE_TI)
+ return "movdqa\t{%1, %0|%0, %1}";
+ /* FALLTHRU */
+ case TYPE_MMXMOV:
+ return "movq\t{%1, %0|%0, %1}";
+ case TYPE_MULTI:
+ return "#";
+ case TYPE_LEA:
+ return "lea{q}\t{%a1, %0|%0, %a1}";
+ default:
+ if (flag_pic && !LEGITIMATE_PIC_OPERAND_P (operands[1]))
+ abort ();
+ if (get_attr_mode (insn) == MODE_SI)
+ return "mov{l}\t{%k1, %k0|%k0, %k1}";
+ else if (which_alternative == 2)
+ return "movabs{q}\t{%1, %0|%0, %1}";
+ else
+ return "mov{q}\t{%1, %0|%0, %1}";
+ }
+}
+ [(set (attr "type")
+ (cond [(eq_attr "alternative" "5,6,7")
+ (const_string "mmxmov")
+ (eq_attr "alternative" "8,9,10")
+ (const_string "ssemov")
+ (eq_attr "alternative" "4")
+ (const_string "multi")
+ (and (ne (symbol_ref "flag_pic") (const_int 0))
+ (match_operand:DI 1 "symbolic_operand" ""))
+ (const_string "lea")
+ ]
+ (const_string "imov")))
+ (set_attr "modrm" "*,0,0,*,*,*,*,*,*,*,*")
+ (set_attr "length_immediate" "*,4,8,*,*,*,*,*,*,*,*")
+ (set_attr "mode" "SI,DI,DI,DI,SI,DI,DI,DI,TI,DI,DI")])
;; Stores and loads of ax to arbitrary constant address.
;; We fake an second form of instruction to force reload to load address
(define_insn "*movsf_1"
[(set (match_operand:SF 0 "nonimmediate_operand" "=f#xr,m,f#xr,r#xf,m,x#rf,x#rf,x#rf,m,!*y,!rm,!*y")
(match_operand:SF 1 "general_operand" "fm#rx,f#rx,G,rmF#fx,Fr#fx,C,x,xm#rf,x#rf,rm,*y,*y"))]
- "(GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)
+ "(TARGET_INTER_UNIT_MOVES || optimize_size)
+ && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)
+ && (reload_in_progress || reload_completed
+ || (ix86_cmodel == CM_MEDIUM || ix86_cmodel == CM_LARGE)
+ || GET_CODE (operands[1]) != CONST_DOUBLE
+ || memory_operand (operands[0], SFmode))"
+{
+ switch (which_alternative)
+ {
+ case 0:
+ if (REG_P (operands[1])
+ && find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return "fstp\t%y0";
+ else if (STACK_TOP_P (operands[0]))
+ return "fld%z1\t%y1";
+ else
+ return "fst\t%y0";
+
+ case 1:
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return "fstp%z0\t%y0";
+ else
+ return "fst%z0\t%y0";
+
+ case 2:
+ switch (standard_80387_constant_p (operands[1]))
+ {
+ case 1:
+ return "fldz";
+ case 2:
+ return "fld1";
+ }
+ abort();
+
+ case 3:
+ case 4:
+ return "mov{l}\t{%1, %0|%0, %1}";
+ case 5:
+ if (get_attr_mode (insn) == MODE_TI)
+ return "pxor\t%0, %0";
+ else
+ return "xorps\t%0, %0";
+ case 6:
+ if (get_attr_mode (insn) == MODE_V4SF)
+ return "movaps\t{%1, %0|%0, %1}";
+ else
+ return "movss\t{%1, %0|%0, %1}";
+ case 7:
+ case 8:
+ return "movss\t{%1, %0|%0, %1}";
+
+ case 9:
+ case 10:
+ return "movd\t{%1, %0|%0, %1}";
+
+ case 11:
+ return "movq\t{%1, %0|%0, %1}";
+
+ default:
+ abort();
+ }
+}
+ [(set_attr "type" "fmov,fmov,fmov,imov,imov,ssemov,ssemov,ssemov,ssemov,mmxmov,mmxmov,mmxmov")
+ (set (attr "mode")
+ (cond [(eq_attr "alternative" "3,4,9,10")
+ (const_string "SI")
+ (eq_attr "alternative" "5")
+ (if_then_else
+ (and (and (ne (symbol_ref "TARGET_SSE_LOAD0_BY_PXOR")
+ (const_int 0))
+ (ne (symbol_ref "TARGET_SSE2")
+ (const_int 0)))
+ (eq (symbol_ref "optimize_size")
+ (const_int 0)))
+ (const_string "TI")
+ (const_string "V4SF"))
+ /* For architectures resolving dependencies on
+ whole SSE registers use APS move to break dependency
+ chains, otherwise use short move to avoid extra work.
+
+ Do the same for architectures resolving dependencies on
+ the parts. While in DF mode it is better to always handle
+ just register parts, the SF mode is different due to lack
+ of instructions to load just part of the register. It is
+ better to maintain the whole registers in single format
+ to avoid problems on using packed logical operations. */
+ (eq_attr "alternative" "6")
+ (if_then_else
+ (ior (ne (symbol_ref "TARGET_SSE_PARTIAL_REG_DEPENDENCY")
+ (const_int 0))
+ (ne (symbol_ref "TARGET_SSE_PARTIAL_REGS")
+ (const_int 0)))
+ (const_string "V4SF")
+ (const_string "SF"))
+ (eq_attr "alternative" "11")
+ (const_string "DI")]
+ (const_string "SF")))])
+
+(define_insn "*movsf_1_nointerunit"
+ [(set (match_operand:SF 0 "nonimmediate_operand" "=f#xr,m,f#xr,r#xf,m,x#rf,x#rf,x#rf,m,!*y,!m,!*y")
+ (match_operand:SF 1 "general_operand" "fm#rx,f#rx,G,rmF#fx,Fr#fx,C,x,xm#rf,x#rf,m,*y,*y"))]
+ "(!TARGET_INTER_UNIT_MOVES && !optimize_size)
+ && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)
&& (reload_in_progress || reload_completed
|| (ix86_cmodel == CM_MEDIUM || ix86_cmodel == CM_LARGE)
|| GET_CODE (operands[1]) != CONST_DOUBLE
static int rank PARAMS ((rtx, rtx));
static int contributes_to_priority PARAMS ((rtx, rtx));
static void compute_jump_reg_dependencies PARAMS ((rtx, regset));
-static void schedule_ebb PARAMS ((rtx, rtx));
+static basic_block schedule_ebb PARAMS ((rtx, rtx));
+static basic_block fix_basic_block_boundaries PARAMS ((basic_block, basic_block, rtx, rtx));
+static void add_missing_bbs PARAMS ((rtx, basic_block, basic_block));
/* Return nonzero if there are more insns that should be scheduled. */
static int
rank (insn1, insn2)
- rtx insn1 ATTRIBUTE_UNUSED, insn2 ATTRIBUTE_UNUSED;
+ rtx insn1, insn2;
{
+ basic_block bb1 = BLOCK_FOR_INSN (insn1);
+ basic_block bb2 = BLOCK_FOR_INSN (insn2);
+
+ if (bb1->count > bb2->count
+ || bb1->frequency > bb2->frequency)
+ return -1;
+ if (bb1->count < bb2->count
+ || bb1->frequency < bb2->frequency)
+ return 1;
return 0;
}
0, 1
};
\f
+/* It is possible that ebb scheduling elliminated some blocks.
+ Place blocks from FIRST to LAST before BEFORE. */
+
+static void
+add_missing_bbs (before, first, last)
+ rtx before;
+ basic_block first, last;
+{
+ for (; last != first->prev_bb; last = last->prev_bb)
+ {
+ before = emit_note_before (NOTE_INSN_BASIC_BLOCK, before);
+ NOTE_BASIC_BLOCK (before) = last;
+ last->head = before;
+ last->end = before;
+ update_bb_for_insn (last);
+ }
+}
+
+/* Fixup the CFG after EBB scheduling. Re-recognize the basic
+ block boundaries in between HEAD and TAIL and update basic block
+ structures between BB and LAST. */
+
+static basic_block
+fix_basic_block_boundaries (bb, last, head, tail)
+ basic_block bb, last;
+ rtx head, tail;
+{
+ rtx insn = head;
+ rtx last_inside = bb->head;
+ rtx aftertail = NEXT_INSN (tail);
+
+ head = bb->head;
+
+ for (; insn != aftertail; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == CODE_LABEL)
+ abort ();
+ /* Create new basic blocks just before first insn. */
+ if (inside_basic_block_p (insn))
+ {
+ if (!last_inside)
+ {
+ rtx note;
+
+ /* Re-emit the basic block note for newly found BB header. */
+ if (GET_CODE (insn) == CODE_LABEL)
+ {
+ note = emit_note_after (NOTE_INSN_BASIC_BLOCK, insn);
+ head = insn;
+ last_inside = note;
+ }
+ else
+ {
+ note = emit_note_before (NOTE_INSN_BASIC_BLOCK, insn);
+ head = note;
+ last_inside = insn;
+ }
+ }
+ else
+ last_inside = insn;
+ }
+ /* Control flow instruction terminate basic block. It is possible
+ that we've elliminated some basic blocks (made them empty).
+ Find the proper basic block using BLOCK_FOR_INSN and arrange things in
+ a sensible way by inserting empty basic blocks as needed. */
+ if (control_flow_insn_p (insn) || (insn == tail && last_inside))
+ {
+ basic_block curr_bb = BLOCK_FOR_INSN (insn);
+ rtx note;
+
+ if (!control_flow_insn_p (insn))
+ curr_bb = last;
+ if (bb == last->next_bb)
+ {
+ edge f;
+ rtx h;
+
+ /* An obscure special case, where we do have partially dead
+ instruction scheduled after last control flow instruction.
+ In this case we can create new basic block. It is
+ always exactly one basic block last in the sequence. Handle
+ it by splitting the edge and repositioning the block.
+ This is somewhat hackish, but at least avoid cut&paste
+
+ Safter sollution can be to bring the code into sequence,
+ do the split and re-emit it back in case this will ever
+ trigger problem. */
+ f = bb->prev_bb->succ;
+ while (f && !(f->flags & EDGE_FALLTHRU))
+ f = f->succ_next;
+
+ if (f)
+ {
+ last = curr_bb = split_edge (f);
+ h = curr_bb->head;
+ curr_bb->head = head;
+ curr_bb->end = insn;
+ /* Edge splitting created missplaced BASIC_BLOCK note, kill
+ it. */
+ delete_insn (h);
+ }
+ /* It may happen that code got moved past unconditional jump in
+ case the code is completely dead. Kill it. */
+ else
+ {
+ rtx next = next_nonnote_insn (insn);
+ delete_insn_chain (head, insn);
+ /* We keep some notes in the way that may split barrier from the
+ jump. */
+ if (GET_CODE (next) == BARRIER)
+ {
+ emit_barrier_after (prev_nonnote_insn (head));
+ delete_insn (next);
+ }
+ insn = NULL;
+ }
+ }
+ else
+ {
+ curr_bb->head = head;
+ curr_bb->end = insn;
+ add_missing_bbs (curr_bb->head, bb, curr_bb->prev_bb);
+ }
+ note = GET_CODE (head) == CODE_LABEL ? NEXT_INSN (head) : head;
+ NOTE_BASIC_BLOCK (note) = curr_bb;
+ update_bb_for_insn (curr_bb);
+ bb = curr_bb->next_bb;
+ last_inside = NULL;
+ if (!insn)
+ break;
+ }
+ }
+ add_missing_bbs (last->next_bb->head, bb, last);
+ return bb->prev_bb;
+}
+
/* Schedule a single extended basic block, defined by the boundaries HEAD
and TAIL. */
-static void
+static basic_block
schedule_ebb (head, tail)
rtx head, tail;
{
int n_insns;
+ basic_block b;
struct deps tmp_deps;
+ basic_block first_bb = BLOCK_FOR_INSN (head);
+ basic_block last_bb = BLOCK_FOR_INSN (tail);
if (no_real_insns_p (head, tail))
- return;
+ return BLOCK_FOR_INSN (tail);
init_deps_global ();
if (write_symbols != NO_DEBUG)
restore_line_notes (head, tail);
+ b = fix_basic_block_boundaries (first_bb, last_bb, head, tail);
finish_deps_global ();
+ return b;
}
/* The one entry point in this file. DUMP_FILE is the dump file for
break;
if (! e)
break;
- if (GET_CODE (tail) == JUMP_INSN)
- {
- rtx x = find_reg_note (tail, REG_BR_PROB, 0);
- if (x)
- {
- int pred_val = INTVAL (XEXP (x, 0));
- if (pred_val > REG_BR_PROB_BASE / 2)
- break;
- }
- }
-
+ if (e->probability < REG_BR_PROB_BASE / 2)
+ break;
bb = bb->next_bb;
}
break;
}
- schedule_ebb (head, tail);
+ bb = schedule_ebb (head, tail);
}
- /* It doesn't make much sense to try and update life information here - we
- probably messed up even the flow graph. */
+ /* Updating life info can be done by local propagation over the modified
+ superblocks. */
/* Reposition the prologue and epilogue notes in case we moved the
prologue/epilogue insns. */
rm_redundant_line_notes ();
sched_finish ();
+
+#ifdef ENABLE_CHECKING
+ verify_flow_info ();
+#endif
}