OSDN Git Service

* v850.c (ep_memory_offset): New function.
authorlaw <law@138bc75d-0d04-0410-961f-82ee72b054a4>
Sat, 18 Oct 1997 05:46:19 +0000 (05:46 +0000)
committerlaw <law@138bc75d-0d04-0410-961f-82ee72b054a4>
Sat, 18 Oct 1997 05:46:19 +0000 (05:46 +0000)
        (ep_memory_operand, substitute_ep_register, v850_reorg): Call it.

        * v850.h (CONST_OK_FOR_*): Add and correct comments.
        (CONSTANT_ADDRESS_P): Add comment.
        (EXTRA_CONSTRAINT): Define 'U'.
        * v850.md: Add comments on bit field instructions.
        (addsi3): Delete &r/r/r alternative.  Add r/r/U alternative.
        (lshrsi3): Use N not J constraint.

        * v850.md (v850_tst1+1): New define_split for tst1 instruction.

        * v850.c (reg_or_0_operand): Call register_operand.
        (reg_or_int5_operand): Likewise.
        * v850.h (MASK_BIG_SWITCH, TARGET_BIG_SWITCH): New macros.
        (TARGET_SWITCHES): Add "big-switch".
        (ASM_OUTPUT_ADDR_VEC_ELT, ASM_OUTPUT_ADDR_DIFF_ELT, CASE_VECTOR_MODE,
        ASM_OUTPUT_BEFORE_BASE_LABEL): Add support for TARGET_BIG_SWITCH.
        (CASE_DROPS_THROUGH): Comment out.
        (CASE_VECTOR_PC_RELATIVE, JUMP_TABLES_IN_TEXT_SECTION): Define.
        * v850.md (cmpsi): Delete compare mode.
        (casesi): New pattern.

        * v850.h (CONST_OK_FOR_N): Delete redundant compare against zero.
        * v850.md (ashlsi3): Use SImode not QImode for shift count.
        (lshrsi3): Likewise.

        * v850.c (print_operand): Add 'c', 'C', and 'z' support.  Delete
        unreachable switch statement after 'b' support.  Remove "b" from
        strings for 'b' support.
        * v850.md (branch_normal, branch_invert): Change %b to b%b.

v850 patches from Jim.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@15992 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/config/v850/v850.c
gcc/config/v850/v850.h
gcc/config/v850/v850.md

index 16a35e5..45adb84 100644 (file)
@@ -1,3 +1,37 @@
+Fri Oct 17 23:48:52 1997  Jim Wilson  (wilson@cygnus.com)
+
+       * v850.c (ep_memory_offset): New function.
+       (ep_memory_operand, substitute_ep_register, v850_reorg): Call it.
+
+       * v850.h (CONST_OK_FOR_*): Add and correct comments.
+       (CONSTANT_ADDRESS_P): Add comment.
+       (EXTRA_CONSTRAINT): Define 'U'.
+       * v850.md: Add comments on bit field instructions.
+       (addsi3): Delete &r/r/r alternative.  Add r/r/U alternative.
+       (lshrsi3): Use N not J constraint. 
+
+       * v850.md (v850_tst1+1): New define_split for tst1 instruction.
+
+       * v850.c (reg_or_0_operand): Call register_operand.
+       (reg_or_int5_operand): Likewise.
+       * v850.h (MASK_BIG_SWITCH, TARGET_BIG_SWITCH): New macros.
+       (TARGET_SWITCHES): Add "big-switch".
+       (ASM_OUTPUT_ADDR_VEC_ELT, ASM_OUTPUT_ADDR_DIFF_ELT, CASE_VECTOR_MODE,
+       ASM_OUTPUT_BEFORE_BASE_LABEL): Add support for TARGET_BIG_SWITCH.
+       (CASE_DROPS_THROUGH): Comment out.
+       (CASE_VECTOR_PC_RELATIVE, JUMP_TABLES_IN_TEXT_SECTION): Define.
+       * v850.md (cmpsi): Delete compare mode.
+       (casesi): New pattern.
+
+       * v850.h (CONST_OK_FOR_N): Delete redundant compare against zero.
+       * v850.md (ashlsi3): Use SImode not QImode for shift count.
+       (lshrsi3): Likewise.
+
+       * v850.c (print_operand): Add 'c', 'C', and 'z' support.  Delete
+       unreachable switch statement after 'b' support.  Remove "b" from
+       strings for 'b' support.
+       * v850.md (branch_normal, branch_invert): Change %b to b%b.
+
 Fri Oct 17 23:33:20 1997  Jeffrey A Law  (law@cygnus.com)
 
        * Makefile.in (LIBGCC2_CFLAGS): Avoid a backslash then an
