From: Oleg Endo Date: Tue, 25 Sep 2012 19:06:28 +0000 (+0000) Subject: re PR target/54089 ([SH] Refactor shift patterns) X-Git-Tag: misc/gccgo-go1_1_2~637 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=780405350cee277933a7835a10d960dcea3a0947;p=thirdparty%2Fgcc.git re PR target/54089 ([SH] Refactor shift patterns) PR target/54089 * config/sh/constraints.md (Jhb): New constraint. * config/sh/predicates.md (negt_reg_shl31_operand): New predicate. * config/sh/sh.md (rotrsi3): New expander. (rotrsi3_1, *rotrsi3_1, *rotlsi3_1): New insns. (rotlsi3, rotlhi3): Use const_int_operand predicate instead of immediate_operand and remove CONST_INT_P checks in expansion code. (*rotcr): Cleanup variable usage. Handle preceding nott insn. Add split with swapped operands. (*rotcr_neg_t, *movt_msb, *negt_msb): New insns and splits. PR target/54089 * gcc.target/sh/pr54089-1.c (test_15, test_16, test_17, test_18, test_19, test_20, test_21, test_22, test_23): New functions. * gcc.target/sh/pr54089-4.c: New. * gcc.target/sh/pr54089-5.c: New. * gcc.target/sh/pr54089-6.c: New. * gcc.target/sh/pr54089-7.c: New. From-SVN: r191743 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index f09f1c84f220..953b07e908a8 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,16 @@ +2012-09-25 Oleg Endo + + PR target/54089 + * config/sh/constraints.md (Jhb): New constraint. + * config/sh/predicates.md (negt_reg_shl31_operand): New predicate. + * config/sh/sh.md (rotrsi3): New expander. + (rotrsi3_1, *rotrsi3_1, *rotlsi3_1): New insns. + (rotlsi3, rotlhi3): Use const_int_operand predicate instead of + immediate_operand and remove CONST_INT_P checks in expansion code. + (*rotcr): Cleanup variable usage. Handle preceding nott insn. Add + split with swapped operands. + (*rotcr_neg_t, *movt_msb, *negt_msb): New insns and splits. + 2012-09-25 Aldy Hernandez PR middle-end/53850 @@ -1066,7 +1079,7 @@ 2012-09-14 Christian Bruel * config/sh/predicates.md (t_reg_operand): Check REG_P for SUBREG. - * config/sh/sh.c (sequence_insn_p: Check INSNP_P for SEQUENCE. + * config/sh/sh.c (sequence_insn_p): Check INSNP_P for SEQUENCE. 2012-09-14 Jakub Jelinek diff --git a/gcc/config/sh/constraints.md b/gcc/config/sh/constraints.md index 47350dff22b5..c2540e8b6683 100644 --- a/gcc/config/sh/constraints.md +++ b/gcc/config/sh/constraints.md @@ -33,6 +33,7 @@ ;; J16: 0xffffffff00000000 | 0x00000000ffffffff ;; Jmb: 0x000000FF ;; Jmw: 0x0000FFFF +;; Jhb: 0x80000000 ;; Kxx: unsigned xx bit ;; M: 1 ;; N: 0 @@ -148,6 +149,11 @@ (and (match_code "const_int") (match_test "ival == 0xFFFF"))) +(define_constraint "Jhb" + "Highest bit constant" + (and (match_code "const_int") + (match_test "(ival & 0xFFFFFFFF) == 0x80000000"))) + (define_constraint "K03" "An unsigned 3-bit constant, as used in SH2A bclr, bset, etc." (and (match_code "const_int") diff --git a/gcc/config/sh/predicates.md b/gcc/config/sh/predicates.md index 27e42f159bc1..93a33bf2544b 100644 --- a/gcc/config/sh/predicates.md +++ b/gcc/config/sh/predicates.md @@ -1033,3 +1033,41 @@ (define_predicate "arith_reg_or_t_reg_operand" (ior (match_operand 0 "arith_reg_operand") (match_operand 0 "t_reg_operand"))) + +;; A predicate describing the negated value of the T bit register shifted +;; left by 31. +(define_predicate "negt_reg_shl31_operand" + (match_code "plus,minus,if_then_else") +{ + /* (plus:SI (mult:SI (match_operand:SI 1 "t_reg_operand") + (const_int -2147483648)) ;; 0xffffffff80000000 + (const_int -2147483648)) + */ + if (GET_CODE (op) == PLUS && satisfies_constraint_Jhb (XEXP (op, 1)) + && GET_CODE (XEXP (op, 0)) == MULT + && t_reg_operand (XEXP (XEXP (op, 0), 0), SImode) + && satisfies_constraint_Jhb (XEXP (XEXP (op, 0), 1))) + return true; + + /* (minus:SI (const_int -2147483648) ;; 0xffffffff80000000 + (mult:SI (match_operand:SI 1 "t_reg_operand") + (const_int -2147483648))) + */ + if (GET_CODE (op) == MINUS + && satisfies_constraint_Jhb (XEXP (op, 0)) + && GET_CODE (XEXP (op, 1)) == MULT + && t_reg_operand (XEXP (XEXP (op, 1), 0), SImode) + && satisfies_constraint_Jhb (XEXP (XEXP (op, 1), 1))) + return true; + + /* (if_then_else:SI (match_operand:SI 1 "t_reg_operand") + (const_int 0) + (const_int -2147483648)) ;; 0xffffffff80000000 + */ + if (GET_CODE (op) == IF_THEN_ELSE && t_reg_operand (XEXP (op, 0), SImode) + && satisfies_constraint_Z (XEXP (op, 1)) + && satisfies_constraint_Jhb (XEXP (op, 2))) + return true; + + return false; +}) diff --git a/gcc/config/sh/sh.md b/gcc/config/sh/sh.md index 27c5633dd9da..8f0443ad6db3 100644 --- a/gcc/config/sh/sh.md +++ b/gcc/config/sh/sh.md @@ -3817,6 +3817,42 @@ label: GEN_INT (56), GEN_INT (8)); }) +(define_expand "rotrsi3" + [(set (match_operand:SI 0 "arith_reg_dest") + (rotatert:SI (match_operand:SI 1 "arith_reg_operand") + (match_operand:SI 2 "const_int_operand")))] + "TARGET_SH1" +{ + HOST_WIDE_INT ival = INTVAL (operands[2]); + if (ival == 1) + { + emit_insn (gen_rotrsi3_1 (operands[0], operands[1])); + DONE; + } + + FAIL; +}) + +(define_insn "rotrsi3_1" + [(set (match_operand:SI 0 "arith_reg_dest" "=r") + (rotatert:SI (match_operand:SI 1 "arith_reg_operand" "0") + (const_int 1))) + (set (reg:SI T_REG) + (and:SI (match_dup 1) (const_int 1)))] + "TARGET_SH1" + "rotr %0" + [(set_attr "type" "arith")]) + +;; A slimplified version of rotr for combine. +(define_insn "*rotrsi3_1" + [(set (match_operand:SI 0 "arith_reg_dest" "=r") + (rotatert:SI (match_operand:SI 1 "arith_reg_operand" "0") + (const_int 1))) + (clobber (reg:SI T_REG))] + "TARGET_SH1" + "rotr %0" + [(set_attr "type" "arith")]) + (define_insn "rotlsi3_1" [(set (match_operand:SI 0 "arith_reg_dest" "=r") (rotate:SI (match_operand:SI 1 "arith_reg_operand" "0") @@ -3827,6 +3863,16 @@ label: "rotl %0" [(set_attr "type" "arith")]) +;; A simplified version of rotl for combine. +(define_insn "*rotlsi3_1" + [(set (match_operand:SI 0 "arith_reg_dest" "=r") + (rotate:SI (match_operand:SI 1 "arith_reg_operand" "0") + (const_int 1))) + (clobber (reg:SI T_REG))] + "TARGET_SH1" + "rotl %0" + [(set_attr "type" "arith")]) + (define_insn "rotlsi3_31" [(set (match_operand:SI 0 "arith_reg_dest" "=r") (rotate:SI (match_operand:SI 1 "arith_reg_operand" "0") @@ -3845,9 +3891,9 @@ label: [(set_attr "type" "arith")]) (define_expand "rotlsi3" - [(set (match_operand:SI 0 "arith_reg_dest" "") - (rotate:SI (match_operand:SI 1 "arith_reg_operand" "") - (match_operand:SI 2 "immediate_operand" "")))] + [(set (match_operand:SI 0 "arith_reg_dest") + (rotate:SI (match_operand:SI 1 "arith_reg_operand") + (match_operand:SI 2 "const_int_operand")))] "TARGET_SH1" { static const char rot_tab[] = { @@ -3857,12 +3903,8 @@ label: 002, 002, 010, 000, 000, 000, 000, 000, }; - int count, choice; - - if (!CONST_INT_P (operands[2])) - FAIL; - count = INTVAL (operands[2]); - choice = rot_tab[count]; + int count = INTVAL (operands[2]); + int choice = rot_tab[count]; if (choice & 010 && SH_DYNAMIC_SHIFT_COST <= 1) FAIL; choice &= 7; @@ -3908,12 +3950,12 @@ label: [(set_attr "type" "arith")]) (define_expand "rotlhi3" - [(set (match_operand:HI 0 "arith_reg_operand" "") - (rotate:HI (match_operand:HI 1 "arith_reg_operand" "") - (match_operand:HI 2 "immediate_operand" "")))] + [(set (match_operand:HI 0 "arith_reg_operand") + (rotate:HI (match_operand:HI 1 "arith_reg_operand") + (match_operand:HI 2 "const_int_operand")))] "TARGET_SH1" { - if (!CONST_INT_P (operands[2]) || INTVAL (operands[2]) != 8) + if (INTVAL (operands[2]) != 8) FAIL; }) @@ -3950,11 +3992,7 @@ label: { if (INTVAL (operands[2]) > 1) { - /* use plus_constant function ?? */ - const int shift_count = INTVAL (operands[2]) - 1; - const rtx shift_count_rtx = GEN_INT (shift_count); - rtx shift_res = gen_reg_rtx (SImode); - + const rtx shift_count = GEN_INT (INTVAL (operands[2]) - 1); rtx prev_set_t_insn = NULL_RTX; rtx tmp_t_reg = NULL_RTX; @@ -3963,10 +4001,24 @@ label: shift insn before that insn, to remove the T_REG dependency. If the insn that sets the T_REG cannot be found, store the T_REG in a temporary reg and restore it after the shift. */ - if (sh_lshrsi_clobbers_t_reg_p (shift_count_rtx) - && ! sh_dynamicalize_shift_p (shift_count_rtx)) + if (sh_lshrsi_clobbers_t_reg_p (shift_count) + && ! sh_dynamicalize_shift_p (shift_count)) { prev_set_t_insn = prev_nonnote_insn_bb (curr_insn); + + /* Skip the nott insn, which was probably inserted by the splitter + of *rotcr_neg_t. Don't use one of the recog functions + here during insn splitting, since that causes problems in later + passes. */ + if (prev_set_t_insn != NULL_RTX) + { + rtx pat = PATTERN (prev_set_t_insn); + if (GET_CODE (pat) == SET + && t_reg_operand (XEXP (pat, 0), SImode) + && negt_reg_operand (XEXP (pat, 1), SImode)) + prev_set_t_insn = prev_nonnote_insn_bb (prev_set_t_insn); + } + if (! (prev_set_t_insn != NULL_RTX && reg_set_p (get_t_reg_rtx (), prev_set_t_insn) && ! reg_referenced_p (get_t_reg_rtx (), @@ -3978,14 +4030,15 @@ label: } } - rtx shift_rtx = gen_lshrsi3 (shift_res, operands[1], shift_count_rtx); - operands[1] = shift_res; + rtx shift_result = gen_reg_rtx (SImode); + rtx shift_insn = gen_lshrsi3 (shift_result, operands[1], shift_count); + operands[1] = shift_result; /* Emit the shift insn before the insn that sets T_REG, if possible. */ if (prev_set_t_insn != NULL_RTX) - emit_insn_before (shift_rtx, prev_set_t_insn); + emit_insn_before (shift_insn, prev_set_t_insn); else - emit_insn (shift_rtx); + emit_insn (shift_insn); /* Restore T_REG if it has been saved before. */ if (tmp_t_reg != NULL_RTX) @@ -4007,6 +4060,20 @@ label: DONE; }) +;; If combine tries the same as above but with swapped operands, split +;; it so that it will try the pattern above. +(define_split + [(set (match_operand:SI 0 "arith_reg_dest") + (ior:SI (ashift:SI (match_operand:SI 1 "arith_reg_or_t_reg_operand") + (const_int 31)) + (lshiftrt:SI (match_operand:SI 2 "arith_reg_operand") + (match_operand:SI 3 "const_int_operand"))))] + "TARGET_SH1 && can_create_pseudo_p ()" + [(parallel [(set (match_dup 0) + (ior:SI (lshiftrt:SI (match_dup 2) (match_dup 3)) + (ashift:SI (match_dup 1) (const_int 31)))) + (clobber (reg:SI T_REG))])]) + ;; rotcr combine bridge pattern which will make combine try out more ;; complex patterns. (define_insn_and_split "*rotcr" @@ -4040,6 +4107,41 @@ label: DONE; }) +;; rotcr combine patterns for rotating in the negated T_REG value. +(define_insn_and_split "*rotcr_neg_t" + [(set (match_operand:SI 0 "arith_reg_dest") + (ior:SI (match_operand:SI 1 "negt_reg_shl31_operand") + (lshiftrt:SI (match_operand:SI 2 "arith_reg_operand") + (match_operand:SI 3 "const_int_operand")))) + (clobber (reg:SI T_REG))] + "TARGET_SH1" + "#" + "&& can_create_pseudo_p ()" + [(parallel [(set (match_dup 0) + (ior:SI (lshiftrt:SI (match_dup 2) (match_dup 3)) + (ashift:SI (reg:SI T_REG) (const_int 31)))) + (clobber (reg:SI T_REG))])] +{ + emit_insn (gen_nott (get_t_reg_rtx ())); +}) + +(define_insn_and_split "*rotcr_neg_t" + [(set (match_operand:SI 0 "arith_reg_dest") + (ior:SI (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand") + (match_operand:SI 2 "const_int_operand")) + (match_operand:SI 3 "negt_reg_shl31_operand"))) + (clobber (reg:SI T_REG))] + "TARGET_SH1" + "#" + "&& can_create_pseudo_p ()" + [(parallel [(set (match_dup 0) + (ior:SI (lshiftrt:SI (match_dup 1) (match_dup 2)) + (ashift:SI (reg:SI T_REG) (const_int 31)))) + (clobber (reg:SI T_REG))])] +{ + emit_insn (gen_nott (get_t_reg_rtx ())); +}) + ;; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ;; SImode shift left @@ -10720,6 +10822,53 @@ label: operands[0] = gen_reg_rtx (SImode); }) +;; Store T bit as MSB in a reg. +;; T = 0: 0x00000000 -> reg +;; T = 1: 0x80000000 -> reg +(define_insn_and_split "*movt_msb" + [(set (match_operand:SI 0 "arith_reg_dest") + (mult:SI (match_operand:SI 1 "t_reg_operand") + (const_int -2147483648))) ;; 0xffffffff80000000 + (clobber (reg:SI T_REG))] + "TARGET_SH1" + "#" + "&& 1" + [(set (match_dup 0) (ashift:SI (reg:SI T_REG) (const_int 31)))]) + +;; Store inverted T bit as MSB in a reg. +;; T = 0: 0x80000000 -> reg +;; T = 1: 0x00000000 -> reg +;; On SH2A we can get away without clobbering the T_REG. +(define_insn_and_split "*negt_msb" + [(set (match_operand:SI 0 "arith_reg_dest") + (match_operand:SI 1 "negt_reg_shl31_operand"))] + "TARGET_SH2A" + "#" + "&& can_create_pseudo_p ()" + [(const_int 0)] +{ + rtx tmp = gen_reg_rtx (SImode); + emit_insn (gen_movrt (tmp, get_t_reg_rtx ())); + emit_insn (gen_rotrsi3 (operands[0], tmp, const1_rtx)); + DONE; +}) + +(define_insn_and_split "*negt_msb" + [(set (match_operand:SI 0 "arith_reg_dest") + (match_operand:SI 1 "negt_reg_shl31_operand")) + (clobber (reg:SI T_REG))] + "TARGET_SH1 && !TARGET_SH2A" + "#" + "&& can_create_pseudo_p ()" + [(const_int 0)] +{ + rtx tmp = gen_reg_rtx (SImode); + emit_move_insn (tmp, get_t_reg_rtx ()); + emit_insn (gen_cmpeqsi_t (tmp, const0_rtx)); + emit_insn (gen_rotcr (operands[0], tmp, get_t_reg_rtx ())); + DONE; +}) + ;; The *cset_zero patterns convert optimizations such as ;; "if (test) x = 0;" to "x &= -(test == 0);" ;; back to conditional branch sequences if zero-displacement branches diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 4d4162c86214..dfbab8205cbc 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,13 @@ +2012-09-25 Oleg Endo + + PR target/54089 + * gcc.target/sh/pr54089-1.c (test_15, test_16, test_17, test_18, + test_19, test_20, test_21, test_22, test_23): New functions. + * gcc.target/sh/pr54089-4.c: New. + * gcc.target/sh/pr54089-5.c: New. + * gcc.target/sh/pr54089-6.c: New. + * gcc.target/sh/pr54089-7.c: New. + 2012-09-25 Richard Guenther PR lto/54625 diff --git a/gcc/testsuite/gcc.target/sh/pr54089-1.c b/gcc/testsuite/gcc.target/sh/pr54089-1.c index 77924554f38e..399b0a53e22c 100644 --- a/gcc/testsuite/gcc.target/sh/pr54089-1.c +++ b/gcc/testsuite/gcc.target/sh/pr54089-1.c @@ -2,7 +2,7 @@ /* { dg-do compile { target "sh*-*-*" } } */ /* { dg-options "-O1" } */ /* { dg-skip-if "" { "sh*-*-*" } { "-m5*"} { "" } } */ -/* { dg-final { scan-assembler-times "rotcr" 15 } } */ +/* { dg-final { scan-assembler-times "rotcr" 24 } } */ /* { dg-final { scan-assembler-times "shll\t" 1 } } */ typedef char bool; @@ -109,3 +109,66 @@ test_14 (unsigned int a, int b) bool r = b < 0; return ((a >> 1) | (r << 31)); } + +unsigned int +test_15 (unsigned int a, int b, int c) +{ + bool r = b != c; + return ((a >> 1) | (r << 31)); +} + +unsigned int +test_16 (unsigned int a, int b, int c) +{ + bool r = b != c; + return ((a >> 2) | (r << 31)); +} + +unsigned int +test_17 (unsigned int a, int b, int c) +{ + bool r = b != c; + return ((a >> 3) | (r << 31)); +} + +unsigned int +test_18 (unsigned int a, int b, int c) +{ + bool r = b != c; + return ((a >> 4) | (r << 31)); +} + +unsigned int +test_19 (unsigned int a, int b, int c) +{ + bool r = b != c; + return ((a >> 5) | (r << 31)); +} + +unsigned int +test_20 (unsigned int a, int b, int c) +{ + bool r = b != c; + return ((a >> 6) | (r << 31)); +} + +unsigned int +test_21 (unsigned int a, int b, int c) +{ + bool r = b != c; + return ((a >> 7) | (r << 31)); +} + +unsigned int +test_22 (unsigned int a, int b, int c) +{ + bool r = b != c; + return ((a >> 8) | (r << 31)); +} + +unsigned int +test_23 (unsigned int a, int b, int c) +{ + bool r = b != c; + return ((a >> 31) | (r << 31)); +} diff --git a/gcc/testsuite/gcc.target/sh/pr54089-4.c b/gcc/testsuite/gcc.target/sh/pr54089-4.c new file mode 100644 index 000000000000..4617c3a5f1db --- /dev/null +++ b/gcc/testsuite/gcc.target/sh/pr54089-4.c @@ -0,0 +1,15 @@ +/* Check that the rotcr instruction is generated when shifting the + negated T bit on non-SH2A. */ +/* { dg-do compile { target "sh*-*-*" } } */ +/* { dg-options "-O1" } */ +/* { dg-skip-if "" { "sh*-*-*" } { "-m5*" "-m2a*" } { "" } } */ +/* { dg-final { scan-assembler-times "rotcr" 1 } } */ +/* { dg-final { scan-assembler-times "tst" 1 } } */ +/* { dg-final { scan-assembler-times "movt" 1 } } */ + +int +test_00 (int a, int b) +{ + int r = a != b; + return r << 31; +} diff --git a/gcc/testsuite/gcc.target/sh/pr54089-5.c b/gcc/testsuite/gcc.target/sh/pr54089-5.c new file mode 100644 index 000000000000..f781acabf8e3 --- /dev/null +++ b/gcc/testsuite/gcc.target/sh/pr54089-5.c @@ -0,0 +1,14 @@ +/* Check that the movrt rotr instruction sequence is generated when shifting + the negated T bit on SH2A. */ +/* { dg-do compile { target "sh*-*-*" } } */ +/* { dg-options "-O1" } */ +/* { dg-skip-if "" { "sh*-*-*" } { "*" } { "-m2a*" } } */ +/* { dg-final { scan-assembler-times "movrt" 1 } } */ +/* { dg-final { scan-assembler-times "rotr" 1 } } */ + +int +test_00 (int a, int b) +{ + int r = a != b; + return r << 31; +} diff --git a/gcc/testsuite/gcc.target/sh/pr54089-6.c b/gcc/testsuite/gcc.target/sh/pr54089-6.c new file mode 100644 index 000000000000..a12a0e93044c --- /dev/null +++ b/gcc/testsuite/gcc.target/sh/pr54089-6.c @@ -0,0 +1,30 @@ +/* Check that the rotr and rotl instructions are generated. */ +/* { dg-do compile { target "sh*-*-*" } } */ +/* { dg-options "-O1" } */ +/* { dg-skip-if "" { "sh*-*-*" } { "-m5*" } { "" } } */ +/* { dg-final { scan-assembler-times "rotr" 2 } } */ +/* { dg-final { scan-assembler-times "rotl" 2 } } */ + +int +test_00 (int a) +{ + return (a << 1) | ((a >> 31) & 1); +} + +int +test_01 (int a) +{ + return (a << 1) | ((unsigned int)a >> 31); +} + +int +test_02 (int a) +{ + return ((unsigned int)a >> 1) | (a << 31); +} + +int +test_03 (int a) +{ + return ((a >> 1) & 0x7FFFFFFF) | (a << 31); +} diff --git a/gcc/testsuite/gcc.target/sh/pr54089-7.c b/gcc/testsuite/gcc.target/sh/pr54089-7.c new file mode 100644 index 000000000000..40ca8219a2fb --- /dev/null +++ b/gcc/testsuite/gcc.target/sh/pr54089-7.c @@ -0,0 +1,63 @@ +/* Check that the rotcr instruction is generated. */ +/* { dg-do compile { target "sh*-*-*" } } */ +/* { dg-options "-O1" } */ +/* { dg-skip-if "" { "sh*-*-*" } { "-m5*"} { "" } } */ +/* { dg-final { scan-assembler-times "rotcr" 4 } } */ +/* { dg-final { scan-assembler-not "movt" } } */ +/* { dg-final { scan-assembler-not "or\t" } } */ +/* { dg-final { scan-assembler-not "rotr" } } */ +/* { dg-final { scan-assembler-not "and" } } */ + +typedef char bool; + +int +test_00 (int* a, int* b) +{ + int i; + unsigned int r = 0; + for (i = 0; i < 16; ++i) + { + bool t = a[i] == b[i]; + r = (t << 31) | (r >> 1); + } + return r; +} + +int +test_01 (int* a, int* b) +{ + int i; + unsigned int r = 0; + for (i = 0; i < 16; ++i) + { + bool t = a[i] == b[i]; + r = (t << 31) | (r >> 2); + } + return r; +} + +int +test_02 (int* a, int* b) +{ + int i; + unsigned int r = 0; + for (i = 0; i < 16; ++i) + { + bool t = a[i] == b[i]; + r = (t << 31) | (r >> 3); + } + return r; +} + +unsigned int +test_03 (const bool* a) +{ + int i; + unsigned int r = 0; + for (i = 0; i < 32; ++i) + { + bool t = a[i]; + r = (t << 31) | (r >> 1); + } + return r; +}