On Thu, 5 Jul 2018, Jakub Jelinek wrote: > Hi! > > I've encountered this while testing the rotate patterns in discussion > with Jonathan for the std::__rot{l,r}, in the rotate-9.c test without > this patch f1 is optimized into a rotate, but f2 is not. > > Fixed by moving the optimization from fold-const.c to match.pd (well, > leaving a helper in fold-const.c to avoid too much duplication). > > Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
OK. Thanks, Richard. > 2018-07-05 Jakub Jelinek <ja...@redhat.com> > > PR tree-optimization/86401 > * fold-const.c (fold_binary_loc) <case BIT_AND_EXPR>: Move the > ((A & N) + B) & M -> (A + B) & M etc. optimization into ... > (fold_bit_and_mask): ... here. New helper function for match.pd. > * fold-const.h (fold_bit_and_mask): Declare. > * match.pd (((A & N) + B) & M -> (A + B) & M): New optimization. > > * gcc.dg/tree-ssa/pr86401-1.c: New test. > * gcc.dg/tree-ssa/pr86401-2.c: New test. > * c-c++-common/rotate-9.c: New test. > > --- gcc/fold-const.c.jj 2018-06-26 09:05:23.196346433 +0200 > +++ gcc/fold-const.c 2018-07-04 12:47:59.139981801 +0200 > @@ -10236,121 +10236,6 @@ fold_binary_loc (location_t loc, enum tr > } > } > > - /* For constants M and N, if M == (1LL << cst) - 1 && (N & M) == M, > - ((A & N) + B) & M -> (A + B) & M > - Similarly if (N & M) == 0, > - ((A | N) + B) & M -> (A + B) & M > - and for - instead of + (or unary - instead of +) > - and/or ^ instead of |. > - If B is constant and (B & M) == 0, fold into A & M. */ > - if (TREE_CODE (arg1) == INTEGER_CST) > - { > - wi::tree_to_wide_ref cst1 = wi::to_wide (arg1); > - if ((~cst1 != 0) && (cst1 & (cst1 + 1)) == 0 > - && INTEGRAL_TYPE_P (TREE_TYPE (arg0)) > - && (TREE_CODE (arg0) == PLUS_EXPR > - || TREE_CODE (arg0) == MINUS_EXPR > - || TREE_CODE (arg0) == NEGATE_EXPR) > - && (TYPE_OVERFLOW_WRAPS (TREE_TYPE (arg0)) > - || TREE_CODE (TREE_TYPE (arg0)) == INTEGER_TYPE)) > - { > - tree pmop[2]; > - int which = 0; > - wide_int cst0; > - > - /* Now we know that arg0 is (C + D) or (C - D) or > - -C and arg1 (M) is == (1LL << cst) - 1. > - Store C into PMOP[0] and D into PMOP[1]. */ > - pmop[0] = TREE_OPERAND (arg0, 0); > - pmop[1] = NULL; > - if (TREE_CODE (arg0) != NEGATE_EXPR) > - { > - pmop[1] = TREE_OPERAND (arg0, 1); > - which = 1; > - } > - > - if ((wi::max_value (TREE_TYPE (arg0)) & cst1) != cst1) > - which = -1; > - > - for (; which >= 0; which--) > - switch (TREE_CODE (pmop[which])) > - { > - case BIT_AND_EXPR: > - case BIT_IOR_EXPR: > - case BIT_XOR_EXPR: > - if (TREE_CODE (TREE_OPERAND (pmop[which], 1)) > - != INTEGER_CST) > - break; > - cst0 = wi::to_wide (TREE_OPERAND (pmop[which], 1)) & cst1; > - if (TREE_CODE (pmop[which]) == BIT_AND_EXPR) > - { > - if (cst0 != cst1) > - break; > - } > - else if (cst0 != 0) > - break; > - /* If C or D is of the form (A & N) where > - (N & M) == M, or of the form (A | N) or > - (A ^ N) where (N & M) == 0, replace it with A. */ > - pmop[which] = TREE_OPERAND (pmop[which], 0); > - break; > - case INTEGER_CST: > - /* If C or D is a N where (N & M) == 0, it can be > - omitted (assumed 0). */ > - if ((TREE_CODE (arg0) == PLUS_EXPR > - || (TREE_CODE (arg0) == MINUS_EXPR && which == 0)) > - && (cst1 & wi::to_wide (pmop[which])) == 0) > - pmop[which] = NULL; > - break; > - default: > - break; > - } > - > - /* Only build anything new if we optimized one or both arguments > - above. */ > - if (pmop[0] != TREE_OPERAND (arg0, 0) > - || (TREE_CODE (arg0) != NEGATE_EXPR > - && pmop[1] != TREE_OPERAND (arg0, 1))) > - { > - tree utype = TREE_TYPE (arg0); > - if (! TYPE_OVERFLOW_WRAPS (TREE_TYPE (arg0))) > - { > - /* Perform the operations in a type that has defined > - overflow behavior. */ > - utype = unsigned_type_for (TREE_TYPE (arg0)); > - if (pmop[0] != NULL) > - pmop[0] = fold_convert_loc (loc, utype, pmop[0]); > - if (pmop[1] != NULL) > - pmop[1] = fold_convert_loc (loc, utype, pmop[1]); > - } > - > - if (TREE_CODE (arg0) == NEGATE_EXPR) > - tem = fold_build1_loc (loc, NEGATE_EXPR, utype, pmop[0]); > - else if (TREE_CODE (arg0) == PLUS_EXPR) > - { > - if (pmop[0] != NULL && pmop[1] != NULL) > - tem = fold_build2_loc (loc, PLUS_EXPR, utype, > - pmop[0], pmop[1]); > - else if (pmop[0] != NULL) > - tem = pmop[0]; > - else if (pmop[1] != NULL) > - tem = pmop[1]; > - else > - return build_int_cst (type, 0); > - } > - else if (pmop[0] == NULL) > - tem = fold_build1_loc (loc, NEGATE_EXPR, utype, pmop[1]); > - else > - tem = fold_build2_loc (loc, MINUS_EXPR, utype, > - pmop[0], pmop[1]); > - /* TEM is now the new binary +, - or unary - replacement. */ > - tem = fold_build2_loc (loc, BIT_AND_EXPR, utype, tem, > - fold_convert_loc (loc, utype, arg1)); > - return fold_convert_loc (loc, type, tem); > - } > - } > - } > - > /* Simplify ((int)c & 0377) into (int)c, if c is unsigned char. */ > if (TREE_CODE (arg1) == INTEGER_CST && TREE_CODE (arg0) == NOP_EXPR > && TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (arg0, 0)))) > @@ -11264,6 +11149,100 @@ fold_binary_loc (location_t loc, enum tr > } /* switch (code) */ > } > > +/* For constants M and N, if M == (1LL << cst) - 1 && (N & M) == M, > + ((A & N) + B) & M -> (A + B) & M > + Similarly if (N & M) == 0, > + ((A | N) + B) & M -> (A + B) & M > + and for - instead of + (or unary - instead of +) > + and/or ^ instead of |. > + If B is constant and (B & M) == 0, fold into A & M. > + > + This function is a helper for match.pd patterns. Return non-NULL > + type in which the simplified operation should be performed only > + if any optimization is possible. > + > + ARG1 is M above, ARG00 is left operand of +/-, if CODE00 is BIT_*_EXPR, > + then ARG00{0,1} are operands of that bitop, otherwise CODE00 is > ERROR_MARK. > + Similarly for ARG01, CODE01 and ARG01{0,1}, just for the right operand of > + +/-. */ > +tree > +fold_bit_and_mask (tree type, tree arg1, enum tree_code code, > + tree arg00, enum tree_code code00, tree arg000, tree arg001, > + tree arg01, enum tree_code code01, tree arg010, tree arg011, > + tree *pmop) > +{ > + gcc_assert (TREE_CODE (arg1) == INTEGER_CST); > + gcc_assert (code == PLUS_EXPR || code == MINUS_EXPR || code == > NEGATE_EXPR); > + wi::tree_to_wide_ref cst1 = wi::to_wide (arg1); > + if (~cst1 == 0 > + || (cst1 & (cst1 + 1)) != 0 > + || !INTEGRAL_TYPE_P (type) > + || (!TYPE_OVERFLOW_WRAPS (type) > + && TREE_CODE (type) != INTEGER_TYPE) > + || (wi::max_value (type) & cst1) != cst1) > + return NULL_TREE; > + > + enum tree_code codes[2] = { code00, code01 }; > + tree arg0xx[4] = { arg000, arg001, arg010, arg011 }; > + int which = 0; > + wide_int cst0; > + > + /* Now we know that arg0 is (C + D) or (C - D) or -C and > + arg1 (M) is == (1LL << cst) - 1. > + Store C into PMOP[0] and D into PMOP[1]. */ > + pmop[0] = arg00; > + pmop[1] = arg01; > + which = code != NEGATE_EXPR; > + > + for (; which >= 0; which--) > + switch (codes[which]) > + { > + case BIT_AND_EXPR: > + case BIT_IOR_EXPR: > + case BIT_XOR_EXPR: > + gcc_assert (TREE_CODE (arg0xx[2 * which + 1]) == INTEGER_CST); > + cst0 = wi::to_wide (arg0xx[2 * which + 1]) & cst1; > + if (codes[which] == BIT_AND_EXPR) > + { > + if (cst0 != cst1) > + break; > + } > + else if (cst0 != 0) > + break; > + /* If C or D is of the form (A & N) where > + (N & M) == M, or of the form (A | N) or > + (A ^ N) where (N & M) == 0, replace it with A. */ > + pmop[which] = arg0xx[2 * which]; > + break; > + case ERROR_MARK: > + if (TREE_CODE (pmop[which]) != INTEGER_CST) > + break; > + /* If C or D is a N where (N & M) == 0, it can be > + omitted (replaced with 0). */ > + if ((code == PLUS_EXPR > + || (code == MINUS_EXPR && which == 0)) > + && (cst1 & wi::to_wide (pmop[which])) == 0) > + pmop[which] = build_int_cst (type, 0); > + /* Similarly, with C - N where (-N & M) == 0. */ > + if (code == MINUS_EXPR > + && which == 1 > + && (cst1 & -wi::to_wide (pmop[which])) == 0) > + pmop[which] = build_int_cst (type, 0); > + break; > + default: > + gcc_unreachable (); > + } > + > + /* Only build anything new if we optimized one or both arguments above. */ > + if (pmop[0] == arg00 && pmop[1] == arg01) > + return NULL_TREE; > + > + if (TYPE_OVERFLOW_WRAPS (type)) > + return type; > + else > + return unsigned_type_for (type); > +} > + > /* Used by contains_label_[p1]. */ > > struct contains_label_data > --- gcc/fold-const.h.jj 2018-05-25 14:34:36.871377394 +0200 > +++ gcc/fold-const.h 2018-07-04 12:34:18.072910936 +0200 > @@ -96,6 +96,9 @@ extern tree omit_two_operands_loc (locat > extern tree invert_truthvalue_loc (location_t, tree); > extern tree fold_unary_to_constant (enum tree_code, tree, tree); > extern tree fold_binary_to_constant (enum tree_code, tree, tree, tree); > +extern tree fold_bit_and_mask (tree, tree, enum tree_code, > + tree, enum tree_code, tree, tree, > + tree, enum tree_code, tree, tree, tree *); > extern tree fold_read_from_constant_string (tree); > extern tree int_const_binop (enum tree_code, const_tree, const_tree); > #define build_fold_addr_expr(T)\ > --- gcc/match.pd.jj 2018-06-20 08:15:34.196862169 +0200 > +++ gcc/match.pd 2018-07-04 13:21:07.897317459 +0200 > @@ -779,6 +779,60 @@ (define_operator_list COND_BINARY > (bit_xor @0 @1))) > #endif > > +/* For constants M and N, if M == (1LL << cst) - 1 && (N & M) == M, > + ((A & N) + B) & M -> (A + B) & M > + Similarly if (N & M) == 0, > + ((A | N) + B) & M -> (A + B) & M > + and for - instead of + (or unary - instead of +) > + and/or ^ instead of |. > + If B is constant and (B & M) == 0, fold into A & M. */ > +(for op (plus minus) > + (for bitop (bit_and bit_ior bit_xor) > + (simplify > + (bit_and (op:s (bitop:s@0 @3 INTEGER_CST@4) @1) INTEGER_CST@2) > + (with > + { tree pmop[2]; > + tree utype = fold_bit_and_mask (TREE_TYPE (@0), @2, op, @0, bitop, > + @3, @4, @1, ERROR_MARK, NULL_TREE, > + NULL_TREE, pmop); } > + (if (utype) > + (convert (bit_and (op (convert:utype { pmop[0]; }) > + (convert:utype { pmop[1]; })) > + (convert:utype @2)))))) > + (simplify > + (bit_and (op:s @0 (bitop:s@1 @3 INTEGER_CST@4)) INTEGER_CST@2) > + (with > + { tree pmop[2]; > + tree utype = fold_bit_and_mask (TREE_TYPE (@0), @2, op, @0, > ERROR_MARK, > + NULL_TREE, NULL_TREE, @1, bitop, @3, > + @4, pmop); } > + (if (utype) > + (convert (bit_and (op (convert:utype { pmop[0]; }) > + (convert:utype { pmop[1]; })) > + (convert:utype @2))))))) > + (simplify > + (bit_and (op:s @0 @1) INTEGER_CST@2) > + (with > + { tree pmop[2]; > + tree utype = fold_bit_and_mask (TREE_TYPE (@0), @2, op, @0, ERROR_MARK, > + NULL_TREE, NULL_TREE, @1, ERROR_MARK, > + NULL_TREE, NULL_TREE, pmop); } > + (if (utype) > + (convert (bit_and (op (convert:utype { pmop[0]; }) > + (convert:utype { pmop[1]; })) > + (convert:utype @2))))))) > +(for bitop (bit_and bit_ior bit_xor) > + (simplify > + (bit_and (negate:s (bitop:s@0 @2 INTEGER_CST@3)) INTEGER_CST@1) > + (with > + { tree pmop[2]; > + tree utype = fold_bit_and_mask (TREE_TYPE (@0), @1, NEGATE_EXPR, @0, > + bitop, @2, @3, NULL_TREE, ERROR_MARK, > + NULL_TREE, NULL_TREE, pmop); } > + (if (utype) > + (convert (bit_and (negate (convert:utype { pmop[0]; })) > + (convert:utype @1))))))) > + > /* X % Y is smaller than Y. */ > (for cmp (lt ge) > (simplify > --- gcc/testsuite/gcc.dg/tree-ssa/pr86401-1.c.jj 2018-07-04 > 13:36:58.765324864 +0200 > +++ gcc/testsuite/gcc.dg/tree-ssa/pr86401-1.c 2018-07-04 13:40:10.726523960 > +0200 > @@ -0,0 +1,48 @@ > +/* PR tree-optimization/86401 */ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fdump-tree-optimized" } */ > +/* { dg-final { scan-tree-dump-not " \\+ 64" "optimized" } } */ > +/* { dg-final { scan-tree-dump-not "64 - " "optimized" } } */ > +/* { dg-final { scan-tree-dump-not " \\+ 4294967232" "optimized" } } */ > +/* { dg-final { scan-tree-dump-not " & 319" "optimized" } } */ > +/* { dg-final { scan-tree-dump-not " \\| 4294967168" "optimized" } } */ > +/* { dg-final { scan-tree-dump-not " \\^ 4294966912" "optimized" } } */ > + > +unsigned f1 (unsigned x) { unsigned m = 64; return (m + x) & (m - 1); } > +unsigned f2 (unsigned x) { return (64 + x) & 63; } > +unsigned f3 (unsigned x) { unsigned m = 64; return (x + m) & (m - 1); } > +unsigned f4 (unsigned x) { return (x + 64) & 63; } > +unsigned f5 (unsigned x) { unsigned m = 64; return (m - x) & (m - 1); } > +unsigned f6 (unsigned x) { return (64 - x) & 63; } > +unsigned f7 (unsigned x) { unsigned m = 64; return (x - m) & (m - 1); } > +unsigned f8 (unsigned x) { return (x - 64) & 63; } > +unsigned f9 (unsigned x, unsigned y) { unsigned m = 64, n = 256 | (m - 1); > return ((x & n) + y) & (m - 1); } > +unsigned f10 (unsigned x, unsigned y) { return ((x & 319) + y) & 63; } > +unsigned f11 (unsigned x, unsigned y) { unsigned m = 64, n = -128; return > ((x | n) + y) & (m - 1); } > +unsigned f12 (unsigned x, unsigned y) { return ((x | -128) + y) & 63; } > +unsigned f13 (unsigned x, unsigned y) { unsigned m = 64, n = -384; return > ((x ^ n) + y) & (m - 1); } > +unsigned f14 (unsigned x, unsigned y) { return ((x ^ -384) + y) & 63; } > +unsigned f15 (unsigned x, unsigned y) { unsigned m = 64, n = 256 | (m - 1); > return (y + (x & n)) & (m - 1); } > +unsigned f16 (unsigned x, unsigned y) { return (y + (x & 319)) & 63; } > +unsigned f17 (unsigned x, unsigned y) { unsigned m = 64, n = -128; return (y > + (x | n)) & (m - 1); } > +unsigned f18 (unsigned x, unsigned y) { return (y + (x | -128)) & 63; } > +unsigned f19 (unsigned x, unsigned y) { unsigned m = 64, n = -384; return (y > + (x ^ n)) & (m - 1); } > +unsigned f20 (unsigned x, unsigned y) { return (y + (x ^ -384)) & 63; } > +unsigned f21 (unsigned x, unsigned y) { unsigned m = 64, n = 256 | (m - 1); > return ((x & n) - y) & (m - 1); } > +unsigned f22 (unsigned x, unsigned y) { return ((x & 319) - y) & 63; } > +unsigned f23 (unsigned x, unsigned y) { unsigned m = 64, n = -128; return > ((x | n) - y) & (m - 1); } > +unsigned f24 (unsigned x, unsigned y) { return ((x | -128) - y) & 63; } > +unsigned f25 (unsigned x, unsigned y) { unsigned m = 64, n = -384; return > ((x ^ n) - y) & (m - 1); } > +unsigned f26 (unsigned x, unsigned y) { return ((x ^ -384) - y) & 63; } > +unsigned f27 (unsigned x, unsigned y) { unsigned m = 64, n = 256 | (m - 1); > return (y - (x & n)) & (m - 1); } > +unsigned f28 (unsigned x, unsigned y) { return (y - (x & 319)) & 63; } > +unsigned f29 (unsigned x, unsigned y) { unsigned m = 64, n = -128; return (y > - (x | n)) & (m - 1); } > +unsigned f30 (unsigned x, unsigned y) { return (y - (x | -128)) & 63; } > +unsigned f31 (unsigned x, unsigned y) { unsigned m = 64, n = -384; return (y > - (x ^ n)) & (m - 1); } > +unsigned f32 (unsigned x, unsigned y) { return (y - (x ^ -384)) & 63; } > +unsigned f33 (unsigned x) { unsigned m = 64, n = 256 | (m - 1); return (-(x > & n)) & (m - 1); } > +unsigned f34 (unsigned x) { return (-(x & 319)) & 63; } > +unsigned f35 (unsigned x) { unsigned m = 64, n = -128; return (-(x | n)) & > (m - 1); } > +unsigned f36 (unsigned x) { return (-(x | -128)) & 63; } > +unsigned f37 (unsigned x) { unsigned m = 64, n = -384; return (-(x ^ n)) & > (m - 1); } > +unsigned f38 (unsigned x) { return (-(x ^ -384)) & 63; } > --- gcc/testsuite/gcc.dg/tree-ssa/pr86401-2.c.jj 2018-07-04 > 13:47:52.371002780 +0200 > +++ gcc/testsuite/gcc.dg/tree-ssa/pr86401-2.c 2018-07-04 13:48:21.267032753 > +0200 > @@ -0,0 +1,48 @@ > +/* PR tree-optimization/86401 */ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fdump-tree-optimized" } */ > +/* { dg-final { scan-tree-dump-not " \\+ 64" "optimized" } } */ > +/* { dg-final { scan-tree-dump-not "64 - " "optimized" } } */ > +/* { dg-final { scan-tree-dump-not " \\+ -64" "optimized" } } */ > +/* { dg-final { scan-tree-dump-not " & 319" "optimized" } } */ > +/* { dg-final { scan-tree-dump-not " \\| -128" "optimized" } } */ > +/* { dg-final { scan-tree-dump-not " \\^ -384" "optimized" } } */ > + > +int f1 (int x) { int m = 64; return (m + x) & (m - 1); } > +int f2 (int x) { return (64 + x) & 63; } > +int f3 (int x) { int m = 64; return (x + m) & (m - 1); } > +int f4 (int x) { return (x + 64) & 63; } > +int f5 (int x) { int m = 64; return (m - x) & (m - 1); } > +int f6 (int x) { return (64 - x) & 63; } > +int f7 (int x) { int m = 64; return (x - m) & (m - 1); } > +int f8 (int x) { return (x - 64) & 63; } > +int f9 (int x, int y) { int m = 64, n = 256 | (m - 1); return ((x & n) + y) > & (m - 1); } > +int f10 (int x, int y) { return ((x & 319) + y) & 63; } > +int f11 (int x, int y) { int m = 64, n = -128; return ((x | n) + y) & (m - > 1); } > +int f12 (int x, int y) { return ((x | -128) + y) & 63; } > +int f13 (int x, int y) { int m = 64, n = -384; return ((x ^ n) + y) & (m - > 1); } > +int f14 (int x, int y) { return ((x ^ -384) + y) & 63; } > +int f15 (int x, int y) { int m = 64, n = 256 | (m - 1); return (y + (x & n)) > & (m - 1); } > +int f16 (int x, int y) { return (y + (x & 319)) & 63; } > +int f17 (int x, int y) { int m = 64, n = -128; return (y + (x | n)) & (m - > 1); } > +int f18 (int x, int y) { return (y + (x | -128)) & 63; } > +int f19 (int x, int y) { int m = 64, n = -384; return (y + (x ^ n)) & (m - > 1); } > +int f20 (int x, int y) { return (y + (x ^ -384)) & 63; } > +int f21 (int x, int y) { int m = 64, n = 256 | (m - 1); return ((x & n) - y) > & (m - 1); } > +int f22 (int x, int y) { return ((x & 319) - y) & 63; } > +int f23 (int x, int y) { int m = 64, n = -128; return ((x | n) - y) & (m - > 1); } > +int f24 (int x, int y) { return ((x | -128) - y) & 63; } > +int f25 (int x, int y) { int m = 64, n = -384; return ((x ^ n) - y) & (m - > 1); } > +int f26 (int x, int y) { return ((x ^ -384) - y) & 63; } > +int f27 (int x, int y) { int m = 64, n = 256 | (m - 1); return (y - (x & n)) > & (m - 1); } > +int f28 (int x, int y) { return (y - (x & 319)) & 63; } > +int f29 (int x, int y) { int m = 64, n = -128; return (y - (x | n)) & (m - > 1); } > +int f30 (int x, int y) { return (y - (x | -128)) & 63; } > +int f31 (int x, int y) { int m = 64, n = -384; return (y - (x ^ n)) & (m - > 1); } > +int f32 (int x, int y) { return (y - (x ^ -384)) & 63; } > +int f33 (int x) { int m = 64, n = 256 | (m - 1); return (-(x & n)) & (m - > 1); } > +int f34 (int x) { return (-(x & 319)) & 63; } > +int f35 (int x) { int m = 64, n = -128; return (-(x | n)) & (m - 1); } > +int f36 (int x) { return (-(x | -128)) & 63; } > +int f37 (int x) { int m = 64, n = -384; return (-(x ^ n)) & (m - 1); } > +int f38 (int x) { return (-(x ^ -384)) & 63; } > --- gcc/testsuite/c-c++-common/rotate-9.c.jj 2018-07-04 13:53:08.810330994 > +0200 > +++ gcc/testsuite/c-c++-common/rotate-9.c 2018-07-04 13:53:35.481358656 > +0200 > @@ -0,0 +1,19 @@ > +/* PR tree-optimization/86401 */ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fno-ipa-icf -fdump-tree-optimized" } */ > +/* { dg-final { scan-tree-dump-times "r\[<>]\[<>]" 2 "optimized" } } */ > + > +unsigned int > +f1 (unsigned int x, unsigned int s) > +{ > + unsigned int t = s % (__CHAR_BIT__ * __SIZEOF_INT__); > + return (x << t) | (x >> (((__CHAR_BIT__ * __SIZEOF_INT__) - t) % > (__CHAR_BIT__ * __SIZEOF_INT__))); > +} > + > +unsigned int > +f2 (unsigned int x, unsigned int s) > +{ > + int n = __CHAR_BIT__ * __SIZEOF_INT__; > + unsigned int t = s % n; > + return (x << t) | (x >> ((n - t) % n)); > +} > > Jakub > > -- Richard Biener <rguent...@suse.de> SUSE LINUX GmbH, GF: Felix Imendoerffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nuernberg)