index 03dd1d0..79ae10f 100644 (file)
@@ -307,73 +307,46 @@ print_operand (file, x, code)
     {
     case 'b':
     case 'B':
-      switch (code == 'b' ? GET_CODE (x) : reverse_condition (GET_CODE (x)))
+    case 'c':
+    case 'C':
+      switch ((code == 'B' || code == 'C')
+             ? reverse_condition (GET_CODE (x)) : GET_CODE (x))
        {
          case NE:
-           fprintf (file, "bne");
+           if (code == 'c' || code == 'C')
+             fprintf (file, "nz");
+           else
+             fprintf (file, "ne");
            break;
          case EQ:
-           fprintf (file, "be");
+           if (code == 'c' || code == 'C')
+             fprintf (file, "z");
+           else
+             fprintf (file, "e");
            break;
          case GE:
-           fprintf (file, "bge");
+           fprintf (file, "ge");
            break;
          case GT:
-           fprintf (file, "bgt");
+           fprintf (file, "gt");
            break;
          case LE:
-           fprintf (file, "ble");
+           fprintf (file, "le");
            break;
          case LT:
-           fprintf (file, "blt");
+           fprintf (file, "lt");
            break;
          case GEU:
-           fprintf (file, "bnl");
+           fprintf (file, "nl");
            break;
          case GTU:
-           fprintf (file, "bh");
+           fprintf (file, "h");
            break;
          case LEU:
-           fprintf (file, "bnh");
+           fprintf (file, "nh");
            break;
          case LTU:
-           fprintf (file, "bl");
-           break;
-         default:
-           abort ();
-       }
-      break;
-      switch (GET_CODE (x))
-       {
-         case NE:
-           fprintf (file, "be");
-           break;
-         case EQ:
-           fprintf (file, "bne");
-           break;
-         case GE:
-           fprintf (file, "blt");
-           break;
-         case GT:
-           fprintf (file, "bgt");
-           break;
-         case LE:
-           fprintf (file, "ble");
-           break;
-         case LT:
-           fprintf (file, "blt");
-           break;
-         case GEU:
-           fprintf (file, "bnl");
-           break;
-         case GTU:
-           fprintf (file, "bh");
-           break;
-         case LEU:
-           fprintf (file, "bnh");
-           break;
-         case LTU:
-           fprintf (file, "bl");
+           fprintf (file, "l");
            break;
          default:
            abort ();
@@ -504,6 +477,14 @@ print_operand (file, x, code)
     case '.':                  /* register r0 */
       fputs (reg_names[0], file);
       break;
+    case 'z':                  /* reg or zero */
+      if (x == const0_rtx)
+       fputs (reg_names[0], file);
+      else if (GET_CODE (x) == REG)
+       fputs (reg_names[REGNO (x)], file);
+      else
+       abort ();
+      break;
     default:
       switch (GET_CODE (x))
        {
@@ -821,43 +802,54 @@ output_move_double (operands)
 }
 
 \f
-/* Return true if OP is a valid short EP memory reference */
+/* Return maximum offset supported for a short EP memory reference of mode
+   MODE and signedness UNSIGNEDP.  */
 
 int
-ep_memory_operand (op, mode, unsigned_load)
-     rtx op;
+ep_memory_offset (mode, unsignedp)
      enum machine_mode mode;
-     int unsigned_load;
+     int unsignedp;
 {
-  rtx addr, op0, op1;
-  int max_offset;
-  int mask;
-
-  if (GET_CODE (op) != MEM)
-    return FALSE;
+  int max_offset = 0;
 
-  switch (GET_MODE (op))
+  switch (mode)
     {
-    default:
-      return FALSE;
-
     case QImode:
-         max_offset = (1 << 7);
-      mask = 0;
+      max_offset = (1 << 7);
       break;
 
     case HImode:
-         max_offset = (1 << 8);
-      mask = 1;
+      max_offset = (1 << 8);
       break;
 
     case SImode:
     case SFmode:
       max_offset = (1 << 8);
-      mask = 3;
       break;
     }
 
+  return max_offset;
+}
+
+/* Return true if OP is a valid short EP memory reference */
+
+int
+ep_memory_operand (op, mode, unsigned_load)
+     rtx op;
+     enum machine_mode mode;
+     int unsigned_load;
+{
+  rtx addr, op0, op1;
+  int max_offset;
+  int mask;
+
+  if (GET_CODE (op) != MEM)
+    return FALSE;
+
+  max_offset = ep_memory_offset (mode, unsigned_load);
+
+  mask = GET_MODE_SIZE (mode) - 1;
+
   addr = XEXP (op, 0);
   if (GET_CODE (addr) == CONST)
     addr = XEXP (addr, 0);
@@ -905,23 +897,8 @@ reg_or_0_operand (op, mode)
   else if (GET_CODE (op) == CONST_DOUBLE)
     return CONST_DOUBLE_OK_FOR_G (op);
 
-  else if (GET_CODE (op) == REG)
-    return TRUE;
-
-  else if (GET_CODE (op) == SUBREG)
-    {
-      do {
-       op = SUBREG_REG (op);
-      } while (GET_CODE (op) == SUBREG);
-
-      if (GET_CODE (op) == MEM && !reload_completed)
-       return TRUE;
-
-      else if (GET_CODE (op) == REG)
-       return TRUE;
-    }
-
-  return FALSE;
+  else
+    return register_operand (op, mode);
 }
 
 /* Return true if OP is either a register or a signed five bit integer */
@@ -934,23 +911,8 @@ reg_or_int5_operand (op, mode)
   if (GET_CODE (op) == CONST_INT)
     return CONST_OK_FOR_J (INTVAL (op));
 
-  else if (GET_CODE (op) == REG)
-    return TRUE;
-
-  else if (GET_CODE (op) == SUBREG)
-    {
-      do {
-       op = SUBREG_REG (op);
-      } while (GET_CODE (op) == SUBREG);
-
-      if (GET_CODE (op) == MEM && !reload_completed)
-       return TRUE;
-
-      else if (GET_CODE (op) == REG)
-       return TRUE;
-    }
-
-  return FALSE;
+  else
+    return register_operand (op, mode);
 }
 
 /* Return true if OP is a valid call operand.  */
@@ -1083,6 +1045,8 @@ substitute_ep_register (first_insn, last_insn, uses, regno, p_r1, p_ep)
          if (pattern)
            {
              rtx *p_mem;
+             /* Memory operands are signed by default.  */
+             int unsignedp = FALSE;
 
              if (GET_CODE (SET_DEST (pattern)) == MEM
                  && GET_CODE (SET_SRC (pattern)) == MEM)
@@ -1108,9 +1072,9 @@ substitute_ep_register (first_insn, last_insn, uses, regno, p_r1, p_ep)
                           && GET_CODE (XEXP (addr, 0)) == REG
                           && REGNO (XEXP (addr, 0)) == regno
                           && GET_CODE (XEXP (addr, 1)) == CONST_INT
-                          && ((unsigned)INTVAL (XEXP (addr, 1))) < 256
-                          && (GET_MODE (*p_mem) != QImode
-                              || ((unsigned)INTVAL (XEXP (addr, 1))) < 128))
+                          && (((unsigned)INTVAL (XEXP (addr, 1)))
+                              < ep_memory_offset (GET_MODE (*p_mem),
+                                                  unsignedp)))
                    *p_mem = change_address (*p_mem, VOIDmode,
                                             gen_rtx (PLUS, Pmode,
                                                      *p_ep, XEXP (addr, 1)));
