From ac3ebe93b6ba2eb7c6b8f4f9e6bc2eb253fe586c Mon Sep 17 00:00:00 2001 From: Nathan Sidwell Date: Thu, 8 Dec 2005 13:46:18 +0000 Subject: [PATCH] ms1.md (UNSPEC_LOOP): New constant. * config/ms1/ms1.md (UNSPEC_LOOP): New constant. (loop_end, loop_init, doloop_end): New insns. * config/ms1/ms1.h (LOOP_FIRST, LOOP_LAST): New. (SPECIAL_REG_FIRST, FIRST_PSEUDO_REGISTER): Adjust. (FIXED_REGISTERS, CALL_USED_REGISTERS): Adjust. (REG_CLASS_CONTENTS, REGISTER_NAMES): Adjust. * config/ms1/ms1.c: #include basic-block.h (struct machine_function): Add has_loops field. (ms1_add_loop): New. (MAX_LOOP_DEPTH, MAX_LOO_LENGTH): New. (struct loop_info, struct loop_work): New. (ms1_loop_nesting, ms1_block_length, ms1_scan_loop): New workers. (ms1_reorg_loops): New loop optimization. (ms1_machine_reorg): Call it. * config/ms1/ms1-protos.h (ms1_add_loop): Declare. From-SVN: r108229 --- gcc/ChangeLog | 18 ++ gcc/config/ms1/ms1-protos.h | 1 + gcc/config/ms1/ms1.c | 511 +++++++++++++++++++++++++++++++++++- gcc/config/ms1/ms1.h | 27 +- gcc/config/ms1/ms1.md | 53 ++++ 5 files changed, 597 insertions(+), 13 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 83df5d937b0f..90e9b84a6155 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,21 @@ +2005-12-08 Nathan Sidwell + + * config/ms1/ms1.md (UNSPEC_LOOP): New constant. + (loop_end, loop_init, doloop_end): New insns. + * config/ms1/ms1.h (LOOP_FIRST, LOOP_LAST): New. + (SPECIAL_REG_FIRST, FIRST_PSEUDO_REGISTER): Adjust. + (FIXED_REGISTERS, CALL_USED_REGISTERS): Adjust. + (REG_CLASS_CONTENTS, REGISTER_NAMES): Adjust. + * config/ms1/ms1.c: #include basic-block.h + (struct machine_function): Add has_loops field. + (ms1_add_loop): New. + (MAX_LOOP_DEPTH, MAX_LOO_LENGTH): New. + (struct loop_info, struct loop_work): New. + (ms1_loop_nesting, ms1_block_length, ms1_scan_loop): New workers. + (ms1_reorg_loops): New loop optimization. + (ms1_machine_reorg): Call it. + * config/ms1/ms1-protos.h (ms1_add_loop): Declare. + 2005-12-08 Zdenek Dvorak PR tree-optimization/25248 diff --git a/gcc/config/ms1/ms1-protos.h b/gcc/config/ms1/ms1-protos.h index 412b42d2ac24..c37048263ca9 100644 --- a/gcc/config/ms1/ms1-protos.h +++ b/gcc/config/ms1/ms1-protos.h @@ -26,6 +26,7 @@ extern void ms1_override_options (void); extern int ms1_initial_elimination_offset (int, int); extern const char * ms1_asm_output_opcode (FILE *, const char *); extern int ms1_epilogue_uses (int); +extern void ms1_add_loop (void); #ifdef TREE_CODE extern const char * ms1_cannot_inline_p (tree); diff --git a/gcc/config/ms1/ms1.c b/gcc/config/ms1/ms1.c index 9bc5138d4c3b..3695f0fb8b48 100644 --- a/gcc/config/ms1/ms1.c +++ b/gcc/config/ms1/ms1.c @@ -47,6 +47,7 @@ #include "except.h" #include "target.h" #include "target-def.h" +#include "basic-block.h" /* Frame pointer register mask. */ #define FP_MASK (1 << (GPR_FP)) @@ -68,6 +69,7 @@ struct machine_function GTY(()) int ra_needs_full_frame; struct rtx_def * eh_stack_adjust; int interrupt_handler; + int has_loops; }; /* Define the information needed to generate branch and scc insns. @@ -811,7 +813,7 @@ ms1_override_options (void) error ("bad value (%s) for -march= switch", ms1_cpu_string); } else - ms1_cpu = PROCESSOR_MS1_64_001; + ms1_cpu = PROCESSOR_MS2; if (flag_exceptions) { @@ -1648,6 +1650,510 @@ ms1_pass_in_stack (enum machine_mode mode ATTRIBUTE_UNUSED, tree type) || TREE_ADDRESSABLE (type)))); } +/* Increment the counter for the number of loop instructions in the + current function. */ + +void ms1_add_loop (void) +{ + cfun->machine->has_loops++; +} + + +/* Maxium loop nesting depth. */ +#define MAX_LOOP_DEPTH 4 +/* Maxium size of a loop (allows some headroom for delayed branch slot + filling. */ +#define MAX_LOOP_LENGTH (200 * 4) + +/* We need to keep a vector of basic blocks */ +DEF_VEC_P (basic_block); +DEF_VEC_ALLOC_P (basic_block,heap); + +/* And a vector of loops */ +typedef struct loop_info *loop_info; +DEF_VEC_P (loop_info); +DEF_VEC_ALLOC_P (loop_info,heap); + +/* Information about a loop we have found (or are in the process of + finding). */ +struct loop_info GTY (()) +{ + /* loop number, for dumps */ + int loop_no; + + /* Predecessor block of the loop. This is the one that falls into + the loop and contains the initialization instruction. */ + basic_block predecessor; + + /* First block in the loop. This is the one branched to by the dbnz + insn. */ + basic_block head; + + /* Last block in the loop (the one with the dbnz insn */ + basic_block tail; + + /* The successor block of the loop. This is the one the dbnz insn + falls into. */ + basic_block successor; + + /* The dbnz insn. */ + rtx dbnz; + + /* The initialization insn. */ + rtx init; + + /* The new initialization instruction. */ + rtx loop_init; + + /* The new ending instruction. */ + rtx loop_end; + + /* The new label placed at the end of the loop. */ + rtx end_label; + + /* The nesting depth of the loop. Set to -1 for a bad loop. */ + int depth; + + /* The length of the loop. */ + int length; + + /* Next loop in the graph. */ + struct loop_info *next; + + /* Vector of blocks only within the loop, (excluding those within + inner loops). */ + VEC (basic_block,heap) *blocks; + + /* Vector of inner loops within this loop */ + VEC (loop_info,heap) *loops; +}; + +/* Information used during loop detection. */ +typedef struct loop_work GTY(()) +{ + /* Basic block to be scanned. */ + basic_block block; + + /* Loop it will be within. */ + loop_info loop; +} loop_work; + +/* Work list. */ +DEF_VEC_O (loop_work); +DEF_VEC_ALLOC_O (loop_work,heap); + +/* Determine the nesting and length of LOOP. Return false if the loop + is bad. */ + +static bool +ms1_loop_nesting (loop_info loop) +{ + loop_info inner; + unsigned ix; + int inner_depth = 0; + + if (!loop->depth) + { + /* Make sure we only have one entry point. */ + if (EDGE_COUNT (loop->head->preds) == 2) + { + loop->predecessor = EDGE_PRED (loop->head, 0)->src; + if (loop->predecessor == loop->tail) + /* We wanted the other predecessor. */ + loop->predecessor = EDGE_PRED (loop->head, 1)->src; + + /* We can only place a loop insn on a fall through edge of a + single exit block. */ + if (EDGE_COUNT (loop->predecessor->succs) != 1 + || !(EDGE_SUCC (loop->predecessor, 0)->flags & EDGE_FALLTHRU)) + loop->predecessor = NULL; + } + + /* Mark this loop as bad for now. */ + loop->depth = -1; + if (loop->predecessor) + { + for (ix = 0; VEC_iterate (loop_info, loop->loops, ix++, inner);) + { + if (!inner->depth) + ms1_loop_nesting (inner); + + if (inner->depth < 0) + { + inner_depth = -1; + break; + } + + if (inner_depth < inner->depth) + inner_depth = inner->depth; + loop->length += inner->length; + } + + /* Set the proper loop depth, if it was good. */ + if (inner_depth >= 0) + loop->depth = inner_depth + 1; + } + } + return (loop->depth > 0 + && loop->predecessor + && loop->depth < MAX_LOOP_DEPTH + && loop->length < MAX_LOOP_LENGTH); +} + +/* Determine the length of block BB. */ + +static int +ms1_block_length (basic_block bb) +{ + int length = 0; + rtx insn; + + for (insn = BB_HEAD (bb); + insn != NEXT_INSN (BB_END (bb)); + insn = NEXT_INSN (insn)) + { + if (!INSN_P (insn)) + continue; + if (CALL_P (insn)) + { + /* Calls are not allowed in loops. */ + length = MAX_LOOP_LENGTH + 1; + break; + } + + length += get_attr_length (insn); + } + return length; +} + +/* Scan the blocks of LOOP (and its inferiors) looking for uses of + REG. Return true, if we find any. Don't count the loop's dbnz + insn if it matches DBNZ. */ + +static bool +ms1_scan_loop (loop_info loop, rtx reg, rtx dbnz) +{ + unsigned ix; + loop_info inner; + basic_block bb; + + for (ix = 0; VEC_iterate (basic_block, loop->blocks, ix, bb); ix++) + { + rtx insn; + + for (insn = BB_HEAD (bb); + insn != NEXT_INSN (BB_END (bb)); + insn = NEXT_INSN (insn)) + { + if (!INSN_P (insn)) + continue; + if (insn == dbnz) + continue; + if (reg_mentioned_p (reg, PATTERN (insn))) + return true; + } + } + for (ix = 0; VEC_iterate (loop_info, loop->loops, ix, inner); ix++) + if (ms1_scan_loop (inner, reg, NULL_RTX)) + return true; + + return false; +} + +/* MS2 has a loop instruction which needs to be placed just before the + loop. It indicates the end of the loop and specifies the number of + loop iterations. It can be nested with an automatically maintained + stack of counter and end address registers. It's an ideal + candidate for doloop. Unfortunately, gcc presumes that loops + always end with an explicit instriction, and the doloop_begin + instruction is not a flow control instruction so it can be + scheduled earlier than just before the start of the loop. To make + matters worse, the optimization pipeline can duplicate loop exit + and entrance blocks and fails to track abnormally exiting loops. + Thus we cannot simply use doloop. + + What we do is emit a dbnz pattern for the doloop optimization, and + let that be optimized as normal. Then in machine dependent reorg + we have to repeat the loop searching algorithm. We use the + flow graph to find closed loops ending in a dbnz insn. We then try + and convert it to use the loop instruction. The conditions are, + + * the loop has no abnormal exits, duplicated end conditions or + duplicated entrance blocks + + * the loop counter register is only used in the dbnz instruction + within the loop + + * we can find the instruction setting the initial value of the loop + counter + + * the loop is not executed more than 65535 times. (This might be + changed to 2^32-1, and would therefore allow variable initializers.) + + * the loop is not nested more than 4 deep 5) there are no + subroutine calls in the loop. */ + +static void +ms1_reorg_loops (FILE *dump_file) +{ + basic_block bb; + loop_info loops = NULL; + loop_info loop; + int nloops = 0; + unsigned dwork = 0; + VEC (loop_work,heap) *works = VEC_alloc (loop_work,heap,20); + loop_work *work; + edge e; + edge_iterator ei; + bool replaced = false; + + /* Find all the possible loop tails. This means searching for every + dbnz instruction. For each one found, create a loop_info + structure and add the head block to the work list. */ + FOR_EACH_BB (bb) + { + rtx tail = BB_END (bb); + + while (GET_CODE (tail) == NOTE) + tail = PREV_INSN (tail); + + bb->aux = NULL; + if (recog_memoized (tail) == CODE_FOR_decrement_and_branch_until_zero) + { + /* A possible loop end */ + + loop = XNEW (struct loop_info); + loop->next = loops; + loops = loop; + loop->tail = bb; + loop->head = BRANCH_EDGE (bb)->dest; + loop->successor = FALLTHRU_EDGE (bb)->dest; + loop->predecessor = NULL; + loop->dbnz = tail; + loop->depth = 0; + loop->length = ms1_block_length (bb); + loop->blocks = VEC_alloc (basic_block, heap, 20); + VEC_quick_push (basic_block, loop->blocks, bb); + loop->loops = NULL; + loop->loop_no = nloops++; + + loop->init = loop->end_label = NULL_RTX; + loop->loop_init = loop->loop_end = NULL_RTX; + + work = VEC_safe_push (loop_work, heap, works, NULL); + work->block = loop->head; + work->loop = loop; + + bb->aux = loop; + + if (dump_file) + { + fprintf (dump_file, ";; potential loop %d ending at\n", + loop->loop_no); + print_rtl_single (dump_file, tail); + } + } + } + + /* Now find all the closed loops. + until work list empty, + if block's auxptr is set + if != loop slot + if block's loop's start != block + mark loop as bad + else + append block's loop's fallthrough block to worklist + increment this loop's depth + else if block is exit block + mark loop as bad + else + set auxptr + for each target of block + add to worklist */ + while (VEC_iterate (loop_work, works, dwork++, work)) + { + loop = work->loop; + bb = work->block; + if (bb == EXIT_BLOCK_PTR) + /* We've reached the exit block. The loop must be bad. */ + loop->depth = -1; + else if (!bb->aux) + { + /* We've not seen this block before. Add it to the loop's + list and then add each successor to the work list. */ + bb->aux = loop; + loop->length += ms1_block_length (bb); + VEC_safe_push (basic_block, heap, loop->blocks, bb); + FOR_EACH_EDGE (e, ei, bb->succs) + { + if (!VEC_space (loop_work, works, 1)) + { + if (dwork) + { + VEC_block_remove (loop_work, works, 0, dwork); + dwork = 0; + } + else + VEC_reserve (loop_work, heap, works, 1); + } + work = VEC_quick_push (loop_work, works, NULL); + work->block = EDGE_SUCC (bb, ei.index)->dest; + work->loop = loop; + } + } + else if (bb->aux != loop) + { + /* We've seen this block in a different loop. If it's not + the other loop's head, then this loop must be bad. + Otherwise, the other loop might be a nested loop, so + continue from that loop's successor. */ + loop_info other = bb->aux; + + if (other->head != bb) + loop->depth = -1; + else + { + VEC_safe_push (loop_info, heap, loop->loops, other); + work = VEC_safe_push (loop_work, heap, works, NULL); + work->loop = loop; + work->block = other->successor; + } + } + } + VEC_free (loop_work, heap, works); + + /* Now optimize the loops. */ + for (loop = loops; loop; loop = loop->next) + { + rtx iter_reg, insn, init_insn; + rtx init_val, loop_end, loop_init, end_label, head_label; + + if (!ms1_loop_nesting (loop)) + { + if (dump_file) + fprintf (dump_file, ";; loop %d is bad\n", loop->loop_no); + continue; + } + + /* Get the loop iteration register. */ + iter_reg = SET_DEST (XVECEXP (PATTERN (loop->dbnz), 0, 1)); + + if (!REG_P (iter_reg)) + { + /* Spilled */ + if (dump_file) + fprintf (dump_file, ";; loop %d has spilled iteration count\n", + loop->loop_no); + continue; + } + + /* Look for the initializing insn */ + init_insn = NULL_RTX; + for (insn = BB_END (loop->predecessor); + insn != PREV_INSN (BB_HEAD (loop->predecessor)); + insn = PREV_INSN (insn)) + { + if (!INSN_P (insn)) + continue; + if (reg_mentioned_p (iter_reg, PATTERN (insn))) + { + rtx set = single_set (insn); + + if (set && rtx_equal_p (iter_reg, SET_DEST (set))) + init_insn = insn; + break; + } + } + + if (!init_insn) + { + if (dump_file) + fprintf (dump_file, ";; loop %d has no initializer\n", + loop->loop_no); + continue; + } + if (dump_file) + { + fprintf (dump_file, ";; loop %d initialized by\n", + loop->loop_no); + print_rtl_single (dump_file, init_insn); + } + + init_val = PATTERN (init_insn); + if (GET_CODE (init_val) == SET) + init_val = SET_SRC (init_val); + if (GET_CODE (init_val) != CONST_INT || INTVAL (init_val) >= 65535) + { + if (dump_file) + fprintf (dump_file, ";; loop %d has complex initializer\n", + loop->loop_no); + continue; + } + + /* Scan all the blocks to make sure they don't use iter_reg. */ + if (ms1_scan_loop (loop, iter_reg, loop->dbnz)) + { + if (dump_file) + fprintf (dump_file, ";; loop %d uses iterator\n", + loop->loop_no); + continue; + } + + /* The loop is good for replacement. */ + + /* loop is 1 based, dbnz is zero based. */ + init_val = GEN_INT (INTVAL (init_val) + 1); + + iter_reg = gen_rtx_REG (SImode, LOOP_FIRST + loop->depth - 1); + end_label = gen_label_rtx (); + head_label = XEXP (SET_SRC (XVECEXP (PATTERN (loop->dbnz), 0, 0)), 1); + loop_end = gen_loop_end (iter_reg, head_label); + loop_init = gen_loop_init (iter_reg, init_val, end_label); + loop->init = init_insn; + loop->end_label = end_label; + loop->loop_init = loop_init; + loop->loop_end = loop_end; + replaced = true; + + if (dump_file) + { + fprintf (dump_file, ";; replacing loop %d initializer with\n", + loop->loop_no); + print_rtl_single (dump_file, loop->loop_init); + fprintf (dump_file, ";; replacing loop %d terminator with\n", + loop->loop_no); + print_rtl_single (dump_file, loop->loop_end); + } + } + + /* Now apply the optimizations. Do it this way so we don't mess up + the flow graph half way through. */ + for (loop = loops; loop; loop = loop->next) + if (loop->loop_init) + { + emit_jump_insn_after (loop->loop_init, BB_END (loop->predecessor)); + delete_insn (loop->init); + emit_label_before (loop->end_label, loop->dbnz); + emit_jump_insn_before (loop->loop_end, loop->dbnz); + delete_insn (loop->dbnz); + } + + /* Free up the loop structures */ + while (loops) + { + loop = loops; + loops = loop->next; + VEC_free (loop_info, heap, loop->loops); + VEC_free (basic_block, heap, loop->blocks); + XDELETE (loop); + } + + if (replaced && dump_file) + { + fprintf (dump_file, ";; Replaced loops\n"); + print_rtl (dump_file, get_insns ()); + } +} /* Structures to hold branch information during reorg. */ typedef struct branch_info @@ -1959,6 +2465,9 @@ ms1_reorg_hazard (void) static void ms1_machine_reorg (void) { + if (cfun->machine->has_loops) + ms1_reorg_loops (dump_file); + if (ms1_flag_delayed_branch) dbr_schedule (get_insns (), dump_file); diff --git a/gcc/config/ms1/ms1.h b/gcc/config/ms1/ms1.h index 68f6eac7dd0f..eff36c7cbaa7 100644 --- a/gcc/config/ms1/ms1.h +++ b/gcc/config/ms1/ms1.h @@ -41,7 +41,7 @@ extern enum processor_type ms1_cpu; /* A C string constant that tells the GCC driver program options to pass to the assembler. */ #undef ASM_SPEC -#define ASM_SPEC "%{march=*} %{!march=*: -march=ms1-16-002}" +#define ASM_SPEC "%{march=*} %{!march=*: -march=ms2}" /* A string to pass to at the end of the command given to the linker. */ #undef LIB_SPEC @@ -55,7 +55,7 @@ march=ms1-16-003:-T 16-003.ld%s; \ march=MS1-16-003:-T 16-003.ld%s; \ march=ms2:-T ms2.ld%s; \ march=MS2:-T ms2.ld%s; \ - : -T 16-002.ld}" + : -T ms2.ld}" /* A string to pass at the very beginning of the command given to the linker. */ @@ -69,7 +69,7 @@ march=ms1-16-003:%{!mno-crt0:crt0-16-003.o%s} startup-16-003.o%s; \ march=MS1-16-003:%{!mno-crt0:crt0-16-003.o%s} startup-16-003.o%s; \ march=ms2:%{!mno-crt0:crt0-ms2.o%s} startup-ms2.o%s; \ march=MS2:%{!mno-crt0:crt0-ms2.o%s} startup-ms2.o%s; \ - :%{!mno-crt0:crt0-16-002.o%s} startup-16-002.o%s} \ + :%{!mno-crt0:crt0-ms2.o%s} startup-ms2.o%s} \ crti.o%s crtbegin.o%s" /* A string to pass at the end of the command given to the linker. */ @@ -83,7 +83,7 @@ march=ms1-16-003:exit-16-003.o%s; \ march=MS1-16-003:exit-16-003.o%s; \ march=ms2:exit-ms2.o%s; \ march=MS2:exit-ms2.o%s; \ - :exit-16-002.o%s} \ + :exit-ms2.o%s} \ crtend.o%s crtn.o%s" /* Run-time target specifications. */ @@ -243,10 +243,13 @@ march=MS2:exit-ms2.o%s; \ seen by the caller */ #define GPR_INTERRUPT_LINK 15 /* hold return addres for interrupts */ +#define LOOP_FIRST (GPR_LAST + 1) +#define LOOP_LAST (LOOP_FIRST + 3) + /* Argument register that is eliminated in favor of the frame and/or stack pointer. Also add register to point to where the return address is stored. */ -#define SPECIAL_REG_FIRST (GPR_LAST + 1) +#define SPECIAL_REG_FIRST (LOOP_LAST + 1) #define SPECIAL_REG_LAST (SPECIAL_REG_FIRST) #define ARG_POINTER_REGNUM (SPECIAL_REG_FIRST + 0) #define SPECIAL_REG_P(R) ((R) == SPECIAL_REG_FIRST) @@ -258,7 +261,7 @@ march=MS2:exit-ms2.o%s; \ /* The register used to hold functions return value */ #define RETVAL_REGNUM 11 -#define FIRST_PSEUDO_REGISTER (GPR_FIRST + 17) +#define FIRST_PSEUDO_REGISTER (SPECIAL_REG_LAST + 1) #define IS_PSEUDO_P(R) (REGNO (R) >= FIRST_PSEUDO_REGISTER) @@ -270,7 +273,7 @@ march=MS2:exit-ms2.o%s; \ R15 IRA interrupt return address. */ #define FIXED_REGISTERS { 1, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 1, 1, 1, 1, \ - 1 \ + 1, 1, 1, 1, 1 \ } /* Like `FIXED_REGISTERS' but has 1 for each register that is clobbered (in @@ -279,7 +282,7 @@ march=MS2:exit-ms2.o%s; \ allocation of values that must live across function calls. */ #define CALL_USED_REGISTERS { 1, 1, 1, 1, 1, 0, 0, 1, \ 1, 1, 1, 1, 1, 1, 1, 1, \ - 1 \ + 1, 1, 1, 1, 1 \ } @@ -310,9 +313,9 @@ enum reg_class #define REG_CLASS_NAMES {"NO_REGS", "ALL_REGS" } #define REG_CLASS_CONTENTS \ - { \ - { 0x0, 0x0 }, \ - { (((1 << (GPR_LAST + 1)) - 1) & ~(1 << GPR_FIRST)), 0x0 }, \ + { \ + { 0x0 }, \ + { 0x000fffff }, \ } /* A C expression whose value is a register class containing hard register @@ -736,7 +739,7 @@ extern struct ms1_frame_info current_frame_info; #define REGISTER_NAMES \ { "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", \ "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", \ - "ap" } + "LOOP1", "LOOP2", "LOOP3", "LOOP4", "ap" } /* If defined, a C initializer for an array of structures containing a name and a register number. This macro defines additional names for hard registers, diff --git a/gcc/config/ms1/ms1.md b/gcc/config/ms1/ms1.md index 50e704c7bb68..104f2829e29a 100644 --- a/gcc/config/ms1/ms1.md +++ b/gcc/config/ms1/ms1.md @@ -25,6 +25,7 @@ (UNSPEC_BLOCKAGE 0) (UNSPEC_EI 1) (UNSPEC_DI 2) + (UNSPEC_LOOP 3) ]) ;; Attributes @@ -148,6 +149,58 @@ "") +;; Loop instructions. ms2 has a low overhead looping instructions. +;; these take a constant or register loop count and a loop length +;; offset. Unfortunately the loop can only be up to 256 instructions, +;; We deal with longer loops by moving the loop end upwards. To do +;; otherwise would force us to to be very pessimistic right up until +;; the end. + +;; This instruction is a placeholder to make the control flow explicit. +(define_insn "loop_end" + [(set (pc) (if_then_else + (ne (match_operand:SI 0 "register_operand" "") + (const_int 1)) + (label_ref (match_operand 1 "" "")) + (pc))) + (set (match_dup 0) (plus:SI (match_dup 0) (const_int -1))) + (unspec [(const_int 0)] UNSPEC_LOOP)] + "TARGET_MS2" + ";loop end %0,%l1" + [(set_attr "length" "0")]) + +;; This is the real looping instruction. It is placed just before the +;; loop body. We make it a branch insn, so it stays at the end of the +;; block it is in. +(define_insn "loop_init" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (match_operand:SI 1 "uns_arith_operand" "r,K")) + (unspec [(label_ref (match_operand 2 "" ""))] UNSPEC_LOOP)] + "TARGET_MS2" + "@ + loop %1,%l2 ;%0%# + loopi %1,%l2 ;%0%#" + [(set_attr "length" "4") + (set_attr "type" "branch")]) + +; operand 0 is the loop count pseudo register +; operand 1 is the number of loop iterations or 0 if it is unknown +; operand 2 is the maximum number of loop iterations +; operand 3 is the number of levels of enclosed loops +; operand 4 is the label to jump to at the top of the loop +(define_expand "doloop_end" + [(parallel [(set (pc) (if_then_else + (ne (match_operand:SI 0 "nonimmediate_operand" "") + (const_int 0)) + (label_ref (match_operand 4 "" "")) + (pc))) + (set (match_dup 0) + (plus:SI (match_dup 0) + (const_int -1))) + (clobber (match_scratch:SI 5 ""))])] + "TARGET_MS1_16_003 || TARGET_MS2" + {ms1_add_loop ();}) + ;; Moves (define_expand "loadqi"