diff --git a/cobc/ChangeLog b/cobc/ChangeLog index f7880ead5..381a67b30 100644 --- a/cobc/ChangeLog +++ b/cobc/ChangeLog @@ -268,6 +268,17 @@ search tree * parser.y (_end_search): if search has no AT END create an implicit one at END-SEARCH for better trace and debugging + * flag.def, typeck.c: new option "fast-compare" (cb_flag_fast_compare, + defaulting to on) to disable old and new optimizations + * cobc.c: disable cb_flag_fast_compare for -fsyntax-only and on compiler + errors to improve parsing time + * tree.c (cb_fits_int, cb_fits_long_long): constant ZERO fits both + integer types + * tree.c (cb_field_size): return FIELD_SIZE_UNKNOWN for constants and + fields with ANY LENGTH + * typeck.c (cb_build_cond_default, cb_build_cond_fields): extracted + from cb_build_cond + 2022-12-14 Simon Sobisch diff --git a/cobc/cobc.c b/cobc/cobc.c index 6d93bf3a1..f6229aa7c 100644 --- a/cobc/cobc.c +++ b/cobc/cobc.c @@ -7896,6 +7896,7 @@ process_translate (struct filename *fn) /* If processing raised errors set syntax-only flag to not loose the information "no codegen occurred" */ cb_flag_syntax_only = 1; + cb_flag_fast_compare = 0; return 1; } if (cb_flag_syntax_only) { @@ -9023,6 +9024,7 @@ process_file (struct filename *fn, int status) /* If preprocessing raised errors go on but only check syntax */ if (fn->has_error) { cb_flag_syntax_only = 1; + cb_flag_fast_compare = 0; } } else if (cb_src_list_file) { @@ -9156,6 +9158,7 @@ main (int argc, char **argv) cobc_flag_module = 1; } } else { + cb_flag_fast_compare = 0; cb_compile_level = CB_LEVEL_TRANSLATE; cobc_flag_main = 0; cobc_flag_module = 0; diff --git a/cobc/flag.def b/cobc/flag.def index 9348d2a7e..7c5fc2c8b 100644 --- a/cobc/flag.def +++ b/cobc/flag.def @@ -129,6 +129,9 @@ CB_FLAG (cb_flag_stack_extended, 1, "stack-extended", CB_FLAG_ON (cb_flag_fast_math, 0, "fast-math", _(" -ffast-math Disables emitting faster arithmetic logic")) +CB_FLAG_ON (cb_flag_fast_compare, 0, "fast-compare", + _(" -fno-fast-compare disables inline comparisions\n")) + /* Normal flags */ CB_FLAG_ON (cb_flag_remove_unreachable, 1, "remove-unreachable", diff --git a/cobc/tree.c b/cobc/tree.c index 6e1988e3f..c3b61dcd2 100644 --- a/cobc/tree.c +++ b/cobc/tree.c @@ -1692,6 +1692,9 @@ cb_fits_int (const cb_tree x) case CB_TAG_INTEGER: return 1; default: + if (x == cb_zero) { + return 1; + } return 0; } } @@ -1777,6 +1780,9 @@ cb_fits_long_long (const cb_tree x) case CB_TAG_INTEGER: return 1; default: + if (x == cb_zero) { + return 1; + } return 0; } } @@ -4268,22 +4274,24 @@ cb_field_add (struct cb_field *f, struct cb_field *p) int cb_field_size (const cb_tree x) { - struct cb_reference *r; - struct cb_field *f; switch (CB_TREE_TAG (x)) { case CB_TAG_LITERAL: return CB_LITERAL (x)->size; - case CB_TAG_FIELD: - f = CB_FIELD (x); + case CB_TAG_FIELD: { + const struct cb_field *f = CB_FIELD (x); + if (f->flag_any_length) { + return FIELD_SIZE_UNKNOWN; + } if (f->usage == CB_USAGE_COMP_X && f->compx_size > 0) { return f->compx_size; } - return CB_FIELD (x)->size; - case CB_TAG_REFERENCE: - r = CB_REFERENCE (x); - f = CB_FIELD (r->value); + return f->size; + } + case CB_TAG_REFERENCE: { + const struct cb_reference *r = CB_REFERENCE (x); + const struct cb_field *f = CB_FIELD (r->value); if (r->length) { if (CB_LITERAL_P (r->length)) { return cb_get_int (r->length); @@ -4296,12 +4304,18 @@ cb_field_size (const cb_tree x) } else { return FIELD_SIZE_UNKNOWN; } + } else if (f->flag_any_length) { + return FIELD_SIZE_UNKNOWN; } else if (f->usage == CB_USAGE_COMP_X && f->compx_size > 0) { return f->compx_size; } else { return f->size; } + } + case CB_TAG_CONST: + /* depends on its actual usage */ + return FIELD_SIZE_UNKNOWN; /* LCOV_EXCL_START */ default: diff --git a/cobc/typeck.c b/cobc/typeck.c index c99d7ccc2..fcae72874 100644 --- a/cobc/typeck.c +++ b/cobc/typeck.c @@ -6635,7 +6635,7 @@ decimal_expand (cb_tree d, cb_tree x) decimal_expand (d, p->x); if (CB_TREE_TAG (p->y) == CB_TAG_LITERAL - && CB_TREE_CATEGORY (p->y) == CB_CATEGORY_NUMERIC) { + && CB_TREE_CATEGORY (p->y) == CB_CATEGORY_NUMERIC) { t = cb_build_decimal_literal (cb_lookup_literal(p->y,1)); decimal_compute (p->op, d, t); } else { @@ -7049,26 +7049,21 @@ cb_check_num_cond (cb_tree x, cb_tree y) return 0; } if (CB_TREE_CATEGORY (x) != CB_CATEGORY_NUMERIC - || CB_TREE_CATEGORY (y) != CB_CATEGORY_NUMERIC) { - return 0; - } - if (CB_TREE_CLASS (x) != CB_CLASS_NUMERIC + || CB_TREE_CATEGORY (y) != CB_CATEGORY_NUMERIC + || CB_TREE_CLASS (x) != CB_CLASS_NUMERIC || CB_TREE_CLASS (y) != CB_CLASS_NUMERIC) { return 0; } fx = CB_FIELD_PTR (x); fy = CB_FIELD_PTR (y); if (fx->usage != CB_USAGE_DISPLAY - || fy->usage != CB_USAGE_DISPLAY) { - return 0; - } - if (fx->pic->have_sign || fy->pic->have_sign) { + || fy->usage != CB_USAGE_DISPLAY + || fx->pic->have_sign + || fy->pic->have_sign) { return 0; } - if (fx->size != fy->size) { - return 0; - } - if (fx->pic->scale != fy->pic->scale) { + if (fx->size != fy->size + || fx->pic->scale != fy->pic->scale) { return 0; } return 1; @@ -7077,10 +7072,8 @@ cb_check_num_cond (cb_tree x, cb_tree y) static int cb_check_alpha_cond (cb_tree x) { - if (current_program->alphabet_name_list) { - return 0; - } - if (CB_LITERAL_P (x)) { + if (CB_LITERAL_P (x) + || CB_CONST_P (x)) { return 1; } if (!CB_REF_OR_FIELD_P (x)) { @@ -7093,9 +7086,6 @@ cb_check_alpha_cond (cb_tree x) if (cb_field_variable_size (CB_FIELD_PTR (x))) { return 0; } - if (cb_field_size (x) == FIELD_SIZE_UNKNOWN) { - return 0; - } return 1; } @@ -7150,17 +7140,197 @@ cb_walk_cond (cb_tree x) } } +/* Field comparison */ +static cb_tree +cb_build_cond_fields (struct cb_binary_op *p, + cb_tree left, cb_tree right, const enum cb_class l_class) +{ + const enum cb_category x_cat = CB_TREE_CATEGORY (left); + const int size1 = cb_field_size (left); + const int size2 = cb_field_size (right); + + if ((CB_REF_OR_FIELD_P (left)) + && (x_cat == CB_CATEGORY_ALPHANUMERIC + || x_cat == CB_CATEGORY_ALPHABETIC) + && size1 == 1 + && (right == cb_space || right == cb_zero + || right == cb_high || right == cb_low)) { + return CB_BUILD_FUNCALL_2 ("$G", left, right); + } + + if (size1 == 1 && size2 == 1) { + return CB_BUILD_FUNCALL_2 ("$G", left, right); + } + if (size1 > 0 && size1 == size2) { + return CB_BUILD_FUNCALL_3 ("memcmp", + CB_BUILD_CAST_ADDRESS (left), + CB_BUILD_CAST_ADDRESS (right), + cb_int (size1)); + } + if (right == cb_zero && l_class == CB_CLASS_NUMERIC) { + return cb_build_optim_cond (p); + } + return CB_BUILD_FUNCALL_2 ("cob_cmp", left, right); +} + +static cb_tree +cb_build_cond_default (struct cb_binary_op *p, cb_tree left, cb_tree right) +{ + const enum cb_class l_class = CB_TREE_CLASS (left); + const enum cb_class r_class = CB_TREE_CLASS (right); + + int has_any_len = 0; + + if (CB_TREE_TAG (left) == CB_TAG_DECIMAL) { + cb_tree d2; + cb_tree ret; + if (CB_TREE_TAG (right) == CB_TAG_LITERAL + && CB_TREE_CATEGORY (right) == CB_CATEGORY_NUMERIC) { + d2 = cb_build_decimal_literal (cb_lookup_literal(right,1)); + dpush (CB_BUILD_FUNCALL_2 ("cob_decimal_cmp", left, d2)); + } else { + d2 = decimal_alloc (); + decimal_expand (d2, right); + dpush (CB_BUILD_FUNCALL_2 ("cob_decimal_cmp", left, d2)); + decimal_free (); + } + ret = cb_list_reverse (decimal_stack); + decimal_stack = NULL; + return ret; + } + + if (CB_REF_OR_FIELD_P (left)) { + struct cb_field *f = CB_FIELD_PTR (left); + if (f->flag_any_length) { + has_any_len = 1; + } + } + + if (CB_BINARY_OP_P (left) + || CB_BINARY_OP_P (right)) { + /* Decimal comparison */ + cb_tree ret; + cb_tree d1; + cb_tree d2; + if (cb_is_integer_expr (CB_TREE (p))) { + return cb_build_optim_cond (p); + } + d1 = decimal_alloc (); + d2 = decimal_alloc (); + decimal_expand (d1, left); + decimal_expand (d2, right); + dpush (CB_BUILD_FUNCALL_2 ("cob_decimal_cmp", d1, d2)); + decimal_free (); + decimal_free (); + ret = cb_list_reverse (decimal_stack); + decimal_stack = NULL; + return ret; + } + +#if 0 /* possibly add check of classes of the two operands, note that there + are a lot of defined comparisions in the standard 8.8.4.1.1 relation + conditions, with explicit comparision of class alphanumeric (where + all edited items go to) and of class numeric; so likely only do this + with a new warning only enabled with -Wextra. */ + if (get_warn_opt_value (cb_warn_strict_typing) != COBC_WARN_DISABLED) { + if (cb_tree_class_are_something (left, right)) { + cb_warning_x (cb_warn_strict_typing, + CB_TREE (p), _("alphanumeric value is expected")); + } else { + cb_warning_x (cb_warn_strict_typing, + CB_TREE(p), _("numeric value is expected")); + } + } +#endif + + if (CB_INDEX_OR_HANDLE_P (left) + || CB_INDEX_OR_HANDLE_P (right) + || l_class == CB_CLASS_POINTER + || r_class == CB_CLASS_POINTER) { + return cb_build_binary_op (left, '-', right); + } + + /* DEBUG Bypass optimization for PERFORM and upon request */ + if (current_program->flag_debugging + || !cb_flag_fast_compare) { + return CB_BUILD_FUNCALL_2 ("cob_cmp", left, right); + } + + if (cb_check_num_cond (left, right)) { + const int size1 = cb_field_size (left); + return CB_BUILD_FUNCALL_3 ("memcmp", + CB_BUILD_CAST_ADDRESS (left), + CB_BUILD_CAST_ADDRESS (right), + cb_int (size1)); + } + + if (p->op == '=' + || p->op == '~' + || p->op == '>' + || p->op == '<' + || p->op == ']' + || p->op == '[') { + int_usage = CB_USAGE_PACKED; + if (l_class == CB_CLASS_NUMERIC + && r_class == CB_CLASS_NUMERIC + && CB_TREE_TAG (left) == CB_TAG_LITERAL + && CB_TREE_TAG (right) == CB_TAG_REFERENCE) { /* literal relop field */ + cb_tree d1; + d1 = left; + left = right; /* Swap operands */ + right = d1; + p->x = left; + p->y = right; + if (p->op == '>') + p->op = '<'; + else if (p->op == '<') + p->op = '>'; + else if (p->op == '[') + p->op = ']'; + else if (p->op == ']') + p->op = '['; + } + } else { + int_usage = -1; + } + + if (l_class == CB_CLASS_NUMERIC + && r_class == CB_CLASS_NUMERIC + && cb_fits_long_long (right)) { +#if 0 /* CHECKME: this code from 4.x causes many testsuite failures when merging with 4878 from 3.x */ + if (CB_REF_OR_FIELD_P (left)) { + cb_tree ret; + struct cb_field *f = CB_FIELD_PTR (left); + if (cb_is_integer_field_and_int (f, right) + && cb_fits_int (right)) { + /* 'native' (short/int/long) on SYNC boundary */ + int_usage = -1; + ret = CB_BUILD_FUNCALL_3 ("$:", left, (cb_tree)(long)p->op, right); + cb_copy_source_reference (ret, CB_TREE (p)); + return ret; + } + } +#endif + int_usage = -1; + return cb_build_optim_cond (p); + } + int_usage = -1; + + if (current_program->alphabet_name_list + || has_any_len + || !cb_check_alpha_cond (left) + || !cb_check_alpha_cond (right)) { + return CB_BUILD_FUNCALL_2 ("cob_cmp", left, right); + } + return cb_build_cond_fields (p, left, right, l_class); +} + cb_tree cb_build_cond (cb_tree x) { struct cb_field *f; struct cb_binary_op *p; - cb_tree d1; - cb_tree d2; cb_tree ret; - int has_any_len = 0; - int size1; - int size2; if (x == cb_error_node) { return cb_error_node; @@ -7170,8 +7340,8 @@ cb_build_cond (cb_tree x) /* ARITHMETIC-OSVS: Determine largest scale used in condition */ if (expr_dmax == -1) { /* FIXME: this is a hack, x should always be a list! */ - if (CB_LIST_P(x)) { - expr_rslt = CB_VALUE(x); + if (CB_LIST_P (x)) { + expr_rslt = CB_VALUE (x); } else { expr_rslt = x; } @@ -7221,173 +7391,21 @@ cb_build_cond (cb_tree x) switch (p->op) { case '!': ret = CB_BUILD_NEGATION (cb_build_cond (p->x)); - goto return_ret; + break; case '&': case '|': if (!p->y || p->y == cb_error_node) { return cb_error_node; } ret = cb_build_binary_op (cb_build_cond (p->x), p->op, cb_build_cond (p->y)); - goto return_ret; + break; default: if (!p->y || p->y == cb_error_node) { return cb_error_node; } - f = NULL; - if (CB_TREE_TAG (p->x) == CB_TAG_DECIMAL) { - if (CB_TREE_TAG (p->y) == CB_TAG_LITERAL - && CB_TREE_CATEGORY (p->y) == CB_CATEGORY_NUMERIC) { - d2 = cb_build_decimal_literal (cb_lookup_literal(p->y,1)); - dpush (CB_BUILD_FUNCALL_2 ("cob_decimal_cmp", p->x, d2)); - } else { - d2 = decimal_alloc (); - decimal_expand (d2, p->y); - dpush (CB_BUILD_FUNCALL_2 ("cob_decimal_cmp", p->x, d2)); - decimal_free (); - } - ret = cb_list_reverse (decimal_stack); - decimal_stack = NULL; - break; - } - if (CB_REF_OR_FIELD_P (p->x)) { - f = CB_FIELD_PTR (p->x); - if(f->flag_any_length) - has_any_len = 1; - } - - if (CB_INDEX_OR_HANDLE_P (p->x) - || CB_INDEX_OR_HANDLE_P (p->y) - || CB_TREE_CLASS (p->x) == CB_CLASS_POINTER - || CB_TREE_CLASS (p->y) == CB_CLASS_POINTER) { - ret = cb_build_binary_op (p->x, '-', p->y); - } else if (CB_BINARY_OP_P (p->x) - || CB_BINARY_OP_P (p->y)) { - if (cb_is_integer_expr (x)) { - ret = cb_build_optim_cond (p); - break; - } - - /* Decimal comparison */ - d1 = decimal_alloc (); - d2 = decimal_alloc (); - decimal_expand (d1, p->x); - decimal_expand (d2, p->y); - dpush (CB_BUILD_FUNCALL_2 ("cob_decimal_cmp", d1, d2)); - decimal_free (); - decimal_free (); - ret = cb_list_reverse (decimal_stack); - decimal_stack = NULL; - } else { - /* DEBUG Bypass optimization for PERFORM */ - if (current_program->flag_debugging) { - ret = CB_BUILD_FUNCALL_2 ("cob_cmp", p->x, p->y); - break; - } - if (cb_check_num_cond (p->x, p->y)) { - size1 = cb_field_size (p->x); - ret = CB_BUILD_FUNCALL_3 ("memcmp", - CB_BUILD_CAST_ADDRESS (p->x), - CB_BUILD_CAST_ADDRESS (p->y), - cb_int (size1)); - break; - } - if (p->op == '=' - || p->op == '~' - || p->op == '>' - || p->op == '<' - || p->op == ']' - || p->op == '[') { - int_usage = CB_USAGE_PACKED; - if (CB_TREE_CLASS (p->x) == CB_CLASS_NUMERIC - && CB_TREE_CLASS (p->y) == CB_CLASS_NUMERIC - && CB_TREE_TAG (p->x) == CB_TAG_LITERAL - && CB_TREE_TAG (p->y) == CB_TAG_REFERENCE) { /* literal relop field */ - d1 = p->x; - p->x = p->y; /* Swap operands */ - p->y = d1; - if (p->op == '>') - p->op = '<'; - else if (p->op == '<') - p->op = '>'; - else if (p->op == '[') - p->op = ']'; - else if (p->op == ']') - p->op = '['; - } - } else { - int_usage = -1; - } - if (CB_TREE_CLASS (p->x) == CB_CLASS_NUMERIC - && CB_TREE_CLASS (p->y) == CB_CLASS_NUMERIC - && cb_fits_long_long (p->y)) { - if (CB_REF_OR_FIELD_P (p->x)) { - f = CB_FIELD_PTR (p->x); - if (cb_is_integer_field_and_int (f, p->y) - && cb_fits_int (p->y)) { - /* 'native' (short/int/long) on SYNC boundary */ - int_usage = -1; - ret = CB_BUILD_FUNCALL_3 ("$:", p->x, (cb_tree)(long)p->op, p->y); - cb_copy_source_reference (ret, x); - return ret; - } - } - int_usage = -1; - ret = cb_build_optim_cond (p); - break; - } - int_usage = -1; - - /* Field comparison */ - if ((CB_REF_OR_FIELD_P (p->x)) - && (CB_TREE_CATEGORY (p->x) == CB_CATEGORY_ALPHANUMERIC || - CB_TREE_CATEGORY (p->x) == CB_CATEGORY_ALPHABETIC) - && cb_field_size (p->x) == 1 - && !has_any_len - && !current_program->alphabet_name_list - && (p->y == cb_space || p->y == cb_low || - p->y == cb_high || p->y == cb_zero)) { - ret = CB_BUILD_FUNCALL_2 ("$G", p->x, p->y); - break; - } - if (cb_check_alpha_cond (p->x) - && cb_check_alpha_cond (p->y)) { - size1 = cb_field_size (p->x); - size2 = cb_field_size (p->y); - } else { - size1 = 0; - size2 = 0; - } -#if 0 /* possibly add check of classes of the two operands, note that there - are a lot of defined comparisions in the standard 8.8.4.1.1 relation - conditions, with explicit comparision of class alphanumeric (where - all edited items go to) and of class numeric; so likely only do this - with a new warning only enabled with -Wextra. */ - if (get_warn_opt_value (cb_warn_strict_typing) != COBC_WARN_DISABLED) { - if (cb_tree_class_are_something (left, right)) { - cb_warning_x (cb_warn_strict_typing, x, _("alphanumeric value is expected")); - } else { - cb_warning_x (cb_warn_strict_typing, x, _("numeric value is expected")); - } - } -#endif - if (size1 == 1 && size2 == 1 && !has_any_len) { - ret = CB_BUILD_FUNCALL_2 ("$G", p->x, p->y); - } else if (size1 != 0 && size1 == size2 && !has_any_len) { - ret = CB_BUILD_FUNCALL_3 ("memcmp", - CB_BUILD_CAST_ADDRESS (p->x), - CB_BUILD_CAST_ADDRESS (p->y), - cb_int (size1)); - } else { - if (CB_TREE_CLASS (p->x) == CB_CLASS_NUMERIC && p->y == cb_zero) { - ret = cb_build_optim_cond (p); - } else { - ret = CB_BUILD_FUNCALL_2 ("cob_cmp", p->x, p->y); - } - } - } + ret = cb_build_cond_default (p, p->x, p->y); + ret = cb_build_binary_op (ret, p->op, p->y); } - ret = cb_build_binary_op (ret, p->op, p->y); -return_ret: if (ret != cb_true && ret != cb_false) { cb_copy_source_reference (ret, x); } diff --git a/tests/testsuite.src/run_fundamental.at b/tests/testsuite.src/run_fundamental.at index cb735f79a..37fd577bd 100644 --- a/tests/testsuite.src/run_fundamental.at +++ b/tests/testsuite.src/run_fundamental.at @@ -5990,8 +5990,8 @@ AT_CHECK([$COBCRUN_DIRECT ./prog], [0], [], []) AT_CHECK([$COMPILE prog.cob -Wno-constant-expression -fno-constant-folding], [0], [], []) AT_CHECK([$COBCRUN_DIRECT ./prog], [0], [], []) -#AT_CHECK([$COMPILE prog.cob -Wno-constant-expression -fno-constant-folding -fno-fast-compare], [0], [], []) -#AT_CHECK([$COBCRUN_DIRECT ./prog], [0], [], []) +AT_CHECK([$COMPILE prog.cob -Wno-constant-expression -fno-constant-folding -fno-fast-compare], [0], [], []) +AT_CHECK([$COBCRUN_DIRECT ./prog], [0], [], []) AT_CLEANUP diff --git a/tests/testsuite.src/run_misc.at b/tests/testsuite.src/run_misc.at index fdfdc3ea9..cdcea4f80 100644 --- a/tests/testsuite.src/run_misc.at +++ b/tests/testsuite.src/run_misc.at @@ -13108,7 +13108,10 @@ AT_DATA([prog.cob], [ STOP RUN. ]) -AT_CHECK([$COMPILE prog.cob -w], [0], [], []) +# Note: this program errors without constant folding, but that is +# checked in syn_misc.at already; we explicit specify the remove +# of folded constants option allowing to run with COBOL_FLAGS=-g +AT_CHECK([$COMPILE prog.cob -fconstant-folding -fremove-unreachable -w], [0], [], []) AT_CHECK([$COBCRUN_DIRECT ./prog], [0], [A: OK VAR-LEN > 16 AND VAR-LEN < 200