@@ -1216,6 +1180,8 @@ void v850_reorg (start_insn)
              rtx src = SET_SRC (pattern);
              rtx dest = SET_DEST (pattern);
              rtx mem;
+             /* Memory operands are signed by default.  */
+             int unsignedp = FALSE;
 
              if (GET_CODE (dest) == MEM && GET_CODE (src) == MEM)
                mem = NULL_RTX;
@@ -1229,7 +1195,7 @@ void v850_reorg (start_insn)
              else
                mem = NULL_RTX;
 
-             if (mem && ep_memory_operand (mem, GET_MODE (mem), FALSE))
+             if (mem && ep_memory_operand (mem, GET_MODE (mem), unsignedp))
                use_ep = TRUE;
 
              else if (!use_ep && mem
@@ -1248,9 +1214,8 @@ void v850_reorg (start_insn)
                  else if (GET_CODE (addr) == PLUS
                           && GET_CODE (XEXP (addr, 0)) == REG
                           && GET_CODE (XEXP (addr, 1)) == CONST_INT
-                          && ((unsigned)INTVAL (XEXP (addr, 1))) < 256
-                          && (GET_MODE (mem) != QImode
-                              || ((unsigned)INTVAL (XEXP (addr, 1))) < 128))
+                          && (((unsigned)INTVAL (XEXP (addr, 1)))
+                              < ep_memory_offset (GET_MODE (mem), unsignedp)))
                    {
                      short_p = TRUE;
                      regno = REGNO (XEXP (addr, 0));
index 8e7f9fe..00e6fd1 100644 (file)
@@ -58,6 +58,8 @@ extern int target_flags;
 #define MASK_CPU                0x00000030
 #define MASK_V850               0x00000010
 
+#define MASK_BIG_SWITCH                0x00000100
+
 #ifndef MASK_DEFAULT
 #define MASK_DEFAULT            MASK_V850
 #endif
@@ -97,6 +99,9 @@ extern int target_flags;
 /* Whether to call out-of-line functions to save registers or not.  */
 #define TARGET_PROLOG_FUNCTION (target_flags & MASK_PROLOG_FUNCTION)
 
+/* Whether to emit 2 byte per entry or 4 byte per entry switch tables.  */
+#define TARGET_BIG_SWITCH (target_flags & MASK_BIG_SWITCH)
+
 /* General debug flag */
 #define TARGET_DEBUG (target_flags & MASK_DEBUG)
 
@@ -119,6 +124,7 @@ extern int target_flags;
    { "debug",                   MASK_DEBUG },                          \
    { "v850",                    MASK_V850 },                           \
    { "v850",                    -(MASK_V850 ^ MASK_CPU) },             \
+   { "big-switch",              MASK_BIG_SWITCH },                     \
    EXTRA_SWITCHES                                                      \
    { "",                        TARGET_DEFAULT}}
 
