From 15b84087fc146ea5fcc518deff19c118525a9be6 Mon Sep 17 00:00:00 2001 From: gjl Date: Wed, 15 Feb 2012 11:07:13 +0000 Subject: [PATCH] gcc/testsuite/ * gcc.target/avr/torture/builtin_insert_bits-1.c: New test. * gcc.target/avr/torture/builtin_insert_bits-2.c: New test. gcc/ * doc/extend.texi (AVR Built-in Functions): Remove doc for __builtin_avr_map8, __builtin_avr_map16. Document __builtin_avr_insert_bits. * config/avr/avr.md (map_bitsqi, map_bitshi): Remove. (insert_bits): New insn. (adjust_len.map_bits): Rename to insert_bits. (UNSPEC_MAP_BITS): Rename to UNSPEC_INSERT_BITS. * avr-protos.h (avr_out_map_bits): Remove. (avr_out_insert_bits, avr_has_nibble_0xf): New. * config/avr/constraints.md (Cxf,C0f): New. * config/avr/avr.c (avr_cpu_cpp_builtins): Remove built-in defines __BUILTIN_AVR_MAP8, __BUILTIN_AVR_MAP16. New built-in define __BUILTIN_AVR_INSERT_BITS. * config/avr/avr.c (TARGET_FOLD_BUILTIN): New define. (enum avr_builtin_id): Add AVR_BUILTIN_INSERT_BITS. (avr_move_bits): Rewrite. (avr_fold_builtin, avr_map_metric, avr_map_decompose): New static functions. (avr_map_op_t): New typedef. (avr_map_op): New static variable. (avr_out_insert_bits, avr_has_nibble_0xf): New functions. (adjust_insn_length): Handle ADJUST_LEN_INSERT_BITS. (avr_init_builtins): Add definition for __builtin_avr_insert_bits. (bdesc_3arg, avr_expand_triop_builtin): New. (avr_expand_builtin): Use them. And handle AVR_BUILTIN_INSERT_BITS. (avr_revert_map, avr_swap_map, avr_id_map, avr_sig_map): Remove. (avr_map_hamming_byte, avr_map_hamming_nonstrict): Remove. (avr_map_equal_p, avr_map_sig_p): Remove. (avr_out_swap_bits, avr_out_revert_bits, avr_out_map_bits): Remove. (bdesc_2arg): Remove AVR_BUILTIN_MAP8, AVR_BUILTIN_MAP16. (adjust_insn_length): Remove handling for ADJUST_LEN_MAP_BITS. (enum avr_builtin_id): Remove AVR_BUILTIN_MAP8, AVR_BUILTIN_MAP16. (avr_init_builtins): Remove __builtin_avr_map8, __builtin_avr_map16. (avr_expand_builtin): Remove AVR_BUILTIN_MAP8, AVR_BUILTIN_MAP16. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@184264 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 38 ++ gcc/config/avr/avr-c.c | 3 +- gcc/config/avr/avr-protos.h | 3 +- gcc/config/avr/avr.c | 712 ++++++++++++++------- gcc/config/avr/avr.md | 32 +- gcc/config/avr/constraints.md | 10 + gcc/doc/extend.texi | 56 +- gcc/testsuite/ChangeLog | 5 + .../gcc.target/avr/torture/builtin_insert_bits-1.c | 97 +++ .../gcc.target/avr/torture/builtin_insert_bits-2.c | 94 +++ 10 files changed, 760 insertions(+), 290 deletions(-) create mode 100644 gcc/testsuite/gcc.target/avr/torture/builtin_insert_bits-1.c create mode 100644 gcc/testsuite/gcc.target/avr/torture/builtin_insert_bits-2.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 1bfeffc980a..1d88845e56e 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,41 @@ +2012-02-15 Georg-Johann Lay + + * doc/extend.texi (AVR Built-in Functions): Remove doc for + __builtin_avr_map8, __builtin_avr_map16. + Document __builtin_avr_insert_bits. + + * config/avr/avr.md (map_bitsqi, map_bitshi): Remove. + (insert_bits): New insn. + (adjust_len.map_bits): Rename to insert_bits. + (UNSPEC_MAP_BITS): Rename to UNSPEC_INSERT_BITS. + * avr-protos.h (avr_out_map_bits): Remove. + (avr_out_insert_bits, avr_has_nibble_0xf): New. + * config/avr/constraints.md (Cxf,C0f): New. + * config/avr/avr.c (avr_cpu_cpp_builtins): Remove built-in + defines __BUILTIN_AVR_MAP8, __BUILTIN_AVR_MAP16. + New built-in define __BUILTIN_AVR_INSERT_BITS. + * config/avr/avr.c (TARGET_FOLD_BUILTIN): New define. + (enum avr_builtin_id): Add AVR_BUILTIN_INSERT_BITS. + (avr_move_bits): Rewrite. + (avr_fold_builtin, avr_map_metric, avr_map_decompose): New static + functions. + (avr_map_op_t): New typedef. + (avr_map_op): New static variable. + (avr_out_insert_bits, avr_has_nibble_0xf): New functions. + (adjust_insn_length): Handle ADJUST_LEN_INSERT_BITS. + (avr_init_builtins): Add definition for __builtin_avr_insert_bits. + (bdesc_3arg, avr_expand_triop_builtin): New. + (avr_expand_builtin): Use them. And handle AVR_BUILTIN_INSERT_BITS. + (avr_revert_map, avr_swap_map, avr_id_map, avr_sig_map): Remove. + (avr_map_hamming_byte, avr_map_hamming_nonstrict): Remove. + (avr_map_equal_p, avr_map_sig_p): Remove. + (avr_out_swap_bits, avr_out_revert_bits, avr_out_map_bits): Remove. + (bdesc_2arg): Remove AVR_BUILTIN_MAP8, AVR_BUILTIN_MAP16. + (adjust_insn_length): Remove handling for ADJUST_LEN_MAP_BITS. + (enum avr_builtin_id): Remove AVR_BUILTIN_MAP8, AVR_BUILTIN_MAP16. + (avr_init_builtins): Remove __builtin_avr_map8, __builtin_avr_map16. + (avr_expand_builtin): Remove AVR_BUILTIN_MAP8, AVR_BUILTIN_MAP16. + 2012-02-14 Bernd Schmidt * config/c6x/c6x.md (reserve_cycles): New attribute. diff --git a/gcc/config/avr/avr-c.c b/gcc/config/avr/avr-c.c index a6e43de9b1a..55aed30412a 100644 --- a/gcc/config/avr/avr-c.c +++ b/gcc/config/avr/avr-c.c @@ -163,8 +163,7 @@ avr_cpu_cpp_builtins (struct cpp_reader *pfile) cpp_define (pfile, "__BUILTIN_AVR_WDR"); cpp_define (pfile, "__BUILTIN_AVR_SLEEP"); cpp_define (pfile, "__BUILTIN_AVR_SWAP"); - cpp_define (pfile, "__BUILTIN_AVR_MAP8"); - cpp_define (pfile, "__BUILTIN_AVR_MAP16"); + cpp_define (pfile, "__BUILTIN_AVR_INSERT_BITS"); cpp_define (pfile, "__BUILTIN_AVR_DELAY_CYCLES"); cpp_define (pfile, "__BUILTIN_AVR_FMUL"); diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h index 2575fc497cd..fa8c421addc 100644 --- a/gcc/config/avr/avr-protos.h +++ b/gcc/config/avr/avr-protos.h @@ -94,8 +94,9 @@ extern const char* avr_out_plus64 (rtx, int*); extern const char* avr_out_addto_sp (rtx*, int*); extern const char* avr_out_xload (rtx, rtx*, int*); extern const char* avr_out_movmem (rtx, rtx*, int*); -extern const char* avr_out_map_bits (rtx, rtx*, int*); +extern const char* avr_out_insert_bits (rtx*, int*); extern bool avr_popcount_each_byte (rtx, int, int); +extern bool avr_has_nibble_0xf (rtx); extern int extra_constraint_Q (rtx x); extern int adjust_insn_length (rtx insn, int len); diff --git a/gcc/config/avr/avr.c b/gcc/config/avr/avr.c index 5b71b366ffb..f564db79856 100644 --- a/gcc/config/avr/avr.c +++ b/gcc/config/avr/avr.c @@ -305,6 +305,9 @@ bool avr_need_copy_data_p = false; #undef TARGET_EXPAND_BUILTIN #define TARGET_EXPAND_BUILTIN avr_expand_builtin +#undef TARGET_FOLD_BUILTIN +#define TARGET_FOLD_BUILTIN avr_fold_builtin + #undef TARGET_ASM_FUNCTION_RODATA_SECTION #define TARGET_ASM_FUNCTION_RODATA_SECTION avr_asm_function_rodata_section @@ -6465,12 +6468,12 @@ adjust_insn_length (rtx insn, int len) case ADJUST_LEN_CALL: len = AVR_HAVE_JMP_CALL ? 2 : 1; break; - case ADJUST_LEN_MAP_BITS: avr_out_map_bits (insn, op, &len); break; + case ADJUST_LEN_INSERT_BITS: avr_out_insert_bits (op, &len); break; default: gcc_unreachable(); } - + return len; } @@ -9945,193 +9948,220 @@ avr_map (double_int f, int x) } -/* Return the map R that reverses the bits of byte B. +/* Return some metrics of map A. */ - R(0) = (0 7) o (1 6) o (2 5) o (3 4) - R(1) = (8 15) o (9 14) o (10 13) o (11 12) - - Notice that R o R = id. */ +enum + { + /* Number of fixed points in { 0 ... 7 } */ + MAP_FIXED_0_7, -static double_int -avr_revert_map (int b) + /* Size of preimage of non-fixed points in { 0 ... 7 } */ + MAP_NONFIXED_0_7, + + /* Mask representing the fixed points in { 0 ... 7 } */ + MAP_MASK_FIXED_0_7, + + /* Size of the preimage of { 0 ... 7 } */ + MAP_PREIMAGE_0_7, + + /* Mask that represents the preimage of { f } */ + MAP_MASK_PREIMAGE_F + }; + +static unsigned +avr_map_metric (double_int a, int mode) { - int i; - double_int r = double_int_zero; + unsigned i, metric = 0; - for (i = 16-1; i >= 0; i--) - r = avr_double_int_push_digit (r, 16, i >> 3 == b ? i ^ 7 : i); + for (i = 0; i < 8; i++) + { + unsigned ai = avr_map (a, i); - return r; + if (mode == MAP_FIXED_0_7) + metric += ai == i; + else if (mode == MAP_NONFIXED_0_7) + metric += ai < 8 && ai != i; + else if (mode == MAP_MASK_FIXED_0_7) + metric |= ((unsigned) (ai == i)) << i; + else if (mode == MAP_PREIMAGE_0_7) + metric += ai < 8; + else if (mode == MAP_MASK_PREIMAGE_F) + metric |= ((unsigned) (ai == 0xf)) << i; + else + gcc_unreachable(); + } + + return metric; } -/* Return the map R that swaps bit-chunks of size SIZE in byte B. +/* Return true if IVAL has a 0xf in its hexadecimal representation + and false, otherwise. Only nibbles 0..7 are taken into account. + Used as constraint helper for C0f and Cxf. */ - R(1,0) = (0 1) o (2 3) o (4 5) o (6 7) - R(1,1) = (8 9) o (10 11) o (12 13) o (14 15) +bool +avr_has_nibble_0xf (rtx ival) +{ + return 0 != avr_map_metric (rtx_to_double_int (ival), MAP_MASK_PREIMAGE_F); +} - R(4,0) = (0 4) o (1 5) o (2 6) o (3 7) - R(4,1) = (8 12) o (9 13) o (10 14) o (11 15) - Notice that R o R = id. */ +/* We have a set of bits that are mapped by a function F. + Try to decompose F by means of a second function G so that -static double_int -avr_swap_map (int size, int b) -{ - int i; - double_int r = double_int_zero; + F = F o G^-1 o G - for (i = 16-1; i >= 0; i--) - r = avr_double_int_push_digit (r, 16, i ^ (i >> 3 == b ? size : 0)); + and - return r; -} + cost (F o G^-1) + cost (G) < cost (F) + Example: Suppose builtin insert_bits supplies us with the map + F = 0x3210ffff. Instead of doing 4 bit insertions to get the high + nibble of the result, we can just as well rotate the bits before inserting + them and use the map 0x7654ffff which is cheaper than the original map. + For this example G = G^-1 = 0x32107654 and F o G^-1 = 0x7654ffff. */ + +typedef struct +{ + /* tree code of binary function G */ + enum tree_code code; -/* Return Identity. */ + /* The constant second argument of G */ + int arg; -static double_int -avr_id_map (void) -{ - int i; - double_int r = double_int_zero; + /* G^-1, the inverse of G (*, arg) */ + unsigned ginv; - for (i = 16-1; i >= 0; i--) - r = avr_double_int_push_digit (r, 16, i); + /* The cost of appplying G (*, arg) */ + int cost; - return r; -} + /* The composition F o G^-1 (*, arg) for some function F */ + double_int map; + /* For debug purpose only */ + const char *str; +} avr_map_op_t; -enum +static const avr_map_op_t avr_map_op[] = { - SIG_ID = 0, - /* for QI and HI */ - SIG_ROL = 0xf, - SIG_REVERT_0 = 1 << 4, - SIG_SWAP1_0 = 1 << 5, - /* HI only */ - SIG_REVERT_1 = 1 << 6, - SIG_SWAP1_1 = 1 << 7, - SIG_SWAP4_0 = 1 << 8, - SIG_SWAP4_1 = 1 << 9 + { LROTATE_EXPR, 0, 0x76543210, 0, { 0, 0 }, "id" }, + { LROTATE_EXPR, 1, 0x07654321, 2, { 0, 0 }, "<<<" }, + { LROTATE_EXPR, 2, 0x10765432, 4, { 0, 0 }, "<<<" }, + { LROTATE_EXPR, 3, 0x21076543, 4, { 0, 0 }, "<<<" }, + { LROTATE_EXPR, 4, 0x32107654, 1, { 0, 0 }, "<<<" }, + { LROTATE_EXPR, 5, 0x43210765, 3, { 0, 0 }, "<<<" }, + { LROTATE_EXPR, 6, 0x54321076, 5, { 0, 0 }, "<<<" }, + { LROTATE_EXPR, 7, 0x65432107, 3, { 0, 0 }, "<<<" }, + { RSHIFT_EXPR, 1, 0x6543210c, 1, { 0, 0 }, ">>" }, + { RSHIFT_EXPR, 1, 0x7543210c, 1, { 0, 0 }, ">>" }, + { RSHIFT_EXPR, 2, 0x543210cc, 2, { 0, 0 }, ">>" }, + { RSHIFT_EXPR, 2, 0x643210cc, 2, { 0, 0 }, ">>" }, + { RSHIFT_EXPR, 2, 0x743210cc, 2, { 0, 0 }, ">>" }, + { LSHIFT_EXPR, 1, 0xc7654321, 1, { 0, 0 }, "<<" }, + { LSHIFT_EXPR, 2, 0xcc765432, 2, { 0, 0 }, "<<" } }; -/* Return basic map with signature SIG. */ - -static double_int -avr_sig_map (int n ATTRIBUTE_UNUSED, int sig) -{ - if (sig == SIG_ID) return avr_id_map (); - else if (sig == SIG_REVERT_0) return avr_revert_map (0); - else if (sig == SIG_REVERT_1) return avr_revert_map (1); - else if (sig == SIG_SWAP1_0) return avr_swap_map (1, 0); - else if (sig == SIG_SWAP1_1) return avr_swap_map (1, 1); - else if (sig == SIG_SWAP4_0) return avr_swap_map (4, 0); - else if (sig == SIG_SWAP4_1) return avr_swap_map (4, 1); - else - gcc_unreachable(); -} - - -/* Return the Hamming distance between the B-th byte of A and C. */ - -static bool -avr_map_hamming_byte (int n, int b, double_int a, double_int c, bool strict) +/* Try to decompose F as F = (F o G^-1) o G as described above. + The result is a struct representing F o G^-1 and G. + If result.cost < 0 then such a decomposition does not exist. */ + +static avr_map_op_t +avr_map_decompose (double_int f, const avr_map_op_t *g, bool val_const_p) { - int i, hamming = 0; - - for (i = 8*b; i < n && i < 8*b + 8; i++) - { - int ai = avr_map (a, i); - int ci = avr_map (c, i); + int i; + bool val_used_p = 0 != avr_map_metric (f, MAP_MASK_PREIMAGE_F); + avr_map_op_t f_ginv = *g; + double_int ginv = uhwi_to_double_int (g->ginv); - hamming += ai != ci && (strict || (ai < n && ci < n)); - } + f_ginv.cost = -1; - return hamming; -} - - -/* Return the non-strict Hamming distance between A and B. */ - -#define avr_map_hamming_nonstrict(N,A,B) \ - (+ avr_map_hamming_byte (N, 0, A, B, false) \ - + avr_map_hamming_byte (N, 1, A, B, false)) + /* Step 1: Computing F o G^-1 */ + for (i = 7; i >= 0; i--) + { + int x = avr_map (f, i); + + if (x <= 7) + { + x = avr_map (ginv, x); -/* Return TRUE iff A and B represent the same mapping. */ - -#define avr_map_equal_p(N,A,B) (0 == avr_map_hamming_nonstrict (N, A, B)) + /* The bit is no element of the image of G: no avail (cost = -1) */ + + if (x > 7) + return f_ginv; + } + + f_ginv.map = avr_double_int_push_digit (f_ginv.map, 16, x); + } + /* Step 2: Compute the cost of the operations. + The overall cost of doing an operation prior to the insertion is + the cost of the insertion plus the cost of the operation. */ -/* Return TRUE iff A is a map of signature S. Notice that there is no - 1:1 correspondance between maps and signatures and thus this is - only supported for basic signatures recognized by avr_sig_map(). */ + /* Step 2a: Compute cost of F o G^-1 */ -#define avr_map_sig_p(N,A,S) avr_map_equal_p (N, A, avr_sig_map (N, S)) + if (0 == avr_map_metric (f_ginv.map, MAP_NONFIXED_0_7)) + { + /* The mapping consists only of fixed points and can be folded + to AND/OR logic in the remainder. Reasonable cost is 3. */ + f_ginv.cost = 2 + (val_used_p && !val_const_p); + } + else + { + rtx xop[4]; -/* Swap odd/even bits of ld-reg %0: %0 = bit-swap (%0) */ + /* Get the cost of the insn by calling the output worker with some + fake values. Mimic effect of reloading xop[3]: Unused operands + are mapped to 0 and used operands are reloaded to xop[0]. */ -static const char* -avr_out_swap_bits (rtx *xop, int *plen) -{ - xop[1] = tmp_reg_rtx; + xop[0] = all_regs_rtx[24]; + xop[1] = gen_int_mode (double_int_to_uhwi (f_ginv.map), SImode); + xop[2] = all_regs_rtx[25]; + xop[3] = val_used_p ? xop[0] : const0_rtx; - return avr_asm_len ("mov %1,%0" CR_TAB - "andi %0,0xaa" CR_TAB - "eor %1,%0" CR_TAB - "lsr %0" CR_TAB - "lsl %1" CR_TAB - "or %0,%1", xop, plen, 6); -} + avr_out_insert_bits (xop, &f_ginv.cost); + + f_ginv.cost += val_const_p && val_used_p ? 1 : 0; + } + + /* Step 2b: Add cost of G */ -/* Revert bit order: %0 = Revert (%1) with %0 != %1 and clobber %1 */ + f_ginv.cost += g->cost; -static const char* -avr_out_revert_bits (rtx *xop, int *plen) -{ - return avr_asm_len ("inc __zero_reg__" "\n" - "0:\tror %1" CR_TAB - "rol %0" CR_TAB - "lsl __zero_reg__" CR_TAB - "brne 0b", xop, plen, 5); + if (avr_log.builtin) + avr_edump (" %s%d=%d", g->str, g->arg, f_ginv.cost); + + return f_ginv; } -/* If OUT_P = true: Output BST/BLD instruction according to MAP. - If OUT_P = false: Just dry-run and fix XOP[1] to resolve - early-clobber conflicts if XOP[0] = XOP[1]. */ +/* Insert bits from XOP[1] into XOP[0] according to MAP. + XOP[0] and XOP[1] don't overlap. + If FIXP_P = true: Move all bits according to MAP using BLD/BST sequences. + If FIXP_P = false: Just move the bit if its position in the destination + is different to its source position. */ static void -avr_move_bits (rtx *xop, double_int map, int n_bits, bool out_p, int *plen) +avr_move_bits (rtx *xop, double_int map, bool fixp_p, int *plen) { - int bit_dest, b, clobber = 0; + int bit_dest, b; /* T-flag contains this bit of the source, i.e. of XOP[1] */ int t_bit_src = -1; - if (!optimize && !out_p) - { - avr_asm_len ("mov __tmp_reg__,%1", xop, plen, 1); - xop[1] = tmp_reg_rtx; - return; - } - /* We order the operations according to the requested source bit b. */ - for (b = 0; b < n_bits; b++) - for (bit_dest = 0; bit_dest < n_bits; bit_dest++) + for (b = 0; b < 8; b++) + for (bit_dest = 0; bit_dest < 8; bit_dest++) { int bit_src = avr_map (map, bit_dest); if (b != bit_src - /* Same position: No need to copy as the caller did MOV. */ - || bit_dest == bit_src - /* Accessing bits 8..f for 8-bit version is void. */ - || bit_src >= n_bits) + || bit_src >= 8 + /* Same position: No need to copy as requested by FIXP_P. */ + || (bit_dest == bit_src && !fixp_p)) continue; if (t_bit_src != bit_src) @@ -10140,121 +10170,103 @@ avr_move_bits (rtx *xop, double_int map, int n_bits, bool out_p, int *plen) t_bit_src = bit_src; - if (out_p) - { - xop[2] = GEN_INT (bit_src); - avr_asm_len ("bst %T1%T2", xop, plen, 1); - } - else if (clobber & (1 << bit_src)) - { - /* Bit to be read was written already: Backup input - to resolve early-clobber conflict. */ - - avr_asm_len ("mov __tmp_reg__,%1", xop, plen, 1); - xop[1] = tmp_reg_rtx; - return; - } + xop[3] = GEN_INT (bit_src); + avr_asm_len ("bst %T1%T3", xop, plen, 1); } /* Load destination bit with T. */ - if (out_p) - { - xop[2] = GEN_INT (bit_dest); - avr_asm_len ("bld %T0%T2", xop, plen, 1); - } - - clobber |= 1 << bit_dest; + xop[3] = GEN_INT (bit_dest); + avr_asm_len ("bld %T0%T3", xop, plen, 1); } } -/* Print assembler code for `map_bitsqi' and `map_bitshi'. */ +/* PLEN == 0: Print assembler code for `insert_bits'. + PLEN != 0: Compute code length in bytes. + + OP[0]: Result + OP[1]: The mapping composed of nibbles. If nibble no. N is + 0: Bit N of result is copied from bit OP[2].0 + ... ... + 7: Bit N of result is copied from bit OP[2].7 + 0xf: Bit N of result is copied from bit OP[3].N + OP[2]: Bits to be inserted + OP[3]: Target value */ const char* -avr_out_map_bits (rtx insn, rtx *operands, int *plen) +avr_out_insert_bits (rtx *op, int *plen) { - bool copy_0, copy_1; - int n_bits = GET_MODE_BITSIZE (GET_MODE (operands[0])); - double_int map = rtx_to_double_int (operands[1]); - rtx xop[3]; + double_int map = rtx_to_double_int (op[1]); + unsigned mask_fixed; + bool fixp_p = true; + rtx xop[4]; - xop[0] = operands[0]; - xop[1] = operands[2]; + xop[0] = op[0]; + xop[1] = op[2]; + xop[2] = op[3]; + gcc_assert (REG_P (xop[2]) || CONST_INT_P (xop[2])); + if (plen) *plen = 0; else if (flag_print_asm_name) - avr_fdump (asm_out_file, ASM_COMMENT_START "%X\n", map); + fprintf (asm_out_file, + ASM_COMMENT_START "map = 0x%08" HOST_LONG_FORMAT "x\n", + double_int_to_uhwi (map) & GET_MODE_MASK (SImode)); - switch (n_bits) - { - default: - gcc_unreachable(); + /* If MAP has fixed points it might be better to initialize the result + with the bits to be inserted instead of moving all bits by hand. */ - case 8: - if (avr_map_sig_p (n_bits, map, SIG_SWAP1_0)) - { - return avr_out_swap_bits (xop, plen); - } - else if (avr_map_sig_p (n_bits, map, SIG_REVERT_0)) - { - if (REGNO (xop[0]) == REGNO (xop[1]) - || !reg_unused_after (insn, xop[1])) - { - avr_asm_len ("mov __tmp_reg__,%1", xop, plen, 1); - xop[1] = tmp_reg_rtx; - } - - return avr_out_revert_bits (xop, plen); - } - - break; /* 8 */ + mask_fixed = avr_map_metric (map, MAP_MASK_FIXED_0_7); - case 16: + if (REGNO (xop[0]) == REGNO (xop[1])) + { + /* Avoid early-clobber conflicts */ - break; /* 16 */ + avr_asm_len ("mov __tmp_reg__,%1", xop, plen, 1); + xop[1] = tmp_reg_rtx; + fixp_p = false; } - /* Copy whole byte is cheaper than moving bits that stay at the same - position. Some bits in a byte stay at the same position iff the - strict Hamming distance to Identity is not 8. */ - - copy_0 = 8 != avr_map_hamming_byte (n_bits, 0, map, avr_id_map(), true); - copy_1 = 8 != avr_map_hamming_byte (n_bits, 1, map, avr_id_map(), true); - - /* Perform the move(s) just worked out. */ - - if (n_bits == 8) + if (avr_map_metric (map, MAP_MASK_PREIMAGE_F)) { - if (REGNO (xop[0]) == REGNO (xop[1])) - { - /* Fix early-clobber clashes. - Notice XOP[0] hat no eary-clobber in its constraint. */ - - avr_move_bits (xop, map, n_bits, false, plen); - } - else if (copy_0) + /* XOP[2] is used and reloaded to XOP[0] already */ + + int n_fix = 0, n_nofix = 0; + + gcc_assert (REG_P (xop[2])); + + /* Get the code size of the bit insertions; once with all bits + moved and once with fixed points omitted. */ + + avr_move_bits (xop, map, true, &n_fix); + avr_move_bits (xop, map, false, &n_nofix); + + if (fixp_p && n_fix - n_nofix > 3) { - avr_asm_len ("mov %0,%1", xop, plen, 1); + xop[3] = gen_int_mode (~mask_fixed, QImode); + + avr_asm_len ("eor %0,%1" CR_TAB + "andi %0,%3" CR_TAB + "eor %0,%1", xop, plen, 3); + fixp_p = false; } } - else if (AVR_HAVE_MOVW && copy_0 && copy_1) - { - avr_asm_len ("movw %A0,%A1", xop, plen, 1); - } else { - if (copy_0) - avr_asm_len ("mov %A0,%A1", xop, plen, 1); - - if (copy_1) - avr_asm_len ("mov %B0,%B1", xop, plen, 1); + /* XOP[2] is unused */ + + if (fixp_p && mask_fixed) + { + avr_asm_len ("mov %0,%1", xop, plen, 1); + fixp_p = false; + } } + + /* Move/insert remaining bits. */ - /* Move individual bits. */ - - avr_move_bits (xop, map, n_bits, true, plen); + avr_move_bits (xop, map, fixp_p, plen); return ""; } @@ -10270,8 +10282,7 @@ enum avr_builtin_id AVR_BUILTIN_WDR, AVR_BUILTIN_SLEEP, AVR_BUILTIN_SWAP, - AVR_BUILTIN_MAP8, - AVR_BUILTIN_MAP16, + AVR_BUILTIN_INSERT_BITS, AVR_BUILTIN_FMUL, AVR_BUILTIN_FMULS, AVR_BUILTIN_FMULSU, @@ -10328,16 +10339,11 @@ avr_init_builtins (void) long_unsigned_type_node, NULL_TREE); - tree uchar_ftype_ulong_uchar + tree uchar_ftype_ulong_uchar_uchar = build_function_type_list (unsigned_char_type_node, long_unsigned_type_node, unsigned_char_type_node, - NULL_TREE); - - tree uint_ftype_ullong_uint - = build_function_type_list (unsigned_type_node, - long_long_unsigned_type_node, - unsigned_type_node, + unsigned_char_type_node, NULL_TREE); DEF_BUILTIN ("__builtin_avr_nop", void_ftype_void, AVR_BUILTIN_NOP); @@ -10356,10 +10362,8 @@ avr_init_builtins (void) DEF_BUILTIN ("__builtin_avr_fmulsu", int_ftype_char_uchar, AVR_BUILTIN_FMULSU); - DEF_BUILTIN ("__builtin_avr_map8", uchar_ftype_ulong_uchar, - AVR_BUILTIN_MAP8); - DEF_BUILTIN ("__builtin_avr_map16", uint_ftype_ullong_uint, - AVR_BUILTIN_MAP16); + DEF_BUILTIN ("__builtin_avr_insert_bits", uchar_ftype_ulong_uchar_uchar, + AVR_BUILTIN_INSERT_BITS); avr_init_builtin_int24 (); } @@ -10384,9 +10388,14 @@ bdesc_2arg[] = { { CODE_FOR_fmul, "__builtin_avr_fmul", AVR_BUILTIN_FMUL }, { CODE_FOR_fmuls, "__builtin_avr_fmuls", AVR_BUILTIN_FMULS }, - { CODE_FOR_fmulsu, "__builtin_avr_fmulsu", AVR_BUILTIN_FMULSU }, - { CODE_FOR_map_bitsqi, "__builtin_avr_map8", AVR_BUILTIN_MAP8 }, - { CODE_FOR_map_bitshi, "__builtin_avr_map16", AVR_BUILTIN_MAP16 } + { CODE_FOR_fmulsu, "__builtin_avr_fmulsu", AVR_BUILTIN_FMULSU } + }; + +static const struct avr_builtin_description +bdesc_3arg[] = + { + { CODE_FOR_insert_bits, "__builtin_avr_insert_bits", + AVR_BUILTIN_INSERT_BITS } }; /* Subroutine of avr_expand_builtin to take care of unop insns. */ @@ -10486,6 +10495,76 @@ avr_expand_binop_builtin (enum insn_code icode, tree exp, rtx target) return target; } +/* Subroutine of avr_expand_builtin to take care of 3-operand insns. */ + +static rtx +avr_expand_triop_builtin (enum insn_code icode, tree exp, rtx target) +{ + rtx pat; + tree arg0 = CALL_EXPR_ARG (exp, 0); + tree arg1 = CALL_EXPR_ARG (exp, 1); + tree arg2 = CALL_EXPR_ARG (exp, 2); + rtx op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL); + rtx op1 = expand_expr (arg1, NULL_RTX, VOIDmode, EXPAND_NORMAL); + rtx op2 = expand_expr (arg2, NULL_RTX, VOIDmode, EXPAND_NORMAL); + enum machine_mode op0mode = GET_MODE (op0); + enum machine_mode op1mode = GET_MODE (op1); + enum machine_mode op2mode = GET_MODE (op2); + enum machine_mode tmode = insn_data[icode].operand[0].mode; + enum machine_mode mode0 = insn_data[icode].operand[1].mode; + enum machine_mode mode1 = insn_data[icode].operand[2].mode; + enum machine_mode mode2 = insn_data[icode].operand[3].mode; + + if (! target + || GET_MODE (target) != tmode + || ! (*insn_data[icode].operand[0].predicate) (target, tmode)) + { + target = gen_reg_rtx (tmode); + } + + if ((op0mode == SImode || op0mode == VOIDmode) && mode0 == HImode) + { + op0mode = HImode; + op0 = gen_lowpart (HImode, op0); + } + + if ((op1mode == SImode || op1mode == VOIDmode) && mode1 == HImode) + { + op1mode = HImode; + op1 = gen_lowpart (HImode, op1); + } + + if ((op2mode == SImode || op2mode == VOIDmode) && mode2 == HImode) + { + op2mode = HImode; + op2 = gen_lowpart (HImode, op2); + } + + /* In case the insn wants input operands in modes different from + the result, abort. */ + + gcc_assert ((op0mode == mode0 || op0mode == VOIDmode) + && (op1mode == mode1 || op1mode == VOIDmode) + && (op2mode == mode2 || op2mode == VOIDmode)); + + if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) + op0 = copy_to_mode_reg (mode0, op0); + + if (! (*insn_data[icode].operand[2].predicate) (op1, mode1)) + op1 = copy_to_mode_reg (mode1, op1); + + if (! (*insn_data[icode].operand[3].predicate) (op2, mode2)) + op2 = copy_to_mode_reg (mode2, op2); + + pat = GEN_FCN (icode) (target, op0, op1, op2); + + if (! pat) + return 0; + + emit_insn (pat); + return target; +} + /* Expand an expression EXP that calls a built-in function, with result going to TARGET if that's convenient @@ -10541,7 +10620,7 @@ avr_expand_builtin (tree exp, rtx target, return 0; } - case AVR_BUILTIN_MAP8: + case AVR_BUILTIN_INSERT_BITS: { arg0 = CALL_EXPR_ARG (exp, 0); op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL); @@ -10553,19 +10632,6 @@ avr_expand_builtin (tree exp, rtx target, return target; } } - - case AVR_BUILTIN_MAP16: - { - arg0 = CALL_EXPR_ARG (exp, 0); - op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL); - - if (!const_double_operand (op0, VOIDmode)) - { - error ("%s expects a compile time long long integer constant" - " as first argument", bname); - return target; - } - } } for (i = 0, d = bdesc_1arg; i < ARRAY_SIZE (bdesc_1arg); i++, d++) @@ -10576,9 +10642,157 @@ avr_expand_builtin (tree exp, rtx target, if (d->id == id) return avr_expand_binop_builtin (d->icode, exp, target); + for (i = 0, d = bdesc_3arg; i < ARRAY_SIZE (bdesc_3arg); i++, d++) + if (d->id == id) + return avr_expand_triop_builtin (d->icode, exp, target); + gcc_unreachable (); } + +/* Implement `TARGET_FOLD_BUILTIN'. */ + +static tree +avr_fold_builtin (tree fndecl, int n_args ATTRIBUTE_UNUSED, tree *arg, + bool ignore ATTRIBUTE_UNUSED) +{ + unsigned int fcode = DECL_FUNCTION_CODE (fndecl); + tree val_type = TREE_TYPE (TREE_TYPE (fndecl)); + + if (!optimize) + return NULL_TREE; + + switch (fcode) + { + default: + break; + + case AVR_BUILTIN_INSERT_BITS: + { + tree tbits = arg[1]; + tree tval = arg[2]; + tree tmap; + tree map_type = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fndecl))); + double_int map = tree_to_double_int (arg[0]); + bool changed = false; + unsigned i; + avr_map_op_t best_g; + + tmap = double_int_to_tree (map_type, map); + + if (TREE_CODE (tval) != INTEGER_CST + && 0 == avr_map_metric (map, MAP_MASK_PREIMAGE_F)) + { + /* There are no F in the map, i.e. 3rd operand is unused. + Replace that argument with some constant to render + respective input unused. */ + + tval = build_int_cst (val_type, 0); + changed = true; + } + + if (TREE_CODE (tbits) != INTEGER_CST + && 0 == avr_map_metric (map, MAP_PREIMAGE_0_7)) + { + /* Similar for the bits to be inserted. If they are unused, + we can just as well pass 0. */ + + tbits = build_int_cst (val_type, 0); + } + + if (TREE_CODE (tbits) == INTEGER_CST) + { + /* Inserting bits known at compile time is easy and can be + performed by AND and OR with appropriate masks. */ + + int bits = TREE_INT_CST_LOW (tbits); + int mask_ior = 0, mask_and = 0xff; + + for (i = 0; i < 8; i++) + { + int mi = avr_map (map, i); + + if (mi < 8) + { + if (bits & (1 << mi)) mask_ior |= (1 << i); + else mask_and &= ~(1 << i); + } + } + + tval = fold_build2 (BIT_IOR_EXPR, val_type, tval, + build_int_cst (val_type, mask_ior)); + return fold_build2 (BIT_AND_EXPR, val_type, tval, + build_int_cst (val_type, mask_and)); + } + + if (changed) + return build_call_expr (fndecl, 3, tmap, tbits, tval); + + /* If bits don't change their position we can use vanilla logic + to merge the two arguments. */ + + if (0 == avr_map_metric (map, MAP_NONFIXED_0_7)) + { + int mask_f = avr_map_metric (map, MAP_MASK_PREIMAGE_F); + tree tres, tmask = build_int_cst (val_type, mask_f ^ 0xff); + + tres = fold_build2 (BIT_XOR_EXPR, val_type, tbits, tval); + tres = fold_build2 (BIT_AND_EXPR, val_type, tres, tmask); + return fold_build2 (BIT_XOR_EXPR, val_type, tres, tval); + } + + /* Try to decomposing map to reduce overall cost. */ + + if (avr_log.builtin) + avr_edump ("\n%?: %X\n%?: ROL cost: ", map); + + best_g = avr_map_op[0]; + best_g.cost = 1000; + + for (i = 0; i < sizeof (avr_map_op) / sizeof (*avr_map_op); i++) + { + avr_map_op_t g + = avr_map_decompose (map, avr_map_op + i, + TREE_CODE (tval) == INTEGER_CST); + + if (g.cost >= 0 && g.cost < best_g.cost) + best_g = g; + } + + if (avr_log.builtin) + avr_edump ("\n"); + + if (best_g.arg == 0) + /* No optimization found */ + break; + + /* Apply operation G to the 2nd argument. */ + + if (avr_log.builtin) + avr_edump ("%?: using OP(%s%d, %X) cost %d\n", + best_g.str, best_g.arg, best_g.map, best_g.cost); + + /* Do right-shifts arithmetically: They copy the MSB instead of + shifting in a non-usable value (0) as with logic right-shift. */ + + tbits = fold_convert (signed_char_type_node, tbits); + tbits = fold_build2 (best_g.code, signed_char_type_node, tbits, + build_int_cst (val_type, best_g.arg)); + tbits = fold_convert (val_type, tbits); + + /* Use map o G^-1 instead of original map to undo the effect of G. */ + + tmap = double_int_to_tree (map_type, best_g.map); + + return build_call_expr (fndecl, 3, tmap, tbits, tval); + } /* AVR_BUILTIN_INSERT_BITS */ + } + + return NULL_TREE; +} + + + struct gcc_target targetm = TARGET_INITIALIZER; #include "gt-avr.h" diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md index 79c81b79612..da2b8bc2d36 100644 --- a/gcc/config/avr/avr.md +++ b/gcc/config/avr/avr.md @@ -68,7 +68,7 @@ UNSPEC_FMULSU UNSPEC_COPYSIGN UNSPEC_IDENTITY - UNSPEC_MAP_BITS + UNSPEC_INSERT_BITS ]) (define_c_enum "unspecv" @@ -144,7 +144,7 @@ ashlhi, ashrhi, lshrhi, ashlsi, ashrsi, lshrsi, ashlpsi, ashrpsi, lshrpsi, - map_bits, + insert_bits, no" (const_string "no")) @@ -5264,28 +5264,20 @@ [(set_attr "length" "9") (set_attr "cc" "clobber")]) -(define_insn "map_bitsqi" - [(set (match_operand:QI 0 "register_operand" "=d") - (unspec:QI [(match_operand:SI 1 "const_int_operand" "n") - (match_operand:QI 2 "register_operand" "r")] - UNSPEC_MAP_BITS))] - "" - { - return avr_out_map_bits (insn, operands, NULL); - } - [(set_attr "adjust_len" "map_bits") - (set_attr "cc" "clobber")]) -(define_insn "map_bitshi" - [(set (match_operand:HI 0 "register_operand" "=&r") - (unspec:HI [(match_operand:DI 1 "const_double_operand" "n") - (match_operand:HI 2 "register_operand" "r")] - UNSPEC_MAP_BITS))] +;; __builtin_avr_insert_bits + +(define_insn "insert_bits" + [(set (match_operand:QI 0 "register_operand" "=r ,d ,r") + (unspec:QI [(match_operand:SI 1 "const_int_operand" "C0f,Cxf,C0f") + (match_operand:QI 2 "register_operand" "r ,r ,r") + (match_operand:QI 3 "nonmemory_operand" "n ,0 ,0")] + UNSPEC_INSERT_BITS))] "" { - return avr_out_map_bits (insn, operands, NULL); + return avr_out_insert_bits (operands, NULL); } - [(set_attr "adjust_len" "map_bits") + [(set_attr "adjust_len" "insert_bits") (set_attr "cc" "clobber")]) diff --git a/gcc/config/avr/constraints.md b/gcc/config/avr/constraints.md index 50aae32b01a..57e259db6d1 100644 --- a/gcc/config/avr/constraints.md +++ b/gcc/config/avr/constraints.md @@ -182,3 +182,13 @@ "Integer constant in the range -6 @dots{} 6." (and (match_code "const_int") (match_test "IN_RANGE (ival, -6, 6)"))) + +(define_constraint "Cxf" + "32-bit integer constant where at least one nibble is 0xf." + (and (match_code "const_int") + (match_test "avr_has_nibble_0xf (op)"))) + +(define_constraint "C0f" + "32-bit integer constant where no nibble equals 0xf." + (and (match_code "const_int") + (match_test "!avr_has_nibble_0xf (op)"))) diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 4c7196080a1..b666a2ef1c4 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -8812,33 +8812,53 @@ might increase delay time. @code{ticks} must be a compile time integer constant; delays with a variable number of cycles are not supported. @smallexample - unsigned char __builtin_avr_map8 (unsigned long map, unsigned char val) + unsigned char __builtin_avr_insert_bits (unsigned long map, unsigned char bits, unsigned char val) @end smallexample @noindent -Each bit of the result is copied from a specific bit of @code{val}. -@code{map} is a compile time constant that represents a map composed -of 8 nibbles (4-bit groups): -The @var{n}-th nibble of @code{map} specifies which bit of @code{val} -is to be moved to the @var{n}-th bit of the result. -For example, @code{map = 0x76543210} represents identity: The MSB of -the result is read from the 7-th bit of @code{val}, the LSB is -read from the 0-th bit to @code{val}, etc. -Two more examples: @code{0x01234567} reverses the bit order and -@code{0x32107654} is equivalent to a @code{swap} instruction. +Insert bits from @var{bits} into @var{val} and return the resulting +value. The nibbles of @var{map} determine how the insertion is +performed: Let @var{X} be the @var{n}-th nibble of @var{map} +@enumerate +@item If @var{X} is @code{0xf}, +then the @var{n}-th bit of @var{val} is returned unaltered. + +@item If X is in the range 0@dots{}7, +then the @var{n}-th result bit is set to the @var{X}-th bit of @var{bits} + +@item If X is in the range 8@dots{}@code{0xe}, +then the @var{n}-th result bit is undefined. +@end enumerate @noindent -One typical use case for this and the following built-in is adjusting input and -output values to non-contiguous port layouts. +One typical use case for this built-in is adjusting input and +output values to non-contiguous port layouts. Some examples: @smallexample - unsigned int __builtin_avr_map16 (unsigned long long map, unsigned int val) +// same as val, bits is unused +__builtin_avr_insert_bits (0xffffffff, bits, val) @end smallexample -@noindent -Similar to the previous built-in except that it operates on @code{int} -and thus 16 bits are involved. Again, @code{map} must be a compile -time constant. +@smallexample +// same as bits, val is unused +__builtin_avr_insert_bits (0x76543210, bits, val) +@end smallexample + +@smallexample +// same as rotating bits by 4 +__builtin_avr_insert_bits (0x32107654, bits, 0) +@end smallexample + +@smallexample +// high-nibble of result is the high-nibble of val +// low-nibble of result is the low-nibble of bits +__builtin_avr_insert_bits (0xffff3210, bits, val) +@end smallexample + +@smallexample +// reverse the bit order of bits +__builtin_avr_insert_bits (0x01234567, bits, 0) +@end smallexample @node Blackfin Built-in Functions @subsection Blackfin Built-in Functions diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 6f53a287eb5..b0e7c456f88 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2012-02-15 Georg-Johann Lay + + * gcc.target/avr/torture/builtin_insert_bits-1.c: New test. + * gcc.target/avr/torture/builtin_insert_bits-2.c: New test. + 2012-02-15 Eric Botcazou * gnat.dg/discr35.ad[sb]: New test. diff --git a/gcc/testsuite/gcc.target/avr/torture/builtin_insert_bits-1.c b/gcc/testsuite/gcc.target/avr/torture/builtin_insert_bits-1.c new file mode 100644 index 00000000000..fe20c916315 --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/torture/builtin_insert_bits-1.c @@ -0,0 +1,97 @@ +/* { dg-do run } */ + +#include + +#define MASK_F(M) \ + (0 \ + | ((0xf == (0xf & ((M) >> (4*0)))) ? (1 << 0) : 0) \ + | ((0xf == (0xf & ((M) >> (4*1)))) ? (1 << 1) : 0) \ + | ((0xf == (0xf & ((M) >> (4*2)))) ? (1 << 2) : 0) \ + | ((0xf == (0xf & ((M) >> (4*3)))) ? (1 << 3) : 0) \ + | ((0xf == (0xf & ((M) >> (4*4)))) ? (1 << 4) : 0) \ + | ((0xf == (0xf & ((M) >> (4*5)))) ? (1 << 5) : 0) \ + | ((0xf == (0xf & ((M) >> (4*6)))) ? (1 << 6) : 0) \ + | ((0xf == (0xf & ((M) >> (4*7)))) ? (1 << 7) : 0) \ + | 0) + +#define MASK_0_7(M) \ + (0 \ + | ((8 > (0xf & ((M) >> (4*0)))) ? (1 << 0) : 0) \ + | ((8 > (0xf & ((M) >> (4*1)))) ? (1 << 1) : 0) \ + | ((8 > (0xf & ((M) >> (4*2)))) ? (1 << 2) : 0) \ + | ((8 > (0xf & ((M) >> (4*3)))) ? (1 << 3) : 0) \ + | ((8 > (0xf & ((M) >> (4*4)))) ? (1 << 4) : 0) \ + | ((8 > (0xf & ((M) >> (4*5)))) ? (1 << 5) : 0) \ + | ((8 > (0xf & ((M) >> (4*6)))) ? (1 << 6) : 0) \ + | ((8 > (0xf & ((M) >> (4*7)))) ? (1 << 7) : 0) \ + | 0) + +#define INSERT_BITS(M,B,V) \ + (__extension__({ \ + unsigned char _n, _r = 0; \ + _n = 0xf & (M >> (4*0)); if (_n<8) _r |= (!!(B & (1 << _n))) << 0; \ + _n = 0xf & (M >> (4*1)); if (_n<8) _r |= (!!(B & (1 << _n))) << 1; \ + _n = 0xf & (M >> (4*2)); if (_n<8) _r |= (!!(B & (1 << _n))) << 2; \ + _n = 0xf & (M >> (4*3)); if (_n<8) _r |= (!!(B & (1 << _n))) << 3; \ + _n = 0xf & (M >> (4*4)); if (_n<8) _r |= (!!(B & (1 << _n))) << 4; \ + _n = 0xf & (M >> (4*5)); if (_n<8) _r |= (!!(B & (1 << _n))) << 5; \ + _n = 0xf & (M >> (4*6)); if (_n<8) _r |= (!!(B & (1 << _n))) << 6; \ + _n = 0xf & (M >> (4*7)); if (_n<8) _r |= (!!(B & (1 << _n))) << 7; \ + (unsigned char) ((V) & MASK_F(M)) | _r; \ + })) + +#define MASK_USED(M) (MASK_F(M) | MASK_0_7(M)) + +#define TEST2(M,B,V) \ + do { \ + __asm volatile (";" #M); \ + r1 = MASK_USED (M) \ + & __builtin_avr_insert_bits (M,B,V); \ + r2 = INSERT_BITS (M,B,V); \ + if (r1 != r2) \ + abort (); \ + } while(0) + +#define TEST1(M,X) \ + do { \ + TEST2 (M,X,0x00); TEST2 (M,0x00,X); \ + TEST2 (M,X,0xff); TEST2 (M,0xff,X); \ + TEST2 (M,X,0xaa); TEST2 (M,0xaa,X); \ + TEST2 (M,X,0xcc); TEST2 (M,0xcc,X); \ + TEST2 (M,X,0x96); TEST2 (M,0x96,X); \ + } while(0) + + + +void test8 (void) +{ + unsigned char r1, r2; + unsigned char ib; + + static const unsigned char V[] = + { + 0, 0xaa, 0xcc, 0xf0, 0xff, 0x5b, 0x4d + }; + + for (ib = 0; ib < sizeof (V) / sizeof (*V); ib++) + { + unsigned char b = V[ib]; + + TEST1 (0x76543210, b); + TEST1 (0x3210ffff, b); + TEST1 (0x67452301, b); + TEST1 (0xf0f1f2f3, b); + TEST1 (0xff10ff54, b); + TEST1 (0x01234567, b); + TEST1 (0xff765f32, b); + } +} + +/****************************************************************/ + +int main() +{ + test8(); + + exit(0); +} diff --git a/gcc/testsuite/gcc.target/avr/torture/builtin_insert_bits-2.c b/gcc/testsuite/gcc.target/avr/torture/builtin_insert_bits-2.c new file mode 100644 index 00000000000..06cafd6afa8 --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/torture/builtin_insert_bits-2.c @@ -0,0 +1,94 @@ +/* { dg-do run } */ + +#include + +#define MASK_F(M) \ + (0 \ + | ((0xf == (0xf & ((M) >> (4*0)))) ? (1 << 0) : 0) \ + | ((0xf == (0xf & ((M) >> (4*1)))) ? (1 << 1) : 0) \ + | ((0xf == (0xf & ((M) >> (4*2)))) ? (1 << 2) : 0) \ + | ((0xf == (0xf & ((M) >> (4*3)))) ? (1 << 3) : 0) \ + | ((0xf == (0xf & ((M) >> (4*4)))) ? (1 << 4) : 0) \ + | ((0xf == (0xf & ((M) >> (4*5)))) ? (1 << 5) : 0) \ + | ((0xf == (0xf & ((M) >> (4*6)))) ? (1 << 6) : 0) \ + | ((0xf == (0xf & ((M) >> (4*7)))) ? (1 << 7) : 0) \ + | 0) + +#define MASK_0_7(M) \ + (0 \ + | ((8 > (0xf & ((M) >> (4*0)))) ? (1 << 0) : 0) \ + | ((8 > (0xf & ((M) >> (4*1)))) ? (1 << 1) : 0) \ + | ((8 > (0xf & ((M) >> (4*2)))) ? (1 << 2) : 0) \ + | ((8 > (0xf & ((M) >> (4*3)))) ? (1 << 3) : 0) \ + | ((8 > (0xf & ((M) >> (4*4)))) ? (1 << 4) : 0) \ + | ((8 > (0xf & ((M) >> (4*5)))) ? (1 << 5) : 0) \ + | ((8 > (0xf & ((M) >> (4*6)))) ? (1 << 6) : 0) \ + | ((8 > (0xf & ((M) >> (4*7)))) ? (1 << 7) : 0) \ + | 0) + +#define INSERT_BITS(M,B,V) \ + (__extension__({ \ + unsigned char _n, _r = 0; \ + _n = 0xf & (M >> (4*0)); if (_n<8) _r |= (!!(B & (1 << _n))) << 0; \ + _n = 0xf & (M >> (4*1)); if (_n<8) _r |= (!!(B & (1 << _n))) << 1; \ + _n = 0xf & (M >> (4*2)); if (_n<8) _r |= (!!(B & (1 << _n))) << 2; \ + _n = 0xf & (M >> (4*3)); if (_n<8) _r |= (!!(B & (1 << _n))) << 3; \ + _n = 0xf & (M >> (4*4)); if (_n<8) _r |= (!!(B & (1 << _n))) << 4; \ + _n = 0xf & (M >> (4*5)); if (_n<8) _r |= (!!(B & (1 << _n))) << 5; \ + _n = 0xf & (M >> (4*6)); if (_n<8) _r |= (!!(B & (1 << _n))) << 6; \ + _n = 0xf & (M >> (4*7)); if (_n<8) _r |= (!!(B & (1 << _n))) << 7; \ + (unsigned char) ((V) & MASK_F(M)) | _r; \ + })) + +#define MASK_USED(M) (MASK_F(M) | MASK_0_7(M)) + +#define TEST2(M,B,V) \ + do { \ + __asm volatile (";" #M); \ + r1 = MASK_USED (M) \ + & __builtin_avr_insert_bits (M,B,V); \ + r2 = INSERT_BITS (M,B,V); \ + if (r1 != r2) \ + abort (); \ + } while(0) + +void test8 (void) +{ + unsigned char r1, r2; + unsigned char ib, iv; + + static const unsigned char V[] = + { + 0, 0xaa, 0xcc, 0xf0, 0xff, 0x5b, 0x4d + }; + + for (ib = 0; ib < sizeof (V) / sizeof (*V); ib++) + { + unsigned char b = V[ib]; + + for (iv = 0; iv < sizeof (V) / sizeof (*V); iv++) + { + unsigned char v = V[iv]; + + TEST2 (0x76543210, b, v); + TEST2 (0xffffffff, b, v); + TEST2 (0x3210ffff, b, v); + TEST2 (0x67452301, b, v); + TEST2 (0xf0f1f2f3, b, v); + TEST2 (0xff10ff54, b, v); + TEST2 (0x0765f321, b, v); + TEST2 (0x11223344, b, v); + TEST2 (0x01234567, b, v); + TEST2 (0xff7765f3, b, v); + } + } +} + +/****************************************************************/ + +int main() +{ + test8(); + + exit(0); +} -- 2.11.0