===================================================================
@@ -17,6 +17,17 @@
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
+/* casesi uses a SImode switch index which is quite costly as most code will
+ work on HImode or QImode. The following pass runs right after .expand and
+ tries to fix such situations by operating on the original mode. This
+ reduces code size and register pressure.
+
+ The assertion is that the code generated by casesi is unaltered and a
+ a sign-extend or zero-extend from QImode or HImode precedes the casesi
+ insns withaout any insns in between. */
+
+INSERT_PASS_AFTER (pass_expand, 1, avr_pass_casesi);
+
/* This avr-specific pass (re)computes insn notes, in particular REG_DEAD
notes which are used by `avr.c::reg_unused_after' and branch offset
computations. These notes must be correct, i.e. there must be no
===================================================================
@@ -129,6 +129,7 @@ extern bool avr_mem_memx_p (rtx);
extern bool avr_load_libgcc_p (rtx);
extern bool avr_xload_libgcc_p (machine_mode);
extern rtx avr_eval_addr_attrib (rtx x);
+extern bool avr_casei_sequence_check_operands (rtx *xop);
static inline unsigned
regmask (machine_mode mode, unsigned regno)
@@ -158,6 +159,7 @@ namespace gcc { class context; }
class rtl_opt_pass;
extern rtl_opt_pass *make_avr_pass_recompute_notes (gcc::context *);
+extern rtl_opt_pass *make_avr_pass_casesi (gcc::context *);
/* From avr-log.c */
===================================================================
@@ -47,6 +47,7 @@
#include "builtins.h"
#include "context.h"
#include "tree-pass.h"
+#include "print-rtl.h"
/* This file should be included last. */
#include "target-def.h"
@@ -329,6 +330,41 @@ public:
}
}; // avr_pass_recompute_notes
+static const pass_data avr_pass_data_casesi =
+{
+ RTL_PASS, // type
+ "", // name (will be patched)
+ OPTGROUP_NONE, // optinfo_flags
+ TV_DF_SCAN, // tv_id
+ 0, // properties_required
+ 0, // properties_provided
+ 0, // properties_destroyed
+ 0, // todo_flags_start
+ 0 // todo_flags_finish
+};
+
+
+class avr_pass_casesi : public rtl_opt_pass
+{
+public:
+ avr_pass_casesi (gcc::context *ctxt, const char *name)
+ : rtl_opt_pass (avr_pass_data_casesi, ctxt)
+ {
+ this->name = name;
+ }
+
+ void avr_rest_of_handle_casesi (function*);
+
+ virtual bool gate (function*) { return optimize > 0; }
+
+ virtual unsigned int execute (function *func)
+ {
+ avr_rest_of_handle_casesi (func);
+
+ return 0;
+ }
+}; // avr_pass_casesi
+
} // anon namespace
rtl_opt_pass*
@@ -337,6 +373,320 @@ make_avr_pass_recompute_notes (gcc::cont
return new avr_pass_recompute_notes (ctxt, "avr-notes-free-cfg");
}
+rtl_opt_pass*
+make_avr_pass_casesi (gcc::context *ctxt)
+{
+ return new avr_pass_casesi (ctxt, "avr-casesi");
+}
+
+
+/* Make one parallel insn with all the patterns from insns i[0]..i[5]. */
+
+static rtx_insn*
+avr_parallel_insn_from_insns (rtx_insn *i[6])
+{
+ rtvec vec = gen_rtvec (6, PATTERN (i[0]), PATTERN (i[1]), PATTERN (i[2]),
+ PATTERN (i[3]), PATTERN (i[4]), PATTERN (i[5]));
+ start_sequence();
+ emit (gen_rtx_PARALLEL (VOIDmode, vec));
+ rtx_insn *insn = get_insns();
+ end_sequence();
+
+ return insn;
+}
+
+
+/* Return true if we see an insn stream generated by casesi expander together
+ with an extension to SImode of the switch value.
+
+ If this is the case, fill in the insns from casesi to INSNS[1..5] and
+ the SImode extension to INSNS[0]. Moreover, extract the operands of
+ pattern casesi_<mode>_sequence forged from the sequence to recog_data. */
+
+static bool
+avr_is_casesi_sequence (basic_block bb, rtx_insn *insn, rtx_insn *insns[6])
+{
+ rtx set_5, set_0;
+
+ /* A first and quick test for a casesi sequences. As a side effect of
+ the test, harvest respective insns to INSNS[0..5]. */
+
+ if (!(JUMP_P (insns[5] = insn)
+ // casesi is the only insn that comes up with UNSPEC_INDEX_JMP,
+ // hence the following test ensures that we are actually dealing
+ // with code from casesi.
+ && (set_5 = single_set (insns[5]))
+ && UNSPEC == GET_CODE (SET_SRC (set_5))
+ && UNSPEC_INDEX_JMP == XINT (SET_SRC (set_5), 1)
+
+ && (insns[4] = prev_real_insn (insns[5]))
+ && (insns[3] = prev_real_insn (insns[4]))
+ && (insns[2] = prev_real_insn (insns[3]))
+ && (insns[1] = prev_real_insn (insns[2]))
+
+ // Insn prior to casesi.
+ && (insns[0] = prev_real_insn (insns[1]))
+ && (set_0 = single_set (insns[0]))
+ && extend_operator (SET_SRC (set_0), SImode)))
+ {
+ return false;
+ }
+
+ if (dump_file)
+ {
+ fprintf (dump_file, ";; Sequence from casesi in "
+ "[bb %d]:\n\n", bb->index);
+ for (int i = 0; i < 6; i++)
+ print_rtl_single (dump_file, insns[i]);
+ }
+
+ /* We have to deal with quite some operands. Extracting them by hand
+ would be tedious, therefore wrap the insn patterns into a parallel,
+ run recog against it and then use insn extract to get the operands. */
+
+ rtx_insn *xinsn = avr_parallel_insn_from_insns (insns);
+
+ INSN_CODE (xinsn) = recog (PATTERN (xinsn), xinsn, NULL /* num_clobbers */);
+
+ /* Failing to recognize means that someone changed the casesi expander or
+ that some passes prior to this one performed some unexpected changes.
+ Gracefully drop such situations instead of aborting. */
+
+ if (INSN_CODE (xinsn) < 0)
+ {
+ if (dump_file)
+ fprintf (dump_file, ";; Sequence not recognized, giving up.\n\n");
+
+ return false;
+ }
+
+ gcc_assert (CODE_FOR_casesi_qi_sequence == INSN_CODE (xinsn)
+ || CODE_FOR_casesi_hi_sequence == INSN_CODE (xinsn));
+
+ extract_insn (xinsn);
+
+ // Assert on the anatomy of xinsn's operands we are going to work with.
+
+ gcc_assert (11 == recog_data.n_operands);
+ gcc_assert (4 == recog_data.n_dups);
+
+ if (dump_file)
+ {
+ fprintf (dump_file, ";; Operands extracted:\n");
+ for (int i = 0; i < recog_data.n_operands; i++)
+ avr_fdump (dump_file, ";; $%d = %r\n", i, recog_data.operand[i]);
+ fprintf (dump_file, "\n");
+ }
+
+ return true;
+}
+
+
+/* Perform some extra checks on operands of casesi_<mode>_sequence.
+ Not all operand dependencies can be described by means of predicates.
+ This function performs left over checks and should always return true.
+ Returning false means that someone changed the casesi expander but did
+ not adjust casesi_<mode>_sequence. */
+
+bool
+avr_casei_sequence_check_operands (rtx *xop)
+{
+ rtx sub_5 = NULL_RTX;
+
+ if (AVR_HAVE_EIJMP_EICALL
+ // The last clobber op of the tablejump.
+ && xop[8] == all_regs_rtx[24])
+ {
+ // $6 is: (subreg:SI ($5) 0)
+ sub_5 = xop[6];
+ }
+
+ if (!AVR_HAVE_EIJMP_EICALL
+ // $6 is: (plus:HI (subreg:SI ($5) 0)
+ // (label_ref ($3)))
+ && PLUS == GET_CODE (xop[6])
+ && LABEL_REF == GET_CODE (XEXP (xop[6], 1))
+ && rtx_equal_p (xop[3], XEXP (XEXP (xop[6], 1), 0))
+ // The last clobber op of the tablejump.
+ && xop[8] == const0_rtx)
+ {
+ sub_5 = XEXP (xop[6], 0);
+ }
+
+ if (sub_5
+ && SUBREG_P (sub_5)
+ && 0 == SUBREG_BYTE (sub_5)
+ && rtx_equal_p (xop[5], SUBREG_REG (sub_5)))
+ return true;
+
+ if (dump_file)
+ fprintf (dump_file, "\n;; Failed condition for casesi_<mode>_sequence\n\n");
+
+ return false;
+}
+
+
+/* INSNS[1..5] is a sequence as generated by casesi and INSNS[0] is an
+ extension of an 8-bit or 16-bit integer to SImode. XOP contains the
+ operands of INSNS as extracted by insn_extract from pattern
+ casesi_<mode>_sequence:
+
+ $0: SImode reg switch value as result of $9.
+ $1: Negative of smallest index in switch.
+ $2: Number of entries in switch.
+ $3: Label to table.
+ $4: Label if out-of-bounds.
+ $5: $0 + $1.
+ $6: 3-byte PC: subreg:HI ($5) + label_ref ($3)
+ 2-byte PC: subreg:HI ($5)
+ $7: HI reg index into table (Z or pseudo)
+ $8: R24 or const0_rtx (to be clobbered)
+ $9: Extension to SImode of an 8-bit or 16-bit integer register $10.
+ $10: QImode or HImode register input of $9.
+
+ Try to optimize this sequence, i.e. use the original HImode / QImode
+ switch value instead of SImode. */
+
+static void
+avr_optimize_casesi (rtx_insn *insns[6], rtx *xop)
+{
+ // Original mode of the switch value; this is QImode or HImode.
+ machine_mode mode = GET_MODE (xop[10]);
+
+ // How the original switch value was extended to SImode; this is
+ // SIGN_EXTEND or ZERO_EXTEND.
+ enum rtx_code code = GET_CODE (xop[9]);
+
+ // Lower index, upper index (plus one) and range of case calues.
+ HOST_WIDE_INT low_idx = -INTVAL (xop[1]);
+ HOST_WIDE_INT num_idx = INTVAL (xop[2]);
+ HOST_WIDE_INT hig_idx = low_idx + num_idx;
+
+ // Maximum ranges of (un)signed QImode resp. HImode.
+ int imin = QImode == mode ? INT8_MIN : INT16_MIN;
+ int imax = QImode == mode ? INT8_MAX : INT16_MAX;
+ unsigned umax = QImode == mode ? UINT8_MAX : UINT16_MAX;
+
+ // Testing the case range and whether it fits into the range of the
+ // (un)signed mode. This test should actually always pass because it
+ // makes no sense to have case values outside the mode range. Notice
+ // that case labels which are unreachable because they are outside the
+ // mode of the switch value (e.g. "case -1" for uint8_t) have already
+ // been thrown away by the middle-end.
+
+ if (SIGN_EXTEND == code
+ && low_idx >= imin
+ && hig_idx <= imax)
+ {
+ // ok
+ }
+ else if (ZERO_EXTEND == code
+ && low_idx >= 0
+ && (unsigned) hig_idx <= umax)
+ {
+ // ok
+ }
+ else
+ {
+ if (dump_file)
+ fprintf (dump_file, ";; Case ranges too big, giving up.\n\n");
+ return;
+ }
+
+ // Do normalization of switch value $10 and out-of-bound check in its
+ // original mode instead of in SImode. Use a newly created pseudo.
+ // This will replace insns[1..2].
+
+ start_sequence();
+
+ rtx_insn *seq1, *seq2, *last1, *last2;
+
+ rtx reg = copy_to_mode_reg (mode, xop[10]);
+
+ rtx (*gen_add)(rtx,rtx,rtx) = QImode == mode ? gen_addqi3 : gen_addhi3;
+ rtx (*gen_cmp)(rtx,rtx) = QImode == mode ? gen_cmpqi3 : gen_cmphi3;
+
+ emit_insn (gen_add (reg, reg, gen_int_mode (-low_idx, mode)));
+ emit_insn (gen_cmp (reg, gen_int_mode (num_idx, mode)));
+
+ seq1 = get_insns();
+ last1 = get_last_insn();
+ end_sequence();
+
+ emit_insn_before (seq1, insns[1]);
+
+ // After the out-of-bounds test and corresponding branch, use a
+ // 16-bit index. If QImode is used, extend it to HImode first.
+ // This will replace insns[4].
+
+ start_sequence();
+
+ if (QImode == mode)
+ reg = force_reg (HImode, gen_rtx_fmt_e (code, HImode, reg));
+
+ rtx pat_4 = AVR_3_BYTE_PC
+ ? gen_movhi (xop[7], reg)
+ : gen_addhi3 (xop[7], reg, gen_rtx_LABEL_REF (VOIDmode, xop[3]));
+
+ emit_insn (pat_4);
+
+ seq2 = get_insns();
+ last2 = get_last_insn();
+ end_sequence();
+
+ emit_insn_after (seq2, insns[4]);
+
+ if (dump_file)
+ {
+ fprintf (dump_file, ";; New insns: ");
+
+ for (rtx_insn *insn = seq1; ; insn = NEXT_INSN (insn))
+ {
+ fprintf (dump_file, "%d, ", INSN_UID (insn));
+ if (insn == last1)
+ break;
+ }
+ for (rtx_insn *insn = seq2; ; insn = NEXT_INSN (insn))
+ {
+ fprintf (dump_file, "%d%s", INSN_UID (insn),
+ insn == last2 ? ".\n\n" : ", ");
+ if (insn == last2)
+ break;
+ }
+
+ fprintf (dump_file, ";; Deleting insns: %d, %d, %d.\n\n",
+ INSN_UID (insns[1]), INSN_UID (insns[2]), INSN_UID (insns[4]));
+ }
+
+ // Pseudodelete the SImode and subreg of SImode insns. We don't care
+ // about the extension insns[0]: Its result is now unused and other
+ // passes will clean it up.
+
+ SET_INSN_DELETED (insns[1]);
+ SET_INSN_DELETED (insns[2]);
+ SET_INSN_DELETED (insns[4]);
+}
+
+
+void
+avr_pass_casesi::avr_rest_of_handle_casesi (function *func)
+{
+ basic_block bb;
+
+ FOR_EACH_BB_FN (bb, func)
+ {
+ rtx_insn *insn, *insns[6];
+
+ FOR_BB_INSNS (bb, insn)
+ {
+ if (avr_is_casesi_sequence (bb, insn, insns))
+ {
+ avr_optimize_casesi (insns, recog_data.operand);
+ }
+ }
+ }
+}
+
/* Set `avr_arch' as specified by `-mmcu='.
Return true on success. */
===================================================================
@@ -4598,9 +4598,9 @@ (define_insn "*reversed_tst<mode>"
(set_attr "length" "4")])
-;; "*cmpqi"
-;; "*cmpqq" "*cmpuqq"
-(define_insn "*cmp<mode>"
+;; "cmpqi3"
+;; "cmpqq3" "cmpuqq3"
+(define_insn "cmp<mode>3"
[(set (cc0)
(compare (match_operand:ALL1 0 "register_operand" "r ,r,d")
(match_operand:ALL1 1 "nonmemory_operand" "Y00,r,i")))]
@@ -4640,10 +4640,10 @@ (define_insn "*cmphi.zero-extend.1"
[(set_attr "cc" "compare")
(set_attr "length" "2")])
-;; "*cmphi"
-;; "*cmphq" "*cmpuhq"
-;; "*cmpha" "*cmpuha"
-(define_insn "*cmp<mode>"
+;; "cmphi3"
+;; "cmphq3" "cmpuhq3"
+;; "cmpha3" "cmpuha3"
+(define_insn "cmp<mode>3"
[(set (cc0)
(compare (match_operand:ALL2 0 "register_operand" "!w ,r ,r,d ,r ,d,r")
(match_operand:ALL2 1 "nonmemory_operand" "Y00,Y00,r,s ,s ,M,n Ynn")))
@@ -5153,50 +5153,115 @@ (define_insn "*tablejump.3byte-pc"
(set_attr "cc" "clobber")])
+;; FIXME: casesi comes up with an SImode switch value $0 which
+;; is quite some overhead because most code would use HI or
+;; even QI. We add an AVR specific pass .avr-casesi which
+;; tries to recover from the superfluous extension to SImode.
+;;
+;; Using "tablejump" could be a way out, but this also does
+;; not perform in a satisfying manner as the middle end will
+;; already multiply the table index by 2. Note that this
+;; multiplication is performed by libgcc's __tablejump2__.
+;; The multiplication there, however, runs *after* the table
+;; start (a byte address) has been added, not before it like
+;; "tablejump" will do.
+;;
+;; The preferred solution would be to let the middle ends pass
+;; down information on the index as an additional casesi operand.
+;;
+;; If this expander is changed, you'll likely have to go through
+;; "casesi_<mode>_sequence" (used to recog + extract casesi
+;; sequences in pass .avr-casesi) and propagate all adjustments
+;; also to that pattern and the code of the extra pass.
+
(define_expand "casesi"
- [(parallel [(set (match_dup 6)
- (minus:HI (subreg:HI (match_operand:SI 0 "register_operand" "") 0)
- (match_operand:HI 1 "register_operand" "")))
+ [(parallel [(set (match_dup 5)
+ (plus:SI (match_operand:SI 0 "register_operand")
+ (match_operand:SI 1 "const_int_operand")))
(clobber (scratch:QI))])
(parallel [(set (cc0)
- (compare (match_dup 6)
- (match_operand:HI 2 "register_operand" "")))
- (clobber (match_scratch:QI 9 ""))])
+ (compare (match_dup 5)
+ (match_operand:SI 2 "const_int_operand")))
+ (clobber (scratch:QI))])
(set (pc)
(if_then_else (gtu (cc0)
(const_int 0))
- (label_ref (match_operand 4 "" ""))
+ (label_ref (match_operand 4))
(pc)))
- (set (match_dup 10)
- (match_dup 7))
+ (set (match_dup 7)
+ (match_dup 6))
(parallel [(set (pc)
- (unspec:HI [(match_dup 10)] UNSPEC_INDEX_JMP))
+ (unspec:HI [(match_dup 7)] UNSPEC_INDEX_JMP))
(use (label_ref (match_dup 3)))
- (clobber (match_dup 10))
+ (clobber (match_dup 7))
(clobber (match_dup 8))])]
""
{
- operands[6] = gen_reg_rtx (HImode);
+ operands[1] = simplify_unary_operation (NEG, SImode, operands[1], SImode);
+ operands[5] = gen_reg_rtx (SImode);
+ operands[6] = simplify_gen_subreg (HImode, operands[5], SImode, 0);
if (AVR_HAVE_EIJMP_EICALL)
{
- operands[7] = operands[6];
+ operands[7] = gen_rtx_REG (HImode, REG_Z);
operands[8] = all_regs_rtx[24];
- operands[10] = gen_rtx_REG (HImode, REG_Z);
}
else
{
- operands[7] = gen_rtx_PLUS (HImode, operands[6],
+ operands[6] = gen_rtx_PLUS (HImode, operands[6],
gen_rtx_LABEL_REF (VOIDmode, operands[3]));
+ operands[7] = gen_reg_rtx (HImode);
operands[8] = const0_rtx;
- operands[10] = operands[6];
}
})
+;; This insn is used only for easy operand extraction.
+;; The elements must match an extension to SImode plus
+;; a sequence generated by casesi above.
+
+;; "casesi_qi_sequence"
+;; "casesi_hi_sequence"
+(define_insn "casesi_<mode>_sequence"
+ [(set (match_operand:SI 0 "register_operand")
+ (match_operator:SI 9 "extend_operator"
+ [(match_operand:QIHI 10 "register_operand")]))
+
+ ;; What follows is a matcher for code from casesi.
+ ;; We keep the same operand numbering (except for $9 and $10
+ ;; which don't appear in casesi).
+ (parallel [(set (match_operand:SI 5 "register_operand")
+ (plus:SI (match_dup 0)
+ (match_operand:SI 1 "const_int_operand")))
+ (clobber (scratch:QI))])
+ (parallel [(set (cc0)
+ (compare (match_dup 5)
+ (match_operand:SI 2 "const_int_operand")))
+ (clobber (scratch:QI))])
+
+ (set (pc)
+ (if_then_else (gtu (cc0)
+ (const_int 0))
+ (label_ref (match_operand 4))
+ (pc)))
+
+ (set (match_operand:HI 7 "register_operand")
+ (match_operand:HI 6))
+
+ (parallel [(set (pc)
+ (unspec:HI [(match_dup 7)] UNSPEC_INDEX_JMP))
+ (use (label_ref (match_operand 3)))
+ (clobber (match_dup 7))
+ (clobber (match_operand:QI 8))])]
+ "optimize
+ && avr_casei_sequence_check_operands (operands)"
+ { gcc_unreachable(); }
+ )
+
+
;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
;; This instruction sets Z flag
@@ -5549,7 +5614,7 @@ (define_peephole ; "*cpse.eq"
;; This peephole avoids code like
;;
-;; TST Rn ; *cmpqi
+;; TST Rn ; cmpqi3
;; BREQ .+2 ; branch
;; RJMP .Lm
;;
===================================================================
@@ -178,6 +178,10 @@ (define_predicate "simple_comparison_ope
(and (match_operand 0 "comparison_operator")
(not (match_code "gt,gtu,le,leu"))))
+;; True for SIGN_EXTEND, ZERO_EXTEND.
+(define_predicate "extend_operator"
+ (match_code "sign_extend,zero_extend"))
+
;; Return true if OP is a valid call operand.
(define_predicate "call_insn_operand"
(and (match_code "mem")
===================================================================
@@ -0,0 +1,347 @@
+/* { dg-do run } */
+/* { dg-options "-Os -Wno-overflow -fno-tree-switch-conversion" } */
+
+#include "exit-abort.h"
+volatile unsigned char y;
+
+unsigned char __attribute__((noinline,noclone))
+foo1 (char x)
+{
+ switch (x)
+ {
+ case (char)0x11: y = 7; break;
+ case (char)0x12: y = 4; break;
+ case (char)0x13: y = 8; break;
+ case (char)0x14: y = 21; break;
+ case (char)0x15: y = 65; break;
+ case (char)0x16: y = 27; break;
+ case (char)0x17: y = 72; break;
+ case (char)0x18: y = 39; break;
+ default: y=0;
+ }
+ return y;
+}
+
+unsigned char __attribute__((noinline,noclone))
+foo2 (char x)
+{
+ switch (x)
+ {
+ case 0x01: y = 7; break;
+ case 0x02: y = 4; break;
+ case 0x03: y = 8; break;
+ case 0x04: y = 21; break;
+ case 0x05: y = 65; break;
+ case 0x06: y = 27; break;
+ case 0x07: y = 72; break;
+ case 0x08: y = 39; break;
+ default: y=0;
+ }
+ return y;
+}
+
+unsigned char __attribute__((noinline,noclone))
+foo3 (char x)
+{
+ switch (x)
+ {
+ case 0x1000001L: y = 7; break;
+ case 0x1000002L: y = 4; break;
+ case 0x1000003L: y = 8; break;
+ case 0x1000004L: y = 21; break;
+ case 0x1000005L: y = 65; break;
+ case 0x1000006L: y = 27; break;
+ case 0x1000007L: y = 72; break;
+ case 0x1000008L: y = 39; break;
+ default: y=0;
+ }
+ return y;
+}
+
+unsigned char __attribute__((noinline,noclone))
+foo4 (char x)
+{
+ switch (x)
+ {
+ case 0x100000001LL: y = 7; break;
+ case 0x100000002LL: y = 4; break;
+ case 0x100000003LL: y = 8; break;
+ case 0x100000004LL: y = 21; break;
+ case 0x100000005LL: y = 65; break;
+ case 0x100000006LL: y = 27; break;
+ case 0x100000007LL: y = 72; break;
+ case 0x100000008LL: y = 39; break;
+ default: y=0;
+ }
+ return y;
+}
+
+unsigned char __attribute__((noinline,noclone))
+foo5 (int x)
+{
+ switch (x)
+ {
+ case (char)0x11: y = 7; break;
+ case (char)0x12: y = 4; break;
+ case (char)0x13: y = 8; break;
+ case (char)0x14: y = 21; break;
+ case (char)0x15: y = 65; break;
+ case (char)0x16: y = 27; break;
+ case (char)0x17: y = 72; break;
+ case (char)0x18: y = 39; break;
+ default: y=0;
+ }
+ return y;
+}
+
+unsigned char __attribute__((noinline,noclone))
+foo6 (int x)
+{
+ switch (x)
+ {
+ case 0x101: y = 7; break;
+ case 0x102: y = 4; break;
+ case 0x103: y = 8; break;
+ case 0x104: y = 21; break;
+ case 0x105: y = 65; break;
+ case 0x106: y = 27; break;
+ case 0x107: y = 72; break;
+ case 0x108: y = 39; break;
+ default: y=0;
+ }
+ return y;
+}
+
+unsigned char __attribute__((noinline,noclone))
+foo7 (int x)
+{
+ switch (x)
+ {
+ case 0x1000001L: y = 7; break;
+ case 0x1000002L: y = 4; break;
+ case 0x1000003L: y = 8; break;
+ case 0x1000004L: y = 21; break;
+ case 0x1000005L: y = 65; break;
+ case 0x1000006L: y = 27; break;
+ case 0x1000007L: y = 72; break;
+ case 0x1000008L: y = 39; break;
+ default: y=0;
+ }
+ return y;
+}
+
+unsigned char __attribute__((noinline,noclone))
+foo8 (int x)
+{
+ switch (x)
+ {
+ case 0x100000001LL: y = 7; break;
+ case 0x100000002LL: y = 4; break;
+ case 0x100000003LL: y = 8; break;
+ case 0x100000004LL: y = 21; break;
+ case 0x100000005LL: y = 65; break;
+ case 0x100000006LL: y = 27; break;
+ case 0x100000007LL: y = 72; break;
+ case 0x100000008LL: y = 39; break;
+ default: y=0;
+ }
+ return y;
+}
+
+unsigned char __attribute__((noinline,noclone))
+foo9 (long x)
+{
+ switch (x)
+ {
+ case (char)0x11: y = 7; break;
+ case (char)0x12: y = 4; break;
+ case (char)0x13: y = 8; break;
+ case (char)0x14: y = 21; break;
+ case (char)0x15: y = 65; break;
+ case (char)0x16: y = 27; break;
+ case (char)0x17: y = 72; break;
+ case (char)0x18: y = 39; break;
+ default: y=0;
+ }
+ return y;
+}
+
+unsigned char __attribute__((noinline,noclone))
+foo10 (unsigned long x)
+{
+ switch (x)
+ {
+ case 0x100: y = 39; break;
+ case 0x101: y = 7; break;
+ case 0x102: y = 4; break;
+ case 0x103: y = 8; break;
+ case 0x104: y = 21; break;
+ case 0x105: y = 65; break;
+ case 0x106: y = 27; break;
+ case 0x107: y = 72; break;
+ default: y=0;
+ }
+ return y;
+}
+
+unsigned char __attribute__((noinline,noclone))
+foo11 (long x)
+{
+ switch (x)
+ {
+ case 0x1000001L: y = 7; break;
+ case 0x1000002L: y = 4; break;
+ case 0x1000003L: y = 8; break;
+ case 0x1000004L: y = 21; break;
+ case 0x1000005L: y = 65; break;
+ case 0x1000006L: y = 27; break;
+ case 0x1000007L: y = 72; break;
+ case 0x1000008L: y = 39; break;
+ default: y=0;
+ }
+ return y;
+}
+
+unsigned char __attribute__((noinline,noclone))
+foo12 (long x)
+{
+ switch (x)
+ {
+ case 0x100000001LL: y = 7; break;
+ case 0x100000002LL: y = 4; break;
+ case 0x100000003LL: y = 8; break;
+ case 0x100000004LL: y = 21; break;
+ case 0x100000005LL: y = 65; break;
+ case 0x100000006LL: y = 27; break;
+ case 0x100000007LL: y = 72; break;
+ case 0x100000008LL: y = 39; break;
+ default: y=0;
+ }
+ return y;
+}
+
+unsigned char __attribute__((noinline,noclone))
+foo13 (long long x)
+{
+ switch (x)
+ {
+ case (char)0x11: y = 7; break;
+ case (char)0x12: y = 4; break;
+ case (char)0x13: y = 8; break;
+ case (char)0x14: y = 21; break;
+ case (char)0x15: y = 65; break;
+ case (char)0x16: y = 27; break;
+ case (char)0x17: y = 72; break;
+ case (char)0x18: y = 39; break;
+ default: y=0;
+ }
+ return y;
+}
+
+unsigned char __attribute__((noinline,noclone))
+foo14 (long long x)
+{
+ switch (x)
+ {
+ case 0x101: y = 7; break;
+ case 0x102: y = 4; break;
+ case 0x103: y = 8; break;
+ case 0x104: y = 21; break;
+ case 0x105: y = 65; break;
+ case 0x106: y = 27; break;
+ case 0x107: y = 72; break;
+ case 0x108: y = 39; break;
+ default: y=0;
+ }
+ return y;
+}
+
+unsigned char __attribute__((noinline,noclone))
+foo15 (long long x)
+{
+ switch (x)
+ {
+ case 0x1000001L: y = 7; break;
+ case 0x1000002L: y = 4; break;
+ case 0x1000003L: y = 8; break;
+ case 0x1000004L: y = 21; break;
+ case 0x1000005L: y = 65; break;
+ case 0x1000006L: y = 27; break;
+ case 0x1000007L: y = 72; break;
+ case 0x1000008L: y = 39; break;
+ default: y=0;
+ }
+ return y;
+}
+
+
+unsigned char __attribute__((noinline,noclone))
+foo16 (long long x)
+{
+ switch (x)
+ {
+ case 0x100000001LL: y = 7; break;
+ case 0x100000002LL: y = 4; break;
+ case 0x100000003LL: y = 8; break;
+ case 0x100000004LL: y = 21; break;
+ case 0x100000005LL: y = 65; break;
+ case 0x100000006LL: y = 27; break;
+ case 0x100000007LL: y = 72; break;
+ case 0x100000008LL: y = 39; break;
+ default: y=0;
+ }
+ return y;
+}
+
+int main ()
+{
+ if (foo1 (0x13) != 8)
+ abort();
+
+ if (foo2 (0x06) != 27)
+ abort();
+
+ if (foo3 (0x02) != 4)
+ abort();
+
+ if (foo4 (0x01) != 7)
+ abort();
+
+ if (foo5 (0x15) != 65)
+ abort();
+
+ if (foo6 (0x103) != 8)
+ abort();
+
+ if (foo7 (0x04) != 21)
+ abort();
+
+ if (foo8 (0x07) != 72)
+ abort();
+
+ if (foo9 (0x10000011L) != 0)
+ abort();
+
+ if (foo10 (0x1000105L) != 0)
+ abort();
+
+ if (foo11 (0x1000008L) != 39)
+ abort();
+
+ if (foo12 (0x1000004L) != 0)
+ abort();
+
+ if (foo13 (0x109LL) != 0)
+ abort();
+
+ if (foo14 (0x108LL) != 39)
+ abort();
+
+ if (foo15 (0x1000001LL) != 7)
+ abort();
+
+ if (foo16 (0x100000004LL) != 21)
+ abort();
+
+ return 0;
+}
===================================================================
@@ -0,0 +1,46 @@
+/* { dg-do compile } */
+/* { dg-options "-dp -w -Os -fno-tree-switch-conversion" } */
+
+#define MK_FUN(NAME, TYP, V) \
+ unsigned char __attribute__((noinline,noclone)) \
+ select_## NAME (TYP x, unsigned char y) \
+ { \
+ switch (x) \
+ { \
+ case V + 0: return 0 + y; \
+ case V + 1: return 1; \
+ case V + 2: return 2 + y; \
+ case V + 3: return 3; \
+ case V + 4: return 4 + y; \
+ case V + 5: return 5; \
+ case V + 6: return 6 + y; \
+ case V + 7: return 7; \
+ case V + 8: return 8 + y; \
+ case V + 9: return 9; \
+ case V + 10: return 10 + y; \
+ case V + 11: return 11; \
+ case V + 12: return 12 + y; \
+ case V + 13: return 13; \
+ case V + 14: return 14 + y; \
+ case V + 15: return 15; \
+ } \
+ return x; \
+ }
+
+MK_FUN (0_s8, signed char, 0)
+MK_FUN (0_u8, unsigned char, 0)
+MK_FUN (0_s16, signed int, 0)
+MK_FUN (0_u16, unsigned int, 0)
+
+MK_FUN (m4_s8, signed char, -4)
+MK_FUN (m4_u8, unsigned char, -4)
+MK_FUN (m4_s16, signed int, -4)
+MK_FUN (m4_u16, unsigned int, -4)
+
+MK_FUN (4_s8, signed char, 4)
+MK_FUN (4_u8, unsigned char, 4)
+MK_FUN (4_s16, signed int, 4)
+MK_FUN (4_u16, unsigned int, 4)
+
+/* { dg-final { scan-assembler-not "extendqisi" } } */
+/* { dg-final { scan-assembler-not "extendhisi" } } */
===================================================================
@@ -0,0 +1,80 @@
+/* { dg-do run } */
+/* { dg-options "-Os -fno-tree-switch-conversion" } */
+
+#define MK_FUN(NAME, TYP, V) \
+ static __inline__ __attribute__((always_inline)) \
+ unsigned char value_## NAME (TYP x, unsigned char y) \
+ { \
+ switch (x) \
+ { \
+ case V + 0: return 0 + y; \
+ case V + 1: return 1; \
+ case V + 2: return 2 + y; \
+ case V + 3: return 3; \
+ case V + 4: return 4 + y; \
+ case V + 5: return 5; \
+ case V + 6: return 6 + y; \
+ case V + 7: return 7; \
+ case V + 8: return 8 + y; \
+ case V + 9: return 9; \
+ case V + 10: return 10 + y; \
+ case V + 11: return 11; \
+ case V + 12: return 12 + y; \
+ case V + 13: return 13; \
+ case V + 14: return 14 + y; \
+ case V + 15: return 15; \
+ } \
+ return x; \
+ } \
+ \
+ __attribute__((noinline,noclone)) \
+ unsigned char select_## NAME (TYP x, unsigned char y) \
+ { \
+ return value_## NAME (x, y); \
+ } \
+ \
+ static __inline__ __attribute__((always_inline)) \
+ void test1_## NAME (TYP x) \
+ { \
+ if (select_## NAME (x, 0) != value_## NAME (x, 0)) \
+ __builtin_abort(); \
+ } \
+ \
+ void test_## NAME (void) \
+ { \
+ test1_## NAME (V); \
+ test1_## NAME (V - 1); \
+ test1_## NAME (V + 15); \
+ test1_## NAME (V + 16); \
+ }
+
+MK_FUN (0_s8, signed char, 0)
+MK_FUN (0_u8, unsigned char, 0)
+MK_FUN (0_s16, signed int, 0)
+MK_FUN (0_u16, unsigned int, 0)
+
+MK_FUN (m4_s8, signed char, -4)
+MK_FUN (m4_s16, signed int, -4)
+
+MK_FUN (4_s8, signed char, 4)
+MK_FUN (4_u8, unsigned char, 4)
+MK_FUN (4_s16, signed int, 4)
+MK_FUN (4_u16, unsigned int, 4)
+
+int main (void)
+{
+ test_0_s8();
+ test_0_u8();
+ test_0_s16();
+ test_0_u16();
+
+ test_m4_s8();
+ test_m4_s16();
+
+ test_4_s8();
+ test_4_u8();
+ test_4_s16();
+ test_4_u16();
+
+ return 0;
+}
===================================================================
@@ -0,0 +1,30 @@
+/* { dg-do run } */
+/* { dg-options "-Os -fno-tree-switch-conversion" } */
+
+#include "exit-abort.h"
+
+volatile unsigned char y;
+
+__attribute__((noinline,noclone))
+unsigned char foo (unsigned long x)
+{
+ switch (x)
+ {
+ case 0: y = 67; break;
+ case 1: y = 20; break;
+ case 2: y = 109; break;
+ case 3: y = 33; break;
+ case 4: y = 44; break;
+ case 5: y = 37; break;
+ case 6: y = 10; break;
+ case 7: y = 98; break;
+ }
+ return y;
+}
+
+int main (void)
+{
+ if (0 != foo (7L + 0x10000L))
+ abort();
+ return 0;
+}
===================================================================
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-Os -fno-tree-switch-conversion" } */
+
+unsigned char foo (long long x)
+{
+ unsigned char y = 0;
+ switch (x)
+ {
+ case 0: y = 67; break;
+ case 1: y = 20; break;
+ case 2: y = 109; break;
+ case 3: y = 33; break;
+ case 4: y = 44; break;
+ case 5: y = 37; break;
+ case 6: y = 10; break;
+ case 7: y = 11; break;
+ }
+ return y;
+}