@@ -468,19 +474,21 @@ enum reg_class {
 
 #define INT_7_BITS(VALUE) ((unsigned) (VALUE) + 0x40 < 0x80)
 #define INT_8_BITS(VALUE) ((unsigned) (VALUE) + 0x80 < 0x100)
-/* 0 bits */
+/* zero */
 #define CONST_OK_FOR_I(VALUE) ((VALUE) == 0)
-/* 4 bits */
+/* 5 bit signed immediate */
 #define CONST_OK_FOR_J(VALUE) ((unsigned) (VALUE) + 0x10 < 0x20)
-/* 15 bits */
+/* 16 bit signed immediate */
 #define CONST_OK_FOR_K(VALUE) ((unsigned) (VALUE) + 0x8000 < 0x10000)
+/* valid constant for movhi instruction.  */
 #define CONST_OK_FOR_L(VALUE) \
   (((unsigned) ((int) (VALUE) >> 16) + 0x8000 < 0x10000) \
    && CONST_OK_FOR_I ((VALUE & 0xffff)))
-/* 16 bits */
-#define CONST_OK_FOR_M(VALUE) ((unsigned)(VALUE) < 0x10000
+/* 16 bit unsigned immediate */
+#define CONST_OK_FOR_M(VALUE) ((unsigned)(VALUE) < 0x10000)
+/* 5 bit unsigned immediate in shift instructions */
+#define CONST_OK_FOR_N(VALUE) ((unsigned) (VALUE) <= 31)
 
-#define CONST_OK_FOR_N(VALUE) ((unsigned) VALUE >= 0 && (unsigned) VALUE <= 31) /* 5 bit signed immediate in shift instructions */
 #define CONST_OK_FOR_O(VALUE) 0
 #define CONST_OK_FOR_P(VALUE) 0
 
@@ -799,6 +807,9 @@ extern int current_function_anonymous_args;
 \f
 /* 1 if X is an rtx for a constant that is a valid address.  */
 
+/* ??? This seems too exclusive.  May get better code by accepting more
+   possibilities here, in particular, should accept ZDA_NAME SYMBOL_REFs.  */
+
 #define CONSTANT_ADDRESS_P(X)   \
   (GET_CODE (X) == CONST_INT                           \
    && CONST_OK_FOR_K (INTVAL (X)))
@@ -864,7 +875,11 @@ extern int current_function_anonymous_args;
   : (C) == 'R' ? special_symbolref_operand (OP, VOIDmode)              \
   : (C) == 'S' ? (GET_CODE (OP) == SYMBOL_REF && ! ZDA_NAME_P (XSTR (OP, 0))) \
   : (C) == 'T' ? 0                                                     \
-  : (C) == 'U' ? 0                                                      \
+  : (C) == 'U' ? ((GET_CODE (OP) == SYMBOL_REF && ZDA_NAME_P (XSTR (OP, 0))) \
+                 || (GET_CODE (OP) == CONST                            \
+                     && GET_CODE (XEXP (OP, 0)) == PLUS                \
+                     && GET_CODE (XEXP (XEXP (OP, 0), 0)) == SYMBOL_REF \
+                     && ZDA_NAME_P (XSTR (XEXP (XEXP (OP, 0), 0), 0)))) \
   : 0)
 \f
 /* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression
@@ -1266,12 +1281,15 @@ do { char dstr[30];                                     \
 /* This is how to output an element of a case-vector that is absolute.  */
 
 #define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \
