diff options
author | Steven Bosscher <steven@gcc.gnu.org> | 2012-07-05 10:12:14 +0000 |
---|---|---|
committer | Steven Bosscher <steven@gcc.gnu.org> | 2012-07-05 10:12:14 +0000 |
commit | 04a40cb9599281348f70d6758f77da7a4cd4caa5 (patch) | |
tree | 5ba82ee1fa687f8bbcb0b24d8f46b093ad127454 | |
parent | 12c0399e136f1e56d6437fff5cd6e01f5f7ec284 (diff) | |
download | gcc-04a40cb9599281348f70d6758f77da7a4cd4caa5.zip gcc-04a40cb9599281348f70d6758f77da7a4cd4caa5.tar.gz gcc-04a40cb9599281348f70d6758f77da7a4cd4caa5.tar.bz2 |
expr.c (try_casesi): Remove bogus ATTRIBUTE_UNUSED markers.
gcc/
* expr.c (try_casesi): Remove bogus ATTRIBUTE_UNUSED markers.
* stmt.c (dump_case_nodes): New.
(expand_case): Split out code generation parts into new functions.
(expand_switch_as_decision_tree_p): Split out from expand_case.
(emit_case_decision_tree): Likewise.
(emit_case_dispatch_table): Likewise.
testsuite/
* gcc.c-torture/compile/20000326-1.c: Fix to not optimize to empty.
From-SVN: r189285
-rw-r--r-- | gcc/ChangeLog | 9 | ||||
-rw-r--r-- | gcc/expr.c | 3 | ||||
-rw-r--r-- | gcc/stmt.c | 559 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 4 | ||||
-rw-r--r-- | gcc/testsuite/gcc.c-torture/compile/20000326-1.c | 13 |
5 files changed, 348 insertions, 240 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 2a891e3c..091bda2 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,12 @@ +2012-07-05 Steven Bosscher <steven@gcc.gnu.org> + + * expr.c (try_casesi): Remove bogus ATTRIBUTE_UNUSED markers. + * stmt.c (dump_case_nodes): New. + (expand_case): Split out code generation parts into new functions. + (expand_switch_as_decision_tree_p): Split out from expand_case. + (emit_case_decision_tree): Likewise. + (emit_case_dispatch_table): Likewise. + 2012-07-05 Matthew Gretton-Dann <matthew.gretton-dann@arm.com> * config/arm/iterators.md (SDF): New mode iterator. @@ -10850,8 +10850,7 @@ do_store_flag (sepops ops, rtx target, enum machine_mode mode) 0 otherwise (i.e. if there is no casesi instruction). */ int try_casesi (tree index_type, tree index_expr, tree minval, tree range, - rtx table_label ATTRIBUTE_UNUSED, rtx default_label, - rtx fallback_label ATTRIBUTE_UNUSED) + rtx table_label, rtx default_label, rtx fallback_label) { struct expand_operand ops[5]; enum machine_mode index_mode = SImode; @@ -1716,6 +1716,35 @@ add_case_node (struct case_node *head, tree type, tree low, tree high, return r; } +/* Dump ROOT, a list or tree of case nodes, to file. */ + +static void +dump_case_nodes (FILE *f, struct case_node *root, + int indent_step, int indent_level) +{ + HOST_WIDE_INT low, high; + + if (root == 0) + return; + indent_level++; + + dump_case_nodes (f, root->left, indent_step, indent_level); + + low = tree_low_cst (root->low, 0); + high = tree_low_cst (root->high, 0); + + fputs (";; ", f); + if (high == low) + fprintf(f, "%*s" HOST_WIDE_INT_PRINT_DEC, + indent_step * indent_level, "", low); + else + fprintf(f, "%*s" HOST_WIDE_INT_PRINT_DEC " ... " HOST_WIDE_INT_PRINT_DEC, + indent_step * indent_level, "", low, high); + fputs ("\n", f); + + dump_case_nodes (f, root->right, indent_step, indent_level); +} + #ifndef HAVE_casesi #define HAVE_casesi 0 #endif @@ -1738,276 +1767,344 @@ case_values_threshold (void) return threshold; } -/* Terminate a case (Pascal/Ada) or switch (C) statement - in which ORIG_INDEX is the expression to be tested. - If ORIG_TYPE is not NULL, it is the original ORIG_INDEX - type as given in the source before any compiler conversions. - Generate the code to test it and jump to the right place. */ +/* Return true if a switch should be expanded as a decision tree. + RANGE is the difference between highest and lowest case. + UNIQ is number of unique case node targets, not counting the default case. + COUNT is the number of comparisons needed, not counting the default case. */ -void -expand_case (gimple stmt) +static bool +expand_switch_as_decision_tree_p (tree range, + unsigned int uniq ATTRIBUTE_UNUSED, + unsigned int count) { - tree minval = NULL_TREE, maxval = NULL_TREE, range = NULL_TREE; - rtx default_label = 0; - struct case_node *n; - unsigned int count, uniq; - rtx index; - rtx table_label; - int ncases; - rtx *labelvec; - int i; - rtx before_case, end, lab; + int max_ratio; + + /* If neither casesi or tablejump is available, or flag_jump_tables + over-ruled us, we really have no choice. */ + if (!HAVE_casesi && !HAVE_tablejump) + return true; + if (!flag_jump_tables) + return true; + + /* If the switch is relatively small such that the cost of one + indirect jump on the target are higher than the cost of a + decision tree, go with the decision tree. + + If range of values is much bigger than number of values, + or if it is too large to represent in a HOST_WIDE_INT, + make a sequence of conditional branches instead of a dispatch. + + The definition of "much bigger" depends on whether we are + optimizing for size or for speed. If the former, the maximum + ratio range/count = 3, because this was found to be the optimal + ratio for size on i686-pc-linux-gnu, see PR11823. The ratio + 10 is much older, and was probably selected after an extensive + benchmarking investigation on numerous platforms. Or maybe it + just made sense to someone at some point in the history of GCC, + who knows... */ + max_ratio = optimize_insn_for_size_p () ? 3 : 10; + if (count < case_values_threshold () + || ! host_integerp (range, /*pos=*/1) + || compare_tree_int (range, max_ratio * count) > 0) + return true; - tree index_expr = gimple_switch_index (stmt); - tree index_type = TREE_TYPE (index_expr); - int unsignedp = TYPE_UNSIGNED (index_type); + return false; +} - /* The insn after which the case dispatch should finally - be emitted. Zero for a dummy. */ - rtx start; +/* Generate a decision tree, switching on INDEX_EXPR and jumping to + one of the labels in CASE_LIST or to the DEFAULT_LABEL. + + We generate a binary decision tree to select the appropriate target + code. This is done as follows: - /* A list of case labels; it is first built as a list and it may then - be rearranged into a nearly balanced binary tree. */ - struct case_node *case_list = 0; + If the index is a short or char that we do not have + an insn to handle comparisons directly, convert it to + a full integer now, rather than letting each comparison + generate the conversion. + + Load the index into a register. + + The list of cases is rearranged into a binary tree, + nearly optimal assuming equal probability for each case. + + The tree is transformed into RTL, eliminating redundant + test conditions at the same time. - /* Label to jump to if no case matches. */ - tree default_label_decl = NULL_TREE; + If program flow could reach the end of the decision tree + an unconditional jump to the default code is emitted. - alloc_pool case_node_pool = create_alloc_pool ("struct case_node pool", - sizeof (struct case_node), - 100); + The above process is unaware of the CFG. The caller has to fix up + the CFG itself. This is done in cfgexpand.c. */ + +static void +emit_case_decision_tree (tree index_expr, tree index_type, + struct case_node *case_list, rtx default_label) +{ + rtx index = expand_normal (index_expr); + + if (GET_MODE_CLASS (GET_MODE (index)) == MODE_INT + && ! have_insn_for (COMPARE, GET_MODE (index))) + { + int unsignedp = TYPE_UNSIGNED (index_type); + enum machine_mode wider_mode; + for (wider_mode = GET_MODE (index); wider_mode != VOIDmode; + wider_mode = GET_MODE_WIDER_MODE (wider_mode)) + if (have_insn_for (COMPARE, wider_mode)) + { + index = convert_to_mode (wider_mode, index, unsignedp); + break; + } + } do_pending_stack_adjust (); - /* An ERROR_MARK occurs for various reasons including invalid data type. */ - if (index_type != error_mark_node) + if (MEM_P (index)) { - tree elt; - bitmap label_bitmap; - int stopi = 0; + index = copy_to_reg (index); + if (TREE_CODE (index_expr) == SSA_NAME) + set_reg_attrs_for_decl_rtl (SSA_NAME_VAR (index_expr), index); + } - /* cleanup_tree_cfg removes all SWITCH_EXPR with their index - expressions being INTEGER_CST. */ - gcc_assert (TREE_CODE (index_expr) != INTEGER_CST); + balance_case_nodes (&case_list, NULL); - /* The default case, if ever taken, is the first element. */ - elt = gimple_switch_label (stmt, 0); - if (!CASE_LOW (elt) && !CASE_HIGH (elt)) - { - default_label_decl = CASE_LABEL (elt); - stopi = 1; - } + /* Don't want to include tree-pass.h here. This code will be moved + to a GIMPLE pass for GCC 4.9 anyway, so for now always dump. */ + if (dump_file && 1/*(dump_flags & TDF_DETAILS)*/) + { + int indent_step = ceil_log2 (TYPE_PRECISION (index_type)) + 2; + fprintf (dump_file, ";; Expanding GIMPLE switch as decision tree:\n"); + dump_case_nodes (dump_file, case_list, indent_step, 0); + } - for (i = gimple_switch_num_labels (stmt) - 1; i >= stopi; --i) - { - tree low, high; - elt = gimple_switch_label (stmt, i); - - low = CASE_LOW (elt); - gcc_assert (low); - high = CASE_HIGH (elt); - - /* The canonical from of a case label in GIMPLE is that a simple case - has an empty CASE_HIGH. For the casesi and tablejump expanders, - the back ends want simple cases to have high == low. */ - gcc_assert (! high || tree_int_cst_lt (low, high)); - if (! high) - high = low; - - case_list = add_case_node (case_list, index_type, low, high, - CASE_LABEL (elt), case_node_pool); - } + emit_case_nodes (index, case_list, default_label, index_type); + if (default_label) + emit_jump (default_label); +} +/* Generate a dispatch tabler, switching on INDEX_EXPR and jumping to + one of the labels in CASE_LIST or to the DEFAULT_LABEL. + MINVAL, MAXVAL, and RANGE are the extrema and range of the case + labels in CASE_LIST. - before_case = start = get_last_insn (); - if (default_label_decl) - default_label = label_rtx (default_label_decl); + First, a jump insn is emitted. First we try "casesi". If that + fails, try "tablejump". A target *must* have one of them (or both). + + Then, a table with the target labels is emitted. + + The process is unaware of the CFG. The caller has to fix up + the CFG itself. This is done in cfgexpand.c. */ + +static void +emit_case_dispatch_table (tree index_expr, tree index_type, + struct case_node *case_list, rtx default_label, + tree minval, tree maxval, tree range) +{ + int i, ncases; + struct case_node *n; + rtx *labelvec; + rtx fallback_label = label_rtx (case_list->code_label); + rtx table_label = gen_label_rtx (); - /* Get upper and lower bounds of case values. */ + if (! try_casesi (index_type, index_expr, minval, range, + table_label, default_label, fallback_label)) + { + bool ok; - uniq = 0; - count = 0; - label_bitmap = BITMAP_ALLOC (NULL); - for (n = case_list; n; n = n->right) + /* Index jumptables from zero for suitable values of minval to avoid + a subtraction. For the rationale see: + "http://gcc.gnu.org/ml/gcc-patches/2001-10/msg01234.html". */ + if (optimize_insn_for_speed_p () + && compare_tree_int (minval, 0) > 0 + && compare_tree_int (minval, 3) < 0) { - /* Count the elements and track the largest and smallest - of them (treating them as signed even if they are not). */ - if (count++ == 0) - { - minval = n->low; - maxval = n->high; - } - else - { - if (tree_int_cst_lt (n->low, minval)) - minval = n->low; - if (tree_int_cst_lt (maxval, n->high)) - maxval = n->high; - } - /* A range counts double, since it requires two compares. */ - if (! tree_int_cst_equal (n->low, n->high)) - count++; - - /* If we have not seen this label yet, then increase the - number of unique case node targets seen. */ - lab = label_rtx (n->code_label); - if (bitmap_set_bit (label_bitmap, CODE_LABEL_NUMBER (lab))) - uniq++; + minval = build_int_cst (index_type, 0); + range = maxval; } - BITMAP_FREE (label_bitmap); - - /* cleanup_tree_cfg removes all SWITCH_EXPR with a single - destination, such as one with a default case only. - It also removes cases that are out of range for the switch - type, so we should never get a zero here. */ - gcc_assert (count > 0); - - /* Compute span of values. */ - range = fold_build2 (MINUS_EXPR, index_type, maxval, minval); - - /* If range of values is much bigger than number of values, - make a sequence of conditional branches instead of a dispatch. - If the switch-index is a constant, do it this way - because we can optimize it. */ - - if (count < case_values_threshold () - || compare_tree_int (range, - (optimize_insn_for_size_p () ? 3 : 10) * count) > 0 - /* RANGE may be signed, and really large ranges will show up - as negative numbers. */ - || compare_tree_int (range, 0) < 0 - || !flag_jump_tables - || TREE_CONSTANT (index_expr) - /* If neither casesi or tablejump is available, we can - only go this way. */ - || (!HAVE_casesi && !HAVE_tablejump)) - { - index = expand_normal (index_expr); + ok = try_tablejump (index_type, index_expr, minval, range, + table_label, default_label); + gcc_assert (ok); + } - /* If the index is a short or char that we do not have - an insn to handle comparisons directly, convert it to - a full integer now, rather than letting each comparison - generate the conversion. */ + /* Get table of labels to jump to, in order of case index. */ - if (GET_MODE_CLASS (GET_MODE (index)) == MODE_INT - && ! have_insn_for (COMPARE, GET_MODE (index))) - { - enum machine_mode wider_mode; - for (wider_mode = GET_MODE (index); wider_mode != VOIDmode; - wider_mode = GET_MODE_WIDER_MODE (wider_mode)) - if (have_insn_for (COMPARE, wider_mode)) - { - index = convert_to_mode (wider_mode, index, unsignedp); - break; - } - } + ncases = tree_low_cst (range, 0) + 1; + labelvec = XALLOCAVEC (rtx, ncases); + memset (labelvec, 0, ncases * sizeof (rtx)); - do_pending_stack_adjust (); + for (n = case_list; n; n = n->right) + { + /* Compute the low and high bounds relative to the minimum + value since that should fit in a HOST_WIDE_INT while the + actual values may not. */ + HOST_WIDE_INT i_low + = tree_low_cst (fold_build2 (MINUS_EXPR, index_type, + n->low, minval), 1); + HOST_WIDE_INT i_high + = tree_low_cst (fold_build2 (MINUS_EXPR, index_type, + n->high, minval), 1); + HOST_WIDE_INT i; + + for (i = i_low; i <= i_high; i ++) + labelvec[i] + = gen_rtx_LABEL_REF (Pmode, label_rtx (n->code_label)); + } - if (MEM_P (index)) - { - index = copy_to_reg (index); - if (TREE_CODE (index_expr) == SSA_NAME) - set_reg_attrs_for_decl_rtl (SSA_NAME_VAR (index_expr), index); - } + /* Fill in the gaps with the default. We may have gaps at + the beginning if we tried to avoid the minval subtraction, + so substitute some label even if the default label was + deemed unreachable. */ + if (!default_label) + default_label = fallback_label; + for (i = 0; i < ncases; i++) + if (labelvec[i] == 0) + labelvec[i] = gen_rtx_LABEL_REF (Pmode, default_label); + + /* Output the table. */ + emit_label (table_label); + + if (CASE_VECTOR_PC_RELATIVE || flag_pic) + emit_jump_insn (gen_rtx_ADDR_DIFF_VEC (CASE_VECTOR_MODE, + gen_rtx_LABEL_REF (Pmode, table_label), + gen_rtvec_v (ncases, labelvec), + const0_rtx, const0_rtx)); + else + emit_jump_insn (gen_rtx_ADDR_VEC (CASE_VECTOR_MODE, + gen_rtvec_v (ncases, labelvec))); - /* We generate a binary decision tree to select the - appropriate target code. This is done as follows: + /* Record no drop-through after the table. */ + emit_barrier (); +} - The list of cases is rearranged into a binary tree, - nearly optimal assuming equal probability for each case. +/* Terminate a case (Pascal/Ada) or switch (C) statement + in which ORIG_INDEX is the expression to be tested. + If ORIG_TYPE is not NULL, it is the original ORIG_INDEX + type as given in the source before any compiler conversions. + Generate the code to test it and jump to the right place. */ + +void +expand_case (gimple stmt) +{ + tree minval = NULL_TREE, maxval = NULL_TREE, range = NULL_TREE; + rtx default_label = NULL_RTX; + unsigned int count, uniq; + int i, stopi = 0; + rtx before_case, end; + int ncases = gimple_switch_num_labels (stmt); + tree index_expr = gimple_switch_index (stmt); + tree index_type = TREE_TYPE (index_expr); - The tree is transformed into RTL, eliminating - redundant test conditions at the same time. + tree elt; + bitmap label_bitmap; - If program flow could reach the end of the - decision tree an unconditional jump to the - default code is emitted. */ + /* The insn after which the case dispatch should finally + be emitted. Zero for a dummy. */ + rtx start; - balance_case_nodes (&case_list, NULL); - emit_case_nodes (index, case_list, default_label, index_type); - if (default_label) - emit_jump (default_label); - } - else - { - rtx fallback_label = label_rtx (case_list->code_label); - table_label = gen_label_rtx (); - if (! try_casesi (index_type, index_expr, minval, range, - table_label, default_label, fallback_label)) - { - bool ok; + /* A list of case labels; it is first built as a list and it may then + be rearranged into a nearly balanced binary tree. */ + struct case_node *case_list = 0; - /* Index jumptables from zero for suitable values of - minval to avoid a subtraction. */ - if (optimize_insn_for_speed_p () - && compare_tree_int (minval, 0) > 0 - && compare_tree_int (minval, 3) < 0) - { - minval = build_int_cst (index_type, 0); - range = maxval; - } + /* A pool for case nodes. */ + alloc_pool case_node_pool; - ok = try_tablejump (index_type, index_expr, minval, range, - table_label, default_label); - gcc_assert (ok); - } + /* An ERROR_MARK occurs for various reasons including invalid data type. + ??? Can this still happen, with GIMPLE and all? */ + if (index_type == error_mark_node) + return; - /* Get table of labels to jump to, in order of case index. */ + /* cleanup_tree_cfg removes all SWITCH_EXPR with their index + expressions being INTEGER_CST. */ + gcc_assert (TREE_CODE (index_expr) != INTEGER_CST); + + case_node_pool = create_alloc_pool ("struct case_node pool", + sizeof (struct case_node), + 100); - ncases = tree_low_cst (range, 0) + 1; - labelvec = XALLOCAVEC (rtx, ncases); - memset (labelvec, 0, ncases * sizeof (rtx)); + do_pending_stack_adjust (); - for (n = case_list; n; n = n->right) - { - /* Compute the low and high bounds relative to the minimum - value since that should fit in a HOST_WIDE_INT while the - actual values may not. */ - HOST_WIDE_INT i_low - = tree_low_cst (fold_build2 (MINUS_EXPR, index_type, - n->low, minval), 1); - HOST_WIDE_INT i_high - = tree_low_cst (fold_build2 (MINUS_EXPR, index_type, - n->high, minval), 1); - HOST_WIDE_INT i; - - for (i = i_low; i <= i_high; i ++) - labelvec[i] - = gen_rtx_LABEL_REF (Pmode, label_rtx (n->code_label)); - } + /* The default case, if ever taken, is the first element. */ + elt = gimple_switch_label (stmt, 0); + if (!CASE_LOW (elt) && !CASE_HIGH (elt)) + { + default_label = label_rtx (CASE_LABEL (elt)); + stopi = 1; + } - /* Fill in the gaps with the default. We may have gaps at - the beginning if we tried to avoid the minval subtraction, - so substitute some label even if the default label was - deemed unreachable. */ - if (!default_label) - default_label = fallback_label; - for (i = 0; i < ncases; i++) - if (labelvec[i] == 0) - labelvec[i] = gen_rtx_LABEL_REF (Pmode, default_label); - - /* Output the table. */ - emit_label (table_label); - - if (CASE_VECTOR_PC_RELATIVE || flag_pic) - emit_jump_insn (gen_rtx_ADDR_DIFF_VEC (CASE_VECTOR_MODE, - gen_rtx_LABEL_REF (Pmode, table_label), - gen_rtvec_v (ncases, labelvec), - const0_rtx, const0_rtx)); - else - emit_jump_insn (gen_rtx_ADDR_VEC (CASE_VECTOR_MODE, - gen_rtvec_v (ncases, labelvec))); + /* Get upper and lower bounds of case values. */ + elt = gimple_switch_label (stmt, stopi); + minval = fold_convert (index_type, CASE_LOW (elt)); + elt = gimple_switch_label (stmt, ncases - 1); + if (CASE_HIGH (elt)) + maxval = fold_convert (index_type, CASE_HIGH (elt)); + else + maxval = fold_convert (index_type, CASE_LOW (elt)); - /* Record no drop-through after the table. */ - emit_barrier (); - } + /* Compute span of values. */ + range = fold_build2 (MINUS_EXPR, index_type, maxval, minval); - before_case = NEXT_INSN (before_case); - end = get_last_insn (); - reorder_insns (before_case, end, start); + /* Listify the labels queue and gather some numbers to decide + how to expand this switch(). */ + uniq = 0; + count = 0; + label_bitmap = BITMAP_ALLOC (NULL); + for (i = gimple_switch_num_labels (stmt) - 1; i >= stopi; --i) + { + tree low, high; + rtx lab; + + elt = gimple_switch_label (stmt, i); + low = CASE_LOW (elt); + gcc_assert (low); + high = CASE_HIGH (elt); + gcc_assert (! high || tree_int_cst_lt (low, high)); + + /* Count the elements. + A range counts double, since it requires two compares. */ + count++; + if (high) + count++; + + /* If we have not seen this label yet, then increase the + number of unique case node targets seen. */ + lab = label_rtx (CASE_LABEL (elt)); + if (bitmap_set_bit (label_bitmap, CODE_LABEL_NUMBER (lab))) + uniq++; + + /* The canonical from of a case label in GIMPLE is that a simple case + has an empty CASE_HIGH. For the casesi and tablejump expanders, + the back ends want simple cases to have high == low. */ + if (! high) + high = low; + + case_list = add_case_node (case_list, index_type, low, high, + CASE_LABEL (elt), case_node_pool); } + BITMAP_FREE (label_bitmap); + + /* cleanup_tree_cfg removes all SWITCH_EXPR with a single + destination, such as one with a default case only. + It also removes cases that are out of range for the switch + type, so we should never get a zero here. */ + gcc_assert (count > 0); + + before_case = start = get_last_insn (); + + /* Decide how to expand this switch. + The two options at this point are a dispatch table (casesi or + tablejump) or a decision tree. */ + + if (expand_switch_as_decision_tree_p (range, uniq, count)) + emit_case_decision_tree (index_expr, index_type, + case_list, default_label); + else + emit_case_dispatch_table (index_expr, index_type, + case_list, default_label, + minval, maxval, range); + + before_case = NEXT_INSN (before_case); + end = get_last_insn (); + reorder_insns (before_case, end, start); free_temp_slots (); free_alloc_pool (case_node_pool); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 1b88564..c6870a1 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2012-07-05 Steven Bosscher <steven@gcc.gnu.org> + + * gcc.c-torture/compile/20000326-1.c: Fix to not optimize to empty. + 2012-07-05 Matthew Gretton-Dann <matthew.gretton-dann@arm.com> * gcc.target/arm/fma-sp.c: New testcase. diff --git a/gcc/testsuite/gcc.c-torture/compile/20000326-1.c b/gcc/testsuite/gcc.c-torture/compile/20000326-1.c index 71ed149..3b38106 100644 --- a/gcc/testsuite/gcc.c-torture/compile/20000326-1.c +++ b/gcc/testsuite/gcc.c-torture/compile/20000326-1.c @@ -2,22 +2,21 @@ long sys_reboot(int magic1, int magic2, int cmd, void * arg) { switch (cmd) { case 0x89ABCDEF: - break; + return 1; case 0x00000000: - break; + return 2; case 0xCDEF0123: - break; + return 3; case 0x4321FEDC: - break; + return 4; case 0xA1B2C3D4: - break; + return 5; default: - break; + return 0; }; - return 0; } |