}
}
-
-/* Exchange the two operands of COND, and swap its mask so that the
- semantics does not change. */
-static void
-s390_swap_cmp (rtx cond)
-{
- enum rtx_code code = swap_condition (GET_CODE (cond));
- rtx tmp = XEXP (cond, 0);
-
- XEXP (cond, 0) = XEXP (cond, 1);
- XEXP (cond, 1) = tmp;
- PUT_CODE (cond, code);
-}
-
-
/* Returns 1 if INSN reads the value of REG for purposes not related
to addressing of memory, and 0 otherwise. */
static int
&& !reg_used_in_mem_p (REGNO (reg), PATTERN (insn));
}
+/* Starting from INSN find_cond_jump looks downwards in the insn
+ stream for a single jump insn which is the last user of the
+ condition code set in INSN. */
+static rtx
+find_cond_jump (rtx insn)
+{
+ for (; insn; insn = NEXT_INSN (insn))
+ {
+ rtx ite, cc;
+
+ if (LABEL_P (insn))
+ break;
+
+ if (!JUMP_P (insn))
+ {
+ if (reg_mentioned_p (gen_rtx_REG (CCmode, CC_REGNUM), insn))
+ break;
+ continue;
+ }
+
+ /* This will be triggered by a return. */
+ if (GET_CODE (PATTERN (insn)) != SET)
+ break;
+
+ gcc_assert (SET_DEST (PATTERN (insn)) == pc_rtx);
+ ite = SET_SRC (PATTERN (insn));
+
+ if (GET_CODE (ite) != IF_THEN_ELSE)
+ break;
+
+ cc = XEXP (XEXP (ite, 0), 0);
+ if (!REG_P (cc) || !CC_REGNO_P (REGNO (cc)))
+ break;
+
+ if (find_reg_note (insn, REG_DEAD, cc))
+ return insn;
+ break;
+ }
+
+ return NULL_RTX;
+}
+
+/* Swap the condition in COND and the operands in OP0 and OP1 so that
+ the semantics does not change. If NULL_RTX is passed as COND the
+ function tries to find the conditional jump starting with INSN. */
+static void
+s390_swap_cmp (rtx cond, rtx *op0, rtx *op1, rtx insn)
+{
+ rtx tmp = *op0;
+
+ if (cond == NULL_RTX)
+ {
+ rtx jump = find_cond_jump (NEXT_INSN (insn));
+ jump = jump ? single_set (jump) : NULL_RTX;
+
+ if (jump == NULL_RTX)
+ return;
+
+ cond = XEXP (XEXP (jump, 1), 0);
+ }
+
+ *op0 = *op1;
+ *op1 = tmp;
+ PUT_CODE (cond, swap_condition (GET_CODE (cond)));
+}
/* On z10, instructions of the compare-and-branch family have the
property to access the register occurring as second operand with
pipeline recycles, thereby causing significant performance decline.
This function locates such situations and exchanges the two
operands of the compare. */
-static void
+static void
s390_z10_optimize_cmp (void)
{
rtx insn, prev_insn, next_insn;
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
+ rtx cond, *op0, *op1;
+
if (!INSN_P (insn) || INSN_CODE (insn) <= 0)
continue;
- if (get_attr_z10prop (insn) == Z10PROP_Z10_COBRA)
+ if (GET_CODE (PATTERN (insn)) == PARALLEL)
{
- rtx op0, op1, pattern, jump_expr, cond;
+ /* Handle compare and branch and branch on count
+ instructions. */
+ rtx pattern = single_set (insn);
- /* Extract the comparison´s condition and its operands. */
- pattern = single_set (insn);
- gcc_assert (GET_CODE (pattern) == SET);
- jump_expr = XEXP (pattern, 1);
- gcc_assert (GET_CODE (jump_expr) == IF_THEN_ELSE);
- cond = XEXP (jump_expr, 0);
- op0 = XEXP (cond, 0);
- op1 = XEXP (cond, 1);
+ if (!pattern
+ || SET_DEST (pattern) != pc_rtx
+ || GET_CODE (SET_SRC (pattern)) != IF_THEN_ELSE)
+ continue;
- /* Swap the COMPARE´s arguments and its mask if there is a
- conflicting access in the previous insn. */
- prev_insn = PREV_INSN (insn);
- if (prev_insn != NULL_RTX && INSN_P (prev_insn)
- && reg_referenced_p (op1, PATTERN (prev_insn)))
- {
- s390_swap_cmp (cond);
- op0 = XEXP (cond, 0);
- op1 = XEXP (cond, 1);
- }
+ cond = XEXP (SET_SRC (pattern), 0);
+ op0 = &XEXP (cond, 0);
+ op1 = &XEXP (cond, 1);
+ }
+ else if (GET_CODE (PATTERN (insn)) == SET)
+ {
+ rtx src, dest;
- /* Check if there is a conflict with the next insn. If there
- was no conflict with the previous insn, then swap the
- COMPARE´s arguments and its mask. If we already swapped
- the operands, or if swapping them would cause a conflict
- with the previous insn, issue a NOP after the COMPARE in
- order to separate the two instuctions. */
- next_insn = NEXT_INSN (insn);
- if (next_insn != NULL_RTX && INSN_P (next_insn)
- && s390_non_addr_reg_read_p (op1, next_insn))
- {
- if (s390_non_addr_reg_read_p (op0, prev_insn))
- {
- if (REGNO(op1) == 0)
- emit_insn_after (gen_nop1 (), insn);
- else
- emit_insn_after (gen_nop (), insn);
- added_NOPs = 1;
- }
+ /* Handle normal compare instructions. */
+ src = SET_SRC (PATTERN (insn));
+ dest = SET_DEST (PATTERN (insn));
+
+ if (!REG_P (dest)
+ || !CC_REGNO_P (REGNO (dest))
+ || GET_CODE (src) != COMPARE)
+ continue;
+
+ /* s390_swap_cmp will try to find the conditional
+ jump when passing NULL_RTX as condition. */
+ cond = NULL_RTX;
+ op0 = &XEXP (src, 0);
+ op1 = &XEXP (src, 1);
+ }
+ else
+ continue;
+
+ if (!REG_P (*op0) || !REG_P (*op1))
+ continue;
+
+ /* Swap the COMPARE arguments and its mask if there is a
+ conflicting access in the previous insn. */
+ prev_insn = PREV_INSN (insn);
+ if (prev_insn != NULL_RTX && INSN_P (prev_insn)
+ && reg_referenced_p (*op1, PATTERN (prev_insn)))
+ s390_swap_cmp (cond, op0, op1, insn);
+
+ /* Check if there is a conflict with the next insn. If there
+ was no conflict with the previous insn, then swap the
+ COMPARE arguments and its mask. If we already swapped
+ the operands, or if swapping them would cause a conflict
+ with the previous insn, issue a NOP after the COMPARE in
+ order to separate the two instuctions. */
+ next_insn = NEXT_INSN (insn);
+ if (next_insn != NULL_RTX && INSN_P (next_insn)
+ && s390_non_addr_reg_read_p (*op1, next_insn))
+ {
+ if (prev_insn != NULL_RTX && INSN_P (prev_insn)
+ && s390_non_addr_reg_read_p (*op0, prev_insn))
+ {
+ if (REGNO (*op1) == 0)
+ emit_insn_after (gen_nop1 (), insn);
else
- s390_swap_cmp (cond);
+ emit_insn_after (gen_nop (), insn);
+ added_NOPs = 1;
}
+ else
+ s390_swap_cmp (cond, op0, op1, insn);
}
}
/* Eliminate z10-specific pipeline recycles related to some compare
instructions. */
- if (TARGET_Z10)
+ if (s390_tune == PROCESSOR_2097_Z10)
s390_z10_optimize_cmp ();
}
;; can immediately read the new value.
;; z10_fr: union of Z10_fwd and z10_rec.
;; z10_c: second operand of instruction is a register and read with complemented bits.
-;; z10_cobra: its a compare and branch instruction
;;
;; An additional suffix A1, A3, or E1 indicates the respective AGI bypass.
z10_fwd, z10_fwd_A1, z10_fwd_A3, z10_fwd_E1,
z10_rec,
z10_fr, z10_fr_A3, z10_fr_E1,
- z10_c, z10_cobra"
+ z10_c"
(const_string "none"))
cy\t%0,%1
#"
[(set_attr "op_type" "RR,RI,RIL,RX,RXY,SS")
- (set_attr "z10prop" "z10_super,z10_super,z10_super,z10_super,z10_super,*")])
+ (set_attr "z10prop" "z10_super_c,z10_super,z10_super,z10_super,z10_super,*")])
; Compare (signed) instructions
}
[(set_attr "op_type" "RIE")
(set_attr "type" "branch")
- (set_attr "z10prop" "z10_cobra,z10_super")
+ (set_attr "z10prop" "z10_super_c,z10_super")
(set (attr "length")
(if_then_else (lt (abs (minus (pc) (match_dup 3))) (const_int 60000))
(const_int 6) (const_int 12)))]) ; 8 byte for cr/jg
}
[(set_attr "op_type" "RIE")
(set_attr "type" "branch")
- (set_attr "z10prop" "z10_cobra,z10_super")
+ (set_attr "z10prop" "z10_super_c,z10_super")
(set (attr "length")
(if_then_else (lt (abs (minus (pc) (match_dup 3))) (const_int 60000))
(const_int 6) (const_int 12)))]) ; 8 byte for clr/jg
(const_string "RR") (const_string "RX")))
(set_attr "type" "branch")
(set_attr "atype" "agen")
- (set_attr "z10prop" "z10_cobra")])
+ (set_attr "z10prop" "z10_c")])
(define_insn_and_split "doloop_di"
[(set (pc)