-  asm_fprintf (FILE, "\t%s .L%d\n", ".long", VALUE)
+  asm_fprintf (FILE, "\t%s .L%d\n",                                    \
+              (TARGET_BIG_SWITCH ? ".long" : ".short"), VALUE)
 
 /* This is how to output an element of a case-vector that is relative.  */
 
 #define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, VALUE, REL) \
-  fprintf (FILE, "\t%s .L%d-.L%d\n", ".long", VALUE, REL)
+  fprintf (FILE, "\t%s .L%d-.L%d\n",                                   \
+          (TARGET_BIG_SWITCH ? ".long" : ".short"),                    \
+          VALUE, REL)
 
 #define ASM_OUTPUT_ALIGN(FILE,LOG)     \
   if ((LOG) != 0)                      \
@@ -1292,12 +1310,26 @@ do { char dstr[30];                                     \
 
 /* Specify the machine mode that this machine uses
    for the index in the tablejump instruction.  */
-#define CASE_VECTOR_MODE Pmode
+#define CASE_VECTOR_MODE (TARGET_BIG_SWITCH ? SImode : HImode)
 
 /* Define this if the case instruction drops through after the table
    when the index is out of range.  Don't define it if the case insn
    jumps to the default label instead.  */
-#define CASE_DROPS_THROUGH
+/* #define CASE_DROPS_THROUGH */
+
+/* We must use a PC relative entry for small tables.  It would be more
+   efficient to use an absolute entry for big tables, but this is not
+   a runtime choice yet.  */
+#define CASE_VECTOR_PC_RELATIVE
+
+/* The switch instruction requires that the jump table immediately follow
+   it. */
+#define JUMP_TABLES_IN_TEXT_SECTION
+
+/* svr4.h defines this assuming that 4 byte alignment is required.  */
+#undef ASM_OUTPUT_BEFORE_CASE_LABEL
+#define ASM_OUTPUT_BEFORE_CASE_LABEL(FILE,PREFIX,NUM,TABLE) \
+  ASM_OUTPUT_ALIGN ((FILE), (TARGET_BIG_SWITCH ? 2 : 1));
 
 #define WORD_REGISTER_OPERATIONS
 
index bb5020d..c2095f2 100644 (file)
   [(set_attr "length" "4")
    (set_attr "cc" "clobber")])
 
+;; This replaces ld.b;sar;andi with tst1;setf nz.
+
+;; ??? The zero_extract sets the Z bit to the opposite of what one would
+;; expect.  This perhaps should be wrapped in a (eq: X (const_int 0)).
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (zero_extract:SI (match_operand:QI 1 "memory_operand" "")
+                        (const_int 1)
+                        (match_operand 2 "const_int_operand" "")))]
+  ""
+  [(set (cc0) (zero_extract:SI (match_dup 1)
+                              (const_int 1)
+                              (match_dup 2)))
+   (set (match_dup 0) (ne:SI (cc0) (const_int 0)))])
+
 (define_insn "tstsi"
   [(set (cc0) (match_operand:SI 0 "register_operand" "r"))]
   ""
 
 (define_insn "cmpsi"
   [(set (cc0)
-       (compare:SI (match_operand:SI 0 "register_operand" "r,r")
-                   (match_operand:SI 1 "reg_or_int5_operand" "r,J")))]
+       (compare (match_operand:SI 0 "register_operand" "r,r")
+                (match_operand:SI 1 "reg_or_int5_operand" "r,J")))]
   ""
   "@
   cmp %1,%0
 ;; ----------------------------------------------------------------------
 
 (define_insn "addsi3"
-  [(set (match_operand:SI 0 "register_operand" "=r,r,&r")
+  [(set (match_operand:SI 0 "register_operand" "=r,r,r")
        (plus:SI (match_operand:SI 1 "register_operand" "%0,r,r")
-                (match_operand:SI 2 "nonmemory_operand" "rJ,K,r")))]
+                (match_operand:SI 2 "nonmemory_operand" "rJ,K,U")))]
   ""
   "@
    add %2,%0
    addi %2,%1,%0
-   mov %1,%0\;add %2,%0"
-  [(set_attr "length" "2,4,6")
-   (set_attr "cc" "set_zn")])
+   addi %O2(%P2),%1,%0"
+  [(set_attr "length" "2,4,4")
+   (set_attr "cc" "set_zn,set_zn,set_zn")])
 
 ;; ----------------------------------------------------------------------
 ;; SUBTRACT INSTRUCTIONS
 ;; -----------------------------------------------------------------
 ;; BIT FIELDS
 ;; -----------------------------------------------------------------
-;; Is it worth defining insv and extv for the V850 series?!?
+
+;; ??? Is it worth defining insv and extv for the V850 series?!?
+
+;; An insv pattern would be useful, but does not get used because
+;; store_bit_field never calls insv when storing a constant value into a
+;; single-bit bitfield.
+
+;; extv/extzv patterns would be useful, but do not get used because
+;; optimize_bitfield_compare in fold-const usually converts single
+;; bit extracts into an AND with a mask.
 
 ;; -----------------------------------------------------------------
 ;; Scc INSTRUCTIONS
     return 0;
 
   if (get_attr_length (insn) == 2)
-    return \"%b1 %l0\";
+    return \"b%b1 %l0\";
   else
-    return \"%B1 .+6\;jr %l0\";
+    return \"b%B1 .+6\;jr %l0\";
 }"
  [(set (attr "length")
     (if_then_else (lt (abs (minus (match_dup 0) (pc)))
          || GET_CODE (operands[1]) == LT))
     return 0;
   if (get_attr_length (insn) == 2)
-    return \"%B1 %l0\";
+    return \"b%B1 %l0\";
   else
-    return \"%b1 .+6\;jr %l0\";
+    return \"b%b1 .+6\;jr %l0\";
 }"
  [(set (attr "length")
     (if_then_else (lt (abs (minus (match_dup 0) (pc)))
   [(set_attr "length" "2")
    (set_attr "cc" "none")])
 
+(define_expand "casesi"
+  [(match_operand:SI 0 "register_operand" "")
+   (match_operand:SI 1 "register_operand" "")
+   (match_operand:SI 2 "register_operand" "")
+   (match_operand 3 "" "") (match_operand 4 "" "")]
+  ""
+  "
+{
+  rtx reg = gen_reg_rtx (SImode);
+  rtx tableaddress = gen_reg_rtx (SImode);
+  rtx mem;
+
+  /* Subtract the lower bound from the index.  */
+  emit_insn (gen_subsi3 (reg, operands[0], operands[1]));
+  /* Compare the result against the number of table entries.  */
+  emit_insn (gen_cmpsi (reg, operands[2]));
+  /* Branch to the default label if out of range of the table.  */
+  emit_jump_insn (gen_bgtu (operands[4]));
+
+  /* Shift index for the table array access.  */
+  emit_insn (gen_ashlsi3 (reg, reg, GEN_INT (TARGET_BIG_SWITCH ? 2 : 1)));
+  /* Load the table address into a psuedo.  */
+  emit_insn (gen_movsi (tableaddress,
+                       gen_rtx (LABEL_REF, VOIDmode, operands[3])));
+  /* Add the table address to the index.  */
+  emit_insn (gen_addsi3 (reg, reg, tableaddress));
+  /* Load the table entry.  */
+  mem = gen_rtx (MEM, CASE_VECTOR_MODE, reg);
+  RTX_UNCHANGING_P (mem);
+  if (! TARGET_BIG_SWITCH)
+    {
+      rtx reg2 = gen_reg_rtx (HImode);
+      emit_insn (gen_movhi (reg2, mem));
+      emit_insn (gen_extendhisi2 (reg, reg2));
+    }
+  else
+    emit_insn (gen_movsi (reg, mem));
+  /* Add the table address.  */
+  emit_insn (gen_addsi3 (reg, reg, tableaddress));
+  /* Branch to the switch label.  */
+  emit_jump_insn (gen_tablejump (reg, operands[3]));
+  DONE;
+}")
+
 ;; Call subroutine with no return value.
 
 (define_expand "call"
   [(set (match_operand:SI 0 "register_operand" "=r,r")
        (ashift:SI
         (match_operand:SI 1 "register_operand" "0,0")
-        (match_operand:QI 2 "nonmemory_operand" "r,N")))]
+        (match_operand:SI 2 "nonmemory_operand" "r,N")))]
   ""
   "@
   shl %2,%0
   [(set (match_operand:SI 0 "register_operand" "=r,r")
        (lshiftrt:SI
         (match_operand:SI 1 "register_operand" "0,0")
-        (match_operand:QI 2 "nonmemory_operand" "r,J")))]
+        (match_operand:SI 2 "nonmemory_operand" "r,N")))]
   ""
   "@
   shr %2,%0