#include "target-def.h"
#include "df.h"
#include "opts.h"
+#include "cfgloop.h"
/* This is used in the am33_2.0-linux-gnu port, in which global symbol
names are not prefixed by underscores, to tell whether to prefix a
}
}
+#define DUMP(reason, insn) \
+ do \
+ { \
+ if (dump_file) \
+ { \
+ fprintf (dump_file, reason "\n"); \
+ if (insn != NULL_RTX) \
+ print_rtl_single (dump_file, insn); \
+ fprintf(dump_file, "\n"); \
+ } \
+ } \
+ while (0)
+
+/* Replace the BRANCH insn with a Lcc insn that goes to LABEL.
+ Insert a SETLB insn just before LABEL. */
+
+static void
+mn10300_insert_setlb_lcc (rtx label, rtx branch)
+{
+ rtx lcc, comparison, cmp_reg;
+
+ if (LABEL_NUSES (label) > 1)
+ {
+ rtx insn;
+
+ /* This label is used both as an entry point to the loop
+ and as a loop-back point for the loop. We need to separate
+ these two functions so that the SETLB happens upon entry,
+ but the loop-back does not go to the SETLB instruction. */
+ DUMP ("Inserting SETLB insn after:", label);
+ insn = emit_insn_after (gen_setlb (), label);
+ label = gen_label_rtx ();
+ emit_label_after (label, insn);
+ DUMP ("Created new loop-back label:", label);
+ }
+ else
+ {
+ DUMP ("Inserting SETLB insn before:", label);
+ emit_insn_before (gen_setlb (), label);
+ }
+
+ comparison = XEXP (SET_SRC (PATTERN (branch)), 0);
+ cmp_reg = XEXP (comparison, 0);
+ gcc_assert (REG_P (cmp_reg));
+
+ /* If the comparison has not already been split out of the branch
+ then do so now. */
+ gcc_assert (REGNO (cmp_reg) == CC_REG);
+
+ if (GET_MODE (cmp_reg) == CC_FLOATmode)
+ lcc = gen_FLcc (comparison, label);
+ else
+ lcc = gen_Lcc (comparison, label);
+
+ lcc = emit_jump_insn_before (lcc, branch);
+ mark_jump_label (XVECEXP (PATTERN (lcc), 0, 0), lcc, 0);
+ DUMP ("Replacing branch insn...", branch);
+ DUMP ("... with Lcc insn:", lcc);
+ delete_insn (branch);
+}
+
+static bool
+mn10300_block_contains_call (struct basic_block_def * block)
+{
+ rtx insn;
+
+ FOR_BB_INSNS (block, insn)
+ if (CALL_P (insn))
+ return true;
+
+ return false;
+}
+
+static bool
+mn10300_loop_contains_call_insn (loop_p loop)
+{
+ basic_block * bbs;
+ bool result = false;
+ unsigned int i;
+
+ bbs = get_loop_body (loop);
+
+ for (i = 0; i < loop->num_nodes; i++)
+ if (mn10300_block_contains_call (bbs[i]))
+ {
+ result = true;
+ break;
+ }
+
+ free (bbs);
+ return result;
+}
+
+static void
+mn10300_scan_for_setlb_lcc (void)
+{
+ struct loops loops;
+ loop_iterator liter;
+ loop_p loop;
+
+ DUMP ("Looking for loops that can use the SETLB insn", NULL_RTX);
+
+ df_analyze ();
+ compute_bb_for_insn ();
+
+ /* Find the loops. */
+ if (flow_loops_find (& loops) < 1)
+ DUMP ("No loops found", NULL_RTX);
+ current_loops = & loops;
+
+ /* FIXME: For now we only investigate innermost loops. In practice however
+ if an inner loop is not suitable for use with the SETLB/Lcc insns, it may
+ be the case that its parent loop is suitable. Thus we should check all
+ loops, but work from the innermost outwards. */
+ FOR_EACH_LOOP (liter, loop, LI_ONLY_INNERMOST)
+ {
+ const char * reason = NULL;
+
+ /* Check to see if we can modify this loop. If we cannot
+ then set 'reason' to describe why it could not be done. */
+ if (loop->latch == NULL)
+ reason = "it contains multiple latches";
+ else if (loop->header != loop->latch)
+ /* FIXME: We could handle loops that span multiple blocks,
+ but this requires a lot more work tracking down the branches
+ that need altering, so for now keep things simple. */
+ reason = "the loop spans multiple blocks";
+ else if (mn10300_loop_contains_call_insn (loop))
+ reason = "it contains CALL insns";
+ else
+ {
+ rtx branch = BB_END (loop->latch);
+
+ gcc_assert (JUMP_P (branch));
+ if (single_set (branch) == NULL_RTX || ! any_condjump_p (branch))
+ /* We cannot optimize tablejumps and the like. */
+ /* FIXME: We could handle unconditional jumps. */
+ reason = "it is not a simple loop";
+ else
+ {
+ rtx label;
+
+ if (dump_file)
+ flow_loop_dump (loop, dump_file, NULL, 0);
+
+ label = BB_HEAD (loop->header);
+ gcc_assert (LABEL_P (label));
+
+ mn10300_insert_setlb_lcc (label, branch);
+ }
+ }
+
+ if (dump_file && reason != NULL)
+ fprintf (dump_file, "Loop starting with insn %d is not suitable because %s\n",
+ INSN_UID (BB_HEAD (loop->header)),
+ reason);
+ }
+
+#if 0 /* FIXME: We should free the storage we allocated, but
+ for some unknown reason this leads to seg-faults. */
+ FOR_EACH_LOOP (liter, loop, 0)
+ free_simple_loop_desc (loop);
+
+ flow_loops_free (current_loops);
+#endif
+
+ current_loops = NULL;
+
+ df_finish_pass (false);
+
+ DUMP ("SETLB scan complete", NULL_RTX);
+}
+
static void
mn10300_reorg (void)
{
- if (TARGET_AM33)
+ /* These are optimizations, so only run them if optimizing. */
+ if (TARGET_AM33 && (optimize > 0 || optimize_size))
{
+ if (TARGET_ALLOW_SETLB)
+ mn10300_scan_for_setlb_lcc ();
+
if (TARGET_ALLOW_LIW)
mn10300_bundle_liw ();
}
#define TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA mn10300_asm_output_addr_const_extra
#undef TARGET_DEFAULT_TARGET_FLAGS
-#define TARGET_DEFAULT_TARGET_FLAGS MASK_MULT_BUG | MASK_PTR_A0D0 | MASK_ALLOW_LIW
+#define TARGET_DEFAULT_TARGET_FLAGS MASK_MULT_BUG | MASK_PTR_A0D0 | MASK_ALLOW_LIW | MASK_ALLOW_SETLB
#undef TARGET_HANDLE_OPTION
#define TARGET_HANDLE_OPTION mn10300_handle_option
#undef TARGET_OPTION_OVERRIDE
;; This is used to encode LIW patterns.
(UNSPEC_LIW 8)
+ ;; This is for the low overhead loop instructions.
+ (UNSPEC_SETLB 9)
])
(include "predicates.md")
})
(define_insn "*movsf_internal"
- [(set (match_operand:SF 0 "nonimmediate_operand" "=rf,r,f,r,f,r,f,r,m,f,Q")
- (match_operand:SF 1 "general_operand" " 0,F,F,r,f,f,r,m,r,Q,f"))]
+ [(set (match_operand:SF 0 "nonimmediate_operand" "=rf,r,f,r,f,r,f,r,m,f,Q,z,d")
+ (match_operand:SF 1 "general_operand" " 0,F,F,r,f,f,r,m,r,Q,f,d,z"))]
"TARGET_AM33_2
&& (register_operand (operands[0], SFmode)
|| register_operand (operands[1], SFmode))"
case 3:
case 7:
case 8:
+ case 11:
+ case 12:
return "mov %1,%0";
case 2:
case 4:
(const_int 13) (const_int 24))
(if_then_else (eq_attr "cpu" "am34")
(const_int 13) (const_int 24))
+ (const_int 22)
+ (const_int 22)
])]
)
DONE;
})
-(define_insn "*cmpsi"
+(define_insn "cmpsi"
[(set (reg CC_REG)
(compare (match_operand:SI 0 "register_operand" "r,r,r")
(match_operand:SI 1 "nonmemory_operand" "r,O,i")))]
[(set (attr "timings") (if_then_else (eq_attr "cpu" "am34")
(const_int 13) (const_int 12)))]
)
+
+;; Note - in theory the doloop patterns could be used here to express
+;; the SETLB and Lcc instructions. In practice this does not work because
+;; the acceptable forms of the doloop patterns do not include UNSPECs
+;; and without them gcc's basic block reordering code can duplicate the
+;; doloop_end pattern, leading to bogus multiple decrements of the loop
+;; counter.
+
+(define_insn "setlb"
+ [(unspec [(const_int 0)] UNSPEC_SETLB)]
+ "TARGET_AM33 && TARGET_ALLOW_SETLB"
+ "setlb"
+)
+
+(define_insn "Lcc"
+ [(set (pc)
+ (if_then_else (match_operator 0 "comparison_operator"
+ [(reg:CC CC_REG) (const_int 0)])
+ (label_ref (match_operand 1 "" ""))
+ (pc)))
+ (unspec [(const_int 1)] UNSPEC_SETLB)]
+ "TARGET_AM33 && TARGET_ALLOW_SETLB"
+ "L%b0 # loop back to: %1"
+)
+
+(define_insn "FLcc"
+ [(set (pc)
+ (if_then_else (match_operator 0 "comparison_operator"
+ [(reg:CC_FLOAT CC_REG) (const_int 0)])
+ (label_ref (match_operand 1 "" ""))
+ (pc)))
+ (unspec [(const_int 2)] UNSPEC_SETLB)]
+ "TARGET_AM33_2 && TARGET_ALLOW_SETLB"
+ "FL%b0 # loop back to: %1"
+ [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34") (const_int 44) (const_int 11)))]
+)