| Index: gcc/gcc/ifcvt.c
|
| diff --git a/gcc/gcc/ifcvt.c b/gcc/gcc/ifcvt.c
|
| index a6db2114be67269d102715a2d694e425d9668ba1..e9f60ed5e8bd33ff4951781d55329630c34a6196 100644
|
| --- a/gcc/gcc/ifcvt.c
|
| +++ b/gcc/gcc/ifcvt.c
|
| @@ -1,5 +1,5 @@
|
| /* If-conversion support.
|
| - Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
|
| + Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2010
|
| Free Software Foundation, Inc.
|
|
|
| This file is part of GCC.
|
| @@ -47,9 +47,6 @@
|
| #include "vecprim.h"
|
| #include "dbgcnt.h"
|
|
|
| -#ifndef HAVE_conditional_execution
|
| -#define HAVE_conditional_execution 0
|
| -#endif
|
| #ifndef HAVE_conditional_move
|
| #define HAVE_conditional_move 0
|
| #endif
|
| @@ -62,9 +59,6 @@
|
| #ifndef HAVE_trap
|
| #define HAVE_trap 0
|
| #endif
|
| -#ifndef HAVE_conditional_trap
|
| -#define HAVE_conditional_trap 0
|
| -#endif
|
|
|
| #ifndef MAX_CONDITIONAL_EXECUTE
|
| #define MAX_CONDITIONAL_EXECUTE \
|
| @@ -197,7 +191,7 @@ first_active_insn (basic_block bb)
|
| insn = NEXT_INSN (insn);
|
| }
|
|
|
| - while (NOTE_P (insn))
|
| + while (NOTE_P (insn) || DEBUG_INSN_P (insn))
|
| {
|
| if (insn == BB_END (bb))
|
| return NULL_RTX;
|
| @@ -220,6 +214,7 @@ last_active_insn (basic_block bb, int skip_use_p)
|
|
|
| while (NOTE_P (insn)
|
| || JUMP_P (insn)
|
| + || DEBUG_INSN_P (insn)
|
| || (skip_use_p
|
| && NONJUMP_INSN_P (insn)
|
| && GET_CODE (PATTERN (insn)) == USE))
|
| @@ -272,7 +267,7 @@ cond_exec_process_insns (ce_if_block_t *ce_info ATTRIBUTE_UNUSED,
|
|
|
| for (insn = start; ; insn = NEXT_INSN (insn))
|
| {
|
| - if (NOTE_P (insn))
|
| + if (NOTE_P (insn) || DEBUG_INSN_P (insn))
|
| goto insn_done;
|
|
|
| gcc_assert(NONJUMP_INSN_P (insn) || CALL_P (insn));
|
| @@ -908,12 +903,12 @@ noce_try_store_flag (struct noce_if_info *if_info)
|
| int reversep;
|
| rtx target, seq;
|
|
|
| - if (GET_CODE (if_info->b) == CONST_INT
|
| + if (CONST_INT_P (if_info->b)
|
| && INTVAL (if_info->b) == STORE_FLAG_VALUE
|
| && if_info->a == const0_rtx)
|
| reversep = 0;
|
| else if (if_info->b == const0_rtx
|
| - && GET_CODE (if_info->a) == CONST_INT
|
| + && CONST_INT_P (if_info->a)
|
| && INTVAL (if_info->a) == STORE_FLAG_VALUE
|
| && (reversed_comparison_code (if_info->cond, if_info->jump)
|
| != UNKNOWN))
|
| @@ -955,8 +950,8 @@ noce_try_store_flag_constants (struct noce_if_info *if_info)
|
| int normalize, can_reverse;
|
| enum machine_mode mode;
|
|
|
| - if (GET_CODE (if_info->a) == CONST_INT
|
| - && GET_CODE (if_info->b) == CONST_INT)
|
| + if (CONST_INT_P (if_info->a)
|
| + && CONST_INT_P (if_info->b))
|
| {
|
| mode = GET_MODE (if_info->x);
|
| ifalse = INTVAL (if_info->a);
|
| @@ -1331,11 +1326,15 @@ noce_try_cmove_arith (struct noce_if_info *if_info)
|
| /* ??? FIXME: Magic number 5. */
|
| if (cse_not_expected
|
| && MEM_P (a) && MEM_P (b)
|
| + && MEM_ADDR_SPACE (a) == MEM_ADDR_SPACE (b)
|
| && if_info->branch_cost >= 5)
|
| {
|
| + enum machine_mode address_mode
|
| + = targetm.addr_space.address_mode (MEM_ADDR_SPACE (a));
|
| +
|
| a = XEXP (a, 0);
|
| b = XEXP (b, 0);
|
| - x = gen_reg_rtx (Pmode);
|
| + x = gen_reg_rtx (address_mode);
|
| is_mem = 1;
|
| }
|
|
|
| @@ -1484,6 +1483,9 @@ noce_try_cmove_arith (struct noce_if_info *if_info)
|
| set_mem_align (tmp,
|
| MIN (MEM_ALIGN (if_info->a), MEM_ALIGN (if_info->b)));
|
|
|
| + gcc_assert (MEM_ADDR_SPACE (if_info->a) == MEM_ADDR_SPACE (if_info->b));
|
| + set_mem_addr_space (tmp, MEM_ADDR_SPACE (if_info->a));
|
| +
|
| noce_emit_move_insn (if_info->x, tmp);
|
| }
|
| else if (target != x)
|
| @@ -1541,7 +1543,7 @@ noce_get_alt_condition (struct noce_if_info *if_info, rtx target,
|
| make equivalent types of changes) to get the constants we need
|
| if they're off by one in the right direction. */
|
|
|
| - if (GET_CODE (target) == CONST_INT)
|
| + if (CONST_INT_P (target))
|
| {
|
| enum rtx_code code = GET_CODE (if_info->cond);
|
| rtx op_a = XEXP (if_info->cond, 0);
|
| @@ -1551,21 +1553,22 @@ noce_get_alt_condition (struct noce_if_info *if_info, rtx target,
|
| /* First, look to see if we put a constant in a register. */
|
| prev_insn = prev_nonnote_insn (if_info->cond_earliest);
|
| if (prev_insn
|
| - && BLOCK_NUM (prev_insn) == BLOCK_NUM (if_info->cond_earliest)
|
| + && BLOCK_FOR_INSN (prev_insn)
|
| + == BLOCK_FOR_INSN (if_info->cond_earliest)
|
| && INSN_P (prev_insn)
|
| && GET_CODE (PATTERN (prev_insn)) == SET)
|
| {
|
| rtx src = find_reg_equal_equiv_note (prev_insn);
|
| if (!src)
|
| src = SET_SRC (PATTERN (prev_insn));
|
| - if (GET_CODE (src) == CONST_INT)
|
| + if (CONST_INT_P (src))
|
| {
|
| if (rtx_equal_p (op_a, SET_DEST (PATTERN (prev_insn))))
|
| op_a = src;
|
| else if (rtx_equal_p (op_b, SET_DEST (PATTERN (prev_insn))))
|
| op_b = src;
|
|
|
| - if (GET_CODE (op_a) == CONST_INT)
|
| + if (CONST_INT_P (op_a))
|
| {
|
| rtx tmp = op_a;
|
| op_a = op_b;
|
| @@ -1577,7 +1580,7 @@ noce_get_alt_condition (struct noce_if_info *if_info, rtx target,
|
|
|
| /* Now, look to see if we can get the right constant by
|
| adjusting the conditional. */
|
| - if (GET_CODE (op_b) == CONST_INT)
|
| + if (CONST_INT_P (op_b))
|
| {
|
| HOST_WIDE_INT desired_val = INTVAL (target);
|
| HOST_WIDE_INT actual_val = INTVAL (op_b);
|
| @@ -1746,13 +1749,16 @@ noce_try_minmax (struct noce_if_info *if_info)
|
| return TRUE;
|
| }
|
|
|
| -/* Convert "if (a < 0) x = -a; else x = a;" to "x = abs(a);", etc. */
|
| +/* Convert "if (a < 0) x = -a; else x = a;" to "x = abs(a);",
|
| + "if (a < 0) x = ~a; else x = a;" to "x = one_cmpl_abs(a);",
|
| + etc. */
|
|
|
| static int
|
| noce_try_abs (struct noce_if_info *if_info)
|
| {
|
| rtx cond, earliest, target, seq, a, b, c;
|
| int negate;
|
| + bool one_cmpl = false;
|
|
|
| /* Reject modes with signed zeros. */
|
| if (HONOR_SIGNED_ZEROS (GET_MODE (if_info->x)))
|
| @@ -1770,6 +1776,17 @@ noce_try_abs (struct noce_if_info *if_info)
|
| c = a; a = b; b = c;
|
| negate = 1;
|
| }
|
| + else if (GET_CODE (a) == NOT && rtx_equal_p (XEXP (a, 0), b))
|
| + {
|
| + negate = 0;
|
| + one_cmpl = true;
|
| + }
|
| + else if (GET_CODE (b) == NOT && rtx_equal_p (XEXP (b, 0), a))
|
| + {
|
| + c = a; a = b; b = c;
|
| + negate = 1;
|
| + one_cmpl = true;
|
| + }
|
| else
|
| return FALSE;
|
|
|
| @@ -1794,7 +1811,7 @@ noce_try_abs (struct noce_if_info *if_info)
|
| {
|
| rtx set, insn = prev_nonnote_insn (earliest);
|
| if (insn
|
| - && BLOCK_NUM (insn) == BLOCK_NUM (earliest)
|
| + && BLOCK_FOR_INSN (insn) == BLOCK_FOR_INSN (earliest)
|
| && (set = single_set (insn))
|
| && rtx_equal_p (SET_DEST (set), c))
|
| {
|
| @@ -1841,13 +1858,23 @@ noce_try_abs (struct noce_if_info *if_info)
|
| }
|
|
|
| start_sequence ();
|
| -
|
| - target = expand_abs_nojump (GET_MODE (if_info->x), b, if_info->x, 1);
|
| + if (one_cmpl)
|
| + target = expand_one_cmpl_abs_nojump (GET_MODE (if_info->x), b,
|
| + if_info->x);
|
| + else
|
| + target = expand_abs_nojump (GET_MODE (if_info->x), b, if_info->x, 1);
|
|
|
| /* ??? It's a quandary whether cmove would be better here, especially
|
| for integers. Perhaps combine will clean things up. */
|
| if (target && negate)
|
| - target = expand_simple_unop (GET_MODE (target), NEG, target, if_info->x, 0);
|
| + {
|
| + if (one_cmpl)
|
| + target = expand_simple_unop (GET_MODE (target), NOT, target,
|
| + if_info->x, 0);
|
| + else
|
| + target = expand_simple_unop (GET_MODE (target), NEG, target,
|
| + if_info->x, 0);
|
| + }
|
|
|
| if (! target)
|
| {
|
| @@ -1976,7 +2003,7 @@ noce_try_bitop (struct noce_if_info *if_info)
|
| if (GET_CODE (cond) == ZERO_EXTRACT)
|
| {
|
| if (XEXP (cond, 1) != const1_rtx
|
| - || GET_CODE (XEXP (cond, 2)) != CONST_INT
|
| + || !CONST_INT_P (XEXP (cond, 2))
|
| || ! rtx_equal_p (x, XEXP (cond, 0)))
|
| return FALSE;
|
| bitnum = INTVAL (XEXP (cond, 2));
|
| @@ -1994,7 +2021,7 @@ noce_try_bitop (struct noce_if_info *if_info)
|
| {
|
| /* Check for "if (X & C) x = x op C". */
|
| if (! rtx_equal_p (x, XEXP (a, 0))
|
| - || GET_CODE (XEXP (a, 1)) != CONST_INT
|
| + || !CONST_INT_P (XEXP (a, 1))
|
| || (INTVAL (XEXP (a, 1)) & GET_MODE_MASK (mode))
|
| != (unsigned HOST_WIDE_INT) 1 << bitnum)
|
| return FALSE;
|
| @@ -2020,7 +2047,7 @@ noce_try_bitop (struct noce_if_info *if_info)
|
| {
|
| /* Check for "if (X & C) x &= ~C". */
|
| if (! rtx_equal_p (x, XEXP (a, 0))
|
| - || GET_CODE (XEXP (a, 1)) != CONST_INT
|
| + || !CONST_INT_P (XEXP (a, 1))
|
| || (INTVAL (XEXP (a, 1)) & GET_MODE_MASK (mode))
|
| != (~((HOST_WIDE_INT) 1 << bitnum) & GET_MODE_MASK (mode)))
|
| return FALSE;
|
| @@ -2145,7 +2172,7 @@ noce_mem_write_may_trap_or_fault_p (const_rtx mem)
|
| addr = XEXP (addr, 1);
|
| break;
|
| case PLUS:
|
| - if (GET_CODE (XEXP (addr, 1)) == CONST_INT)
|
| + if (CONST_INT_P (XEXP (addr, 1)))
|
| addr = XEXP (addr, 0);
|
| else
|
| return false;
|
| @@ -2259,24 +2286,25 @@ noce_process_if_block (struct noce_if_info *if_info)
|
| else
|
| {
|
| insn_b = prev_nonnote_insn (if_info->cond_earliest);
|
| + while (insn_b && DEBUG_INSN_P (insn_b))
|
| + insn_b = prev_nonnote_insn (insn_b);
|
| /* We're going to be moving the evaluation of B down from above
|
| COND_EARLIEST to JUMP. Make sure the relevant data is still
|
| intact. */
|
| if (! insn_b
|
| - || BLOCK_NUM (insn_b) != BLOCK_NUM (if_info->cond_earliest)
|
| + || BLOCK_FOR_INSN (insn_b) != BLOCK_FOR_INSN (if_info->cond_earliest)
|
| || !NONJUMP_INSN_P (insn_b)
|
| || (set_b = single_set (insn_b)) == NULL_RTX
|
| || ! rtx_equal_p (x, SET_DEST (set_b))
|
| || ! noce_operand_ok (SET_SRC (set_b))
|
| || reg_overlap_mentioned_p (x, SET_SRC (set_b))
|
| - || modified_between_p (SET_SRC (set_b),
|
| - PREV_INSN (if_info->cond_earliest), jump)
|
| + || modified_between_p (SET_SRC (set_b), insn_b, jump)
|
| /* Likewise with X. In particular this can happen when
|
| noce_get_condition looks farther back in the instruction
|
| stream than one might expect. */
|
| || reg_overlap_mentioned_p (x, cond)
|
| || reg_overlap_mentioned_p (x, a)
|
| - || modified_between_p (x, PREV_INSN (if_info->cond_earliest), jump))
|
| + || modified_between_p (x, insn_b, jump))
|
| insn_b = set_b = NULL_RTX;
|
| }
|
|
|
| @@ -2301,8 +2329,8 @@ noce_process_if_block (struct noce_if_info *if_info)
|
| return FALSE;
|
|
|
| if (GET_CODE (x) == ZERO_EXTRACT
|
| - && (GET_CODE (XEXP (x, 1)) != CONST_INT
|
| - || GET_CODE (XEXP (x, 2)) != CONST_INT))
|
| + && (!CONST_INT_P (XEXP (x, 1))
|
| + || !CONST_INT_P (XEXP (x, 2))))
|
| return FALSE;
|
|
|
| x = gen_reg_rtx (GET_MODE (GET_CODE (x) == STRICT_LOW_PART
|
| @@ -2396,7 +2424,7 @@ noce_process_if_block (struct noce_if_info *if_info)
|
| if (HAVE_conditional_move
|
| && noce_try_cmove (if_info))
|
| goto success;
|
| - if (! HAVE_conditional_execution)
|
| + if (! targetm.have_conditional_execution ())
|
| {
|
| if (noce_try_store_flag_constants (if_info))
|
| goto success;
|
| @@ -2484,7 +2512,7 @@ check_cond_move_block (basic_block bb, rtx *vals, VEC (int, heap) **regs, rtx co
|
| {
|
| rtx set, dest, src;
|
|
|
| - if (!INSN_P (insn) || JUMP_P (insn))
|
| + if (!NONDEBUG_INSN_P (insn) || JUMP_P (insn))
|
| continue;
|
| set = single_set (insn);
|
| if (!set)
|
| @@ -2562,7 +2590,8 @@ cond_move_convert_if_block (struct noce_if_info *if_infop,
|
| rtx set, target, dest, t, e;
|
| unsigned int regno;
|
|
|
| - if (!INSN_P (insn) || JUMP_P (insn))
|
| + /* ??? Maybe emit conditional debug insn? */
|
| + if (!NONDEBUG_INSN_P (insn) || JUMP_P (insn))
|
| continue;
|
| set = single_set (insn);
|
| gcc_assert (set && REG_P (SET_DEST (set)));
|
| @@ -3039,16 +3068,17 @@ find_if_header (basic_block test_bb, int pass)
|
| && noce_find_if_block (test_bb, then_edge, else_edge, pass))
|
| goto success;
|
|
|
| - if (HAVE_conditional_execution && reload_completed
|
| + if (targetm.have_conditional_execution () && reload_completed
|
| && cond_exec_find_if_block (&ce_info))
|
| goto success;
|
|
|
| - if (HAVE_trap && HAVE_conditional_trap
|
| + if (HAVE_trap
|
| + && optab_handler (ctrap_optab, word_mode)->insn_code != CODE_FOR_nothing
|
| && find_cond_trap (test_bb, then_edge, else_edge))
|
| goto success;
|
|
|
| if (dom_info_state (CDI_POST_DOMINATORS) >= DOM_NO_FAST_QUERY
|
| - && (! HAVE_conditional_execution || reload_completed))
|
| + && (! targetm.have_conditional_execution () || reload_completed))
|
| {
|
| if (find_if_case_1 (test_bb, then_edge, else_edge))
|
| goto success;
|
| @@ -3122,6 +3152,7 @@ block_jumps_and_fallthru_p (basic_block cur_bb, basic_block target_bb)
|
|
|
| if (INSN_P (insn)
|
| && !JUMP_P (insn)
|
| + && !DEBUG_INSN_P (insn)
|
| && GET_CODE (PATTERN (insn)) != USE
|
| && GET_CODE (PATTERN (insn)) != CLOBBER)
|
| n_insns++;
|
| @@ -3154,7 +3185,7 @@ cond_exec_find_if_block (struct ce_if_block * ce_info)
|
|
|
| /* We only ever should get here after reload,
|
| and only if we have conditional execution. */
|
| - gcc_assert (HAVE_conditional_execution && reload_completed);
|
| + gcc_assert (targetm.have_conditional_execution () && reload_completed);
|
|
|
| /* Discover if any fall through predecessors of the current test basic block
|
| were && tests (which jump to the else block) or || tests (which jump to
|
| @@ -3735,7 +3766,7 @@ find_if_case_2 (basic_block test_bb, edge then_edge, edge else_edge)
|
| test_bb->index, else_bb->index);
|
|
|
| /* ELSE is small. */
|
| - if (! cheap_bb_rtx_cost_p (else_bb,
|
| + if (! cheap_bb_rtx_cost_p (else_bb,
|
| COSTS_N_INSNS (BRANCH_COST (optimize_bb_for_speed_p (else_edge->src),
|
| predictable_edge_p (else_edge)))))
|
| return FALSE;
|
| @@ -3782,6 +3813,8 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb,
|
| basic_block other_bb, basic_block new_dest, int reversep)
|
| {
|
| rtx head, end, jump, earliest = NULL_RTX, old_dest, new_label = NULL_RTX;
|
| + /* Number of pending changes. */
|
| + int n_validated_changes = 0;
|
|
|
| jump = BB_END (test_bb);
|
|
|
| @@ -3789,6 +3822,9 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb,
|
| head = BB_HEAD (merge_bb);
|
| end = BB_END (merge_bb);
|
|
|
| + while (DEBUG_INSN_P (end) && end != head)
|
| + end = PREV_INSN (end);
|
| +
|
| /* If merge_bb ends with a tablejump, predicating/moving insn's
|
| into test_bb and then deleting merge_bb will result in the jumptable
|
| that follows merge_bb being removed along with merge_bb and then we
|
| @@ -3798,6 +3834,8 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb,
|
|
|
| if (LABEL_P (head))
|
| head = NEXT_INSN (head);
|
| + while (DEBUG_INSN_P (head) && head != end)
|
| + head = NEXT_INSN (head);
|
| if (NOTE_P (head))
|
| {
|
| if (head == end)
|
| @@ -3806,6 +3844,8 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb,
|
| goto no_body;
|
| }
|
| head = NEXT_INSN (head);
|
| + while (DEBUG_INSN_P (head) && head != end)
|
| + head = NEXT_INSN (head);
|
| }
|
|
|
| if (JUMP_P (end))
|
| @@ -3816,12 +3856,14 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb,
|
| goto no_body;
|
| }
|
| end = PREV_INSN (end);
|
| + while (DEBUG_INSN_P (end) && end != head)
|
| + end = PREV_INSN (end);
|
| }
|
|
|
| /* Disable handling dead code by conditional execution if the machine needs
|
| to do anything funny with the tests, etc. */
|
| #ifndef IFCVT_MODIFY_TESTS
|
| - if (HAVE_conditional_execution)
|
| + if (targetm.have_conditional_execution ())
|
| {
|
| /* In the conditional execution case, we have things easy. We know
|
| the condition is reversible. We don't have to check life info
|
| @@ -3850,14 +3892,17 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb,
|
| prob_val = GEN_INT (REG_BR_PROB_BASE - INTVAL (prob_val));
|
| }
|
|
|
| - if (! cond_exec_process_insns ((ce_if_block_t *)0, head, end, cond,
|
| - prob_val, 0))
|
| - goto cancel;
|
| + if (cond_exec_process_insns (NULL, head, end, cond, prob_val, 0)
|
| + && verify_changes (0))
|
| + n_validated_changes = num_validated_changes ();
|
| + else
|
| + cancel_changes (0);
|
|
|
| earliest = jump;
|
| }
|
| - else
|
| #endif
|
| + /* Try the NCE path if the CE path did not result in any changes. */
|
| + if (n_validated_changes == 0)
|
| {
|
| /* In the non-conditional execution case, we have to verify that there
|
| are no trapping operations, no calls, no references to memory, and
|
| @@ -3873,7 +3918,7 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb,
|
| {
|
| if (CALL_P (insn))
|
| return FALSE;
|
| - if (INSN_P (insn))
|
| + if (NONDEBUG_INSN_P (insn))
|
| {
|
| if (may_trap_p (PATTERN (insn)))
|
| return FALSE;
|
| @@ -3919,7 +3964,7 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb,
|
|
|
| FOR_BB_INSNS (merge_bb, insn)
|
| {
|
| - if (INSN_P (insn))
|
| + if (NONDEBUG_INSN_P (insn))
|
| {
|
| unsigned int uid = INSN_UID (insn);
|
| df_ref *def_rec;
|
| @@ -3943,11 +3988,11 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb,
|
| fail = 1;
|
| }
|
| }
|
| -
|
| +
|
| /* For TEST, we're interested in a range of insns, not a whole block.
|
| Moreover, we're interested in the insns live from OTHER_BB. */
|
| -
|
| - /* The loop below takes the set of live registers
|
| +
|
| + /* The loop below takes the set of live registers
|
| after JUMP, and calculates the live set before EARLIEST. */
|
| bitmap_copy (test_live, df_get_live_in (other_bb));
|
| df_simulate_initialize_backwards (test_bb, test_live);
|
| @@ -3997,8 +4042,10 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb,
|
| goto cancel;
|
| }
|
|
|
| - if (! apply_change_group ())
|
| - return FALSE;
|
| + if (verify_changes (n_validated_changes))
|
| + confirm_change_group ();
|
| + else
|
| + goto cancel;
|
|
|
| if (other_bb != new_dest)
|
| {
|
| @@ -4112,7 +4159,7 @@ if_convert (void)
|
| FOR_EACH_BB (bb)
|
| {
|
| basic_block new_bb;
|
| - while (!df_get_bb_dirty (bb)
|
| + while (!df_get_bb_dirty (bb)
|
| && (new_bb = find_if_header (bb, pass)) != NULL)
|
| bb = new_bb;
|
| }
|
|
|