diff --git a/NEWS b/NEWS index 90577272e..47b274500 100644 --- a/NEWS +++ b/NEWS @@ -232,7 +232,8 @@ NEWS - user visible changes -*- outline -*- to use this extension for other dialects use the new -fself-call-recursive=warning (or "ok") -** the option -g does not longer imply -fsource-location +** the option -g does not longer imply -fsource-location; but it auto-includes + references to the COBOL-paragraphs to further ease source level debugging ** new flag -fstack-extended (implied with --debug and --dump) to include the origin of entrypoints and PERFORM, this is used for the internal diff --git a/cobc/ChangeLog b/cobc/ChangeLog index f576d82b3..3f42bbb84 100644 --- a/cobc/ChangeLog +++ b/cobc/ChangeLog @@ -8,6 +8,10 @@ is done and swap the result for return values as used in SEARCH ALL later * codegen.c (output_long_integer): reduce scope of variables as done in (output_integer) + * codegen.c (output_assign, output_if, output_debug_item): extracted + from output_stmt + * typeck.c (cb_emit, cb_emit_list): changed from defines to inline + functions, now returning the tree that was emitted 2022-12-15 Simon Sobisch @@ -27,6 +31,15 @@ if left side is constant or literal * typeck.c (cb_build_cond_fields): optimize comparison between field and SPACES up to COB_SPACES_ALPHABETIC_BYTE_LENGTH + * codegen.c (output_label, output_label_c): extracted from output_stmt + * codegen.c (output_label_c): added output of C labels for paragraphs + using prefix PARAGRAPH and, to make them distinct, its label id as suffix + * codegen.c (output_search_all, output_search_whens): if no AT END position + token is available, use the start token instead + * typeck.c (cb_emit_search, cb_emit_search_all), tree.h: return created + search tree + * parser.y (_end_search): if search has no AT END create an implicit one + at END-SEARCH for better trace and debugging 2022-12-14 Simon Sobisch diff --git a/cobc/codegen.c b/cobc/codegen.c index c2793a08e..11b5a737b 100644 --- a/cobc/codegen.c +++ b/cobc/codegen.c @@ -5776,10 +5776,11 @@ output_search_whens (cb_tree table, struct cb_field *p, cb_tree at_end, output_source_reference (CB_PAIR_X (at_end), STMT_AT_END); output_stmt (CB_PAIR_Y (at_end)); } else { - /* position is best guess here */ - table->source_line++; + /* position to table here, otherwise we likely land in the + first WHEN + (Note: if there's an explicit END-SEARCH there's always + and implicit AT END on its position (included by parser.y) */ output_source_reference (table, STMT_AT_END); - table->source_line--; output_line ("break;"); } output_block_close (); @@ -5847,10 +5848,10 @@ output_search_all (cb_tree table, struct cb_field *p, cb_tree at_end, output_source_reference (CB_PAIR_X (at_end), STMT_AT_END); output_stmt (CB_PAIR_Y (at_end)); } else { - /* position is best guess here */ - table->source_line++; + /* position to table here, otherwise we likely land in the + WHEN (Note: if there's an explicit END-SEARCH there's always + and implicit AT END on its position (included by parser.y) */ output_source_reference (table, STMT_AT_END); - table->source_line--; output_line ("break;"); } output_block_close (); @@ -5882,7 +5883,7 @@ output_search_all (cb_tree table, struct cb_field *p, cb_tree at_end, { /* output_source_reference would be ok here but we don't want to trace this (already tracing - SEARCH VARYING), so temporarily disable trace all here */ + SEARCH VARYING), so temporarily disable traceall here */ const int sav_trc_all = cb_flag_traceall; const int sav_trc_old = cb_old_trace; cb_flag_traceall = cb_old_trace = 0; @@ -7543,6 +7544,97 @@ output_perform (struct cb_perform *p) } } +static void +output_debug_item (const struct cb_debug *dbg) +{ + const size_t size = cb_code_field (dbg->target)->size; + const size_t copy_size = dbg->size > size ? size : dbg->size; + if (!dbg->value) { + /* content of variable */ + struct cb_field *f = CB_FIELD_PTR (dbg->fld); + /* address may change so we may have NULL or invalid pointer */ + if (f->flag_item_based || f->storage == CB_STORAGE_LINKAGE) { +#if 0 /* FIXME: this should be replaced in 4.x by a call to libcob + which checks for NULL, and for invalid access via handler, + then outputs the appropriate value */ + struct cb_field * ff = real_field_founder (f); + output_prefix (); + output ("cob_set_verified_data ("); + output_data (dbg->target); + output (", "); + output_data (CB_TREE(ff)); + output (", " CB_FMT_LLU ", %u); ", f->offset, size); + output_newline (); +#else + + const char *null_rep = ""; + f = real_field_founder (f); + /* in this case - pre-fill with space, then set var / null_rep */ + output_prefix (); + output ("memset ("); + output_data (dbg->target); + output (", ' ', %u);", (unsigned int)size); + output_newline (); + output_prefix (); + output ("if ("); + output_data (CB_TREE (f)); + output (" == NULL)"); + output_newline (); + output_prefix (); + output ("\t""memcpy ("); + output_data (dbg->target); + output (", %s%d", CB_PREFIX_STRING, lookup_string (null_rep)); + output (", %u);", (unsigned int)strlen (null_rep)); + output_newline (); + output_line ("else"); + output_prefix (); + output ("\t""memcpy ("); + output_data (dbg->target); + output (", "); + output_data (dbg->fld); + output (", %u);", (unsigned int)copy_size); + output_newline (); +#endif + } else { + /* normal field without changing address, copy data up to max*/ + output ("memcpy ("); + output_data (dbg->target); + output (", "); + output_data (dbg->fld); + output (", %u);", (unsigned int)copy_size); + output_newline (); + /* ... filled up with space */ + if (copy_size != size) { + output_prefix (); + output ("memset ("); + output_data (dbg->target); + output (" + %u, ' ', %u);", + (unsigned int)dbg->size, (unsigned int)(size - dbg->size)); + output_newline (); + } + } + return; + } + + /* pre-defined string */ + output_prefix (); + output ("memcpy ("); + output_data (dbg->target); + output (", "); + output ("%s%d", CB_PREFIX_STRING, lookup_string (dbg->value)); + output (", %u);", (unsigned int)copy_size); + output_newline (); + /* ... filled up with space */ + if (copy_size != size) { + output_prefix (); + output ("memset ("); + output_data (dbg->target); + output (" + %u, ' ', %u);", + (unsigned int)dbg->size, (unsigned int)(size - dbg->size)); + output_newline (); + } +} + static void output_file_error (struct cb_file *pfile) { @@ -7731,6 +7823,174 @@ output_alter (struct cb_alter *p) } } +/* conditions IF / WHEN / PRSENT-WHEN */ + +static void +output_if (const struct cb_if *ip) +{ + char *px; + int skip_else; +#ifdef COBC_HAS_CUTOFF_FLAG /* Note: will be removed completely in 4.x */ + int code; +#endif + if (ip->stmt1 == NULL + && ip->stmt2 == NULL) { + if (ip->statement != STMT_IF) { + output_line ("/* WHEN has code omitted */"); + } else { + output_line ("/* IF has code omitted */"); + } + return; + } + + if (ip->statement != STMT_IF) { + output_newline (); + if (ip->test == cb_true + && cb_flag_remove_unreachable) { + output_line ("/* WHEN is always TRUE */"); + } else if (ip->test == cb_false + && cb_flag_remove_unreachable) { + output_line ("/* WHEN is always FALSE */"); + } else + if (ip->test + && CB_TREE_TAG (ip->test) == CB_TAG_BINARY_OP) { + const struct cb_binary_op *bop = CB_BINARY_OP (ip->test); + cb_tree w = NULL; + if (bop->op == '!') { + w = bop->x; + } else if (bop->y) { + w = bop->y; + } else if (bop->x) { + w = bop->x; + } + if (w == cb_true) { + output_line ("/* WHEN is always %s */", + bop->op == '!' ? "FALSE" : "TRUE"); + } else if (w == cb_false) { + output_line ("/* WHEN is always %s */", + bop->op != '!' ? "FALSE" : "TRUE"); + } else if (ip->test->source_line || (w && w->source_line)) { + if (ip->test->source_line) { + w = ip->test; + } + output_source_reference (w, STMT_WHEN); + } else { + output_line ("/* WHEN */"); + } + } else if (ip->test->source_line) { + output_source_reference (ip->test, STMT_WHEN); + } else { + output_line ("/* WHEN */"); + } + output_newline (); + } +#ifdef COBC_HAS_CUTOFF_FLAG /* Note: will be removed completely in 4.x */ + gen_if_level++; + code = 0; +#endif + + /* Really PRESENT WHEN for Report field/line */ + if (ip->statement == STMT_PRESENT_WHEN + && ip->stmt1 == NULL + && ip->stmt2 != NULL) { + struct cb_field *p2 = (struct cb_field *)ip->stmt2; + const char *target; + if (p2->report_flag & COB_REPORT_LINE) { + px = (char*)CB_PREFIX_REPORT_LINE; + target = "Line"; + } else { + px = (char*)CB_PREFIX_REPORT_FIELD; + target = "Field"; + } + output_line ("/* PRESENT WHEN %s: %d */", target, p2->common.source_line); + output_prefix (); + output ("if ("); + output_cond (ip->test, 0); + output (")"); + output_newline (); + output_line ("{"); + output_line ("\t%s%d.suppress = 0;", px, p2->id); + output_line ("} else {"); + output_line ("\t%s%d.suppress = 1;", px, p2->id); + output_line ("}"); +#ifdef COBC_HAS_CUTOFF_FLAG /* Note: will be removed completely in 4.x */ + gen_if_level--; +#endif + return; + } + + if (ip->test == cb_false + && ip->stmt1 == NULL + && cb_flag_remove_unreachable) { + output_line (" /* FALSE condition and code omitted */"); + skip_else = 1; + } else { + skip_else = 0; + output_prefix (); + output ("if ("); + output_cond (ip->test, 0); + output (")"); + output_newline (); + output_block_open (); + if (ip->stmt1) { + output_stmt (ip->stmt1); + } else { + output_line ("; /* Nothing */"); + } +#ifdef COBC_HAS_CUTOFF_FLAG /* Note: will be removed completely in 4.x */ + if (gen_if_level > cb_if_cutoff) { + if (ip->stmt2) { + code = cb_id++; + output_line ("goto %s%d;", CB_PREFIX_LABEL, code); + } + } +#endif + output_block_close (); + } + + if (ip->stmt2) { +#ifdef COBC_HAS_CUTOFF_FLAG /* Note: will be removed completely in 4.x */ + if (gen_if_level <= cb_if_cutoff) { + if (!skip_else) { + output_line ("else"); + } + output_line ("{"); + output_indent_level += 2; + } + if (ip->statement == STMT_IF) { + output_line ("/* ELSE */"); + } else { + output_line ("/* WHEN */"); + } + output_stmt (ip->stmt2); + if (gen_if_level <= cb_if_cutoff) { + output_indent_level -= 2; + output_line ("}"); + } else { + output_line ("l_%d:;", CB_PREFIX_LABEL, code); + } + } +#else /* ifdef COBC_HAS_CUTOFF_FLAG */ + if (!skip_else) { + output_line ("else"); + } + output_line ("{"); + output_indent_level += 2; + if (ip->statement == STMT_IF) { + output_line ("/* ELSE */"); + } else { + output_line ("/* WHEN */"); + } + output_stmt (ip->stmt2); + output_indent_level -= 2; + output_line ("}"); +#endif + } +#ifdef COBC_HAS_CUTOFF_FLAG /* Note: will be removed completely in 4.x */ + gen_if_level--; +#endif +} + /* JSON/XML GENERATE suppress checks */ static void @@ -8283,10 +8543,251 @@ output_debug_stmts (cb_tree debug_checks) } static void -output_stmt (cb_tree x) +output_label_as_c (const struct cb_label *lp) +{ + unsigned char buff[COB_MINI_BUFF]; + unsigned char *ptr = (unsigned char *)&buff; + cob_encode_program_id ((unsigned char*)lp->orig_name, ptr, + COB_MINI_MAX, COB_FOLD_UPPER); + if (*ptr == '_') ptr++; + if (lp->flag_section) { + /* SECTION label */ + output_line ("SECTION_%s:\t%s;", ptr, "cob_nop ()"); + } else if (lp->flag_entry_for_goto) { + /* ENTRY FOR GOTO label */ + if (cb_flag_source_location) { + const char *stmnt_enum + = cb_statement_enum_name[STMT_ENTRY_FOR_GO_TO]; + output_line ("ENTRY_GOTO_%s:\tmodule->statement = %s;", + ptr, stmnt_enum); + } else { + output_line ("ENTRY_GOTO_%s:\t%s;", ptr, "cob_nop ()"); + } + } else if (lp->flag_entry) { + /* ENTRY label */ + if (cb_flag_source_location) { + const char *stmnt_enum + = cb_statement_enum_name[STMT_ENTRY]; + output_line ("ENTRY_%s:\tmodule->statement = %s;", + ptr, stmnt_enum); + } else { + output_line ("ENTRY_%s:\t%s;", ptr, "cob_nop ()"); + } + } else { + /* Paragraph label */ + /* note: paragraphs need a suffix, both to not break some macro + names, and most important to prevent duplicates: + COBOL allows multiple pagraphs with the same name, even in the + same section; C allows only one per function and with our current + generation that means one identical generated paragraph + name "per program" */ + if (cb_flag_source_location) { + const char *stmnt_enum + = cb_statement_enum_name[STMT_ENTRY]; + output_line ("PARAGRAPH_%s_l_%d:\tmodule->statement = %s;", + ptr, lp->id, stmnt_enum); + } else { + output_line ("PARAGRAPH_%s_l_%d:\t%s;", ptr, lp->id, "cob_nop ()"); + } + } +} + +static void +output_label (const struct cb_label *lp) +{ + if (lp->flag_skip_label) { + return; + } + if (cb_flag_section_exit_check + && lp->flag_section + && !lp->flag_dummy_section) { + if (last_section + && last_section->flag_declaratives + && !lp->flag_declaratives) { + last_section = NULL; + } + if (last_section != NULL) { + output_line ("cob_check_beyond_exit (%s%d);" + "\t/* prevent fall-through */", CB_PREFIX_STRING, + lookup_string (last_section->name)); + } + } + output_label_info (CB_TREE(lp), lp); + if (lp->flag_section) { + struct cb_para_label *pal; + for (pal = lp->para_label; pal; pal = pal->next) { + if (pal->para->segment > 49 + && pal->para->flag_alter) { + output_line ("label_%s%d = 0;", + CB_PREFIX_LABEL, pal->para->id); + } + } + last_segment = lp->segment; + last_section = lp; + } + if (lp->flag_begin) { + output_line ("%s%d:;", CB_PREFIX_LABEL, lp->id); + } + if (!lp->flag_dummy_exit + && !lp->flag_dummy_section + && !lp->flag_dummy_paragraph + && !lp->flag_default_handler) { + if (cb_flag_c_line_directives) { + output_cobol_info (CB_TREE(lp)); + } + if (cb_flag_c_labels) { + output_label_as_c (lp); + if (cb_flag_c_line_directives) { + output_c_info (); + } + } else { + if (cb_flag_c_line_directives) { + output_line ("cob_nop ();"); + output_c_info (); + } + } + } + + /* Check for runtime debug flag */ + if (current_prog->flag_debugging && lp->flag_is_debug_sect) { +#if 0 /* only needed for compilation to GnuCOBOL 2.0-2.2 level (later addition) */ + output_line ("if (!cob_debugging_mode)"); +#else + output_line ("if (!cob_glob_ptr->cob_debugging_mode)"); +#endif + output_line ("\tgoto %s%d;", + CB_PREFIX_LABEL, CB_LABEL (lp->exit_label)->id); + } + + if (cb_flag_trace + || cobc_wants_debug) { + output_section_info (lp); + } + + /* Check procedure debugging */ + if (current_prog->flag_gen_debug && lp->flag_real_label) { + output_stmt (cb_build_debug (cb_debug_name, + (const char*)lp->name, NULL)); + if (current_prog->all_procedure) { + output_perform_call (current_prog->all_procedure, NULL); + } else if (lp->flag_debugging_mode) { + output_perform_call (lp->debug_section, NULL); + } + } + + /* Check ALTER processing */ + if (lp->flag_alter) { + output_alter_check (lp); + } +} + +static void +output_assign (const struct cb_assign *ap) { +#ifdef COB_NON_ALIGNED /* Nonaligned */ + if (CB_TREE_CLASS (ap->var) == CB_CLASS_POINTER + || CB_TREE_CLASS (ap->val) == CB_CLASS_POINTER) { + /* Pointer assignment */ + output_block_open (); + output_line ("void *temp_ptr;"); + + /* temp_ptr = source address; */ + output_prefix (); + if (ap->val == cb_null || ap->val == cb_zero) { + /* MOVE NULL ... */ + output ("temp_ptr = 0;"); + } else if (CB_TREE_TAG (ap->val) == CB_TAG_CAST) { + /* MOVE ADDRESS OF val ... */ + const struct cb_cast *cp = CB_CAST (ap->val); + output ("temp_ptr = "); + switch (cp->cast_type) { + case CB_CAST_ADDRESS: + output_data (cp->val); + break; + case CB_CAST_PROGRAM_POINTER: + output ("cob_call_field ("); + output_param (ap->val, -1); + if (current_prog->nested_prog_list) { + gen_nested_tab = 1; + output (", cob_nest_tab, 0, %d)", + cb_fold_call); + } else { + output (", NULL, 0, %d)", + cb_fold_call); + } + break; + /* LCOV_EXCL_START */ + default: + cobc_err_msg (_("unexpected cast type: %d"), + cp->cast_type); + COBC_ABORT (); + /* LCOV_EXCL_STOP */ + } + output (";"); + } else { + /* MOVE val ... */ + output ("memcpy(&temp_ptr, "); + output_data (ap->val); + output (", sizeof(temp_ptr));"); + } + output_newline (); + + /* Destination address = temp_ptr; */ + output_prefix (); + if (CB_TREE_TAG (ap->var) == CB_TAG_CAST) { + /* SET ADDRESS OF var ... */ + const struct cb_cast *cp = CB_CAST (ap->var); + /* LCOV_EXCL_START */ + if (cp->cast_type != CB_CAST_ADDRESS) { + cobc_err_msg (_("unexpected tree type: %d"), + cp->cast_type); + COBC_ABORT (); + } + /* LCOV_EXCL_STOP */ + output_data (cp->val); + output (" = temp_ptr;"); + } else { + /* MOVE ... TO var */ + output ("memcpy("); + output_data (ap->var); + output (", &temp_ptr, sizeof(temp_ptr));"); + } + output_newline (); + + output_block_close (); + } else { + /* Numeric assignment */ + output_prefix (); + output_integer (ap->var); + output (" = "); + output_integer (ap->val); + if (inside_check == 0) { + output (";"); + output_newline (); + } else { + inside_stack[inside_check - 1] = 1; + } + } +#else /* Nonaligned */ + /* Numeric assignment */ + output_prefix (); + output_integer (ap->var); + output (" = "); + output_integer (ap->val); + if (inside_check == 0) { + output (";"); + output_newline (); + } else { + inside_stack[inside_check - 1] = 1; + } +#endif /* Nonaligned */ +} +static void +output_stmt (cb_tree x) +{ stack_id = 0; + if (x == NULL) { output_line (";"); return; @@ -8395,126 +8896,10 @@ output_stmt (cb_tree x) } break; } - case CB_TAG_LABEL: { - const struct cb_label *lp = CB_LABEL (x); - if (lp->flag_skip_label) { - break; - } - if (cb_flag_section_exit_check - && lp->flag_section - && !lp->flag_dummy_section) { - if (last_section - && last_section->flag_declaratives - && !lp->flag_declaratives) { - last_section = NULL; - } - if (last_section != NULL) { - output_line ("cob_check_beyond_exit (%s%d);" - "\t/* prevent fall-through */", CB_PREFIX_STRING, - lookup_string (last_section->name)); - } - } - output_label_info (x, lp); - if (lp->flag_section) { - struct cb_para_label *pal; - for (pal = lp->para_label; pal; pal = pal->next) { - if (pal->para->segment > 49 - && pal->para->flag_alter) { - output_line ("label_%s%d = 0;", - CB_PREFIX_LABEL, pal->para->id); - } - } - last_segment = lp->segment; - last_section = lp; - } - if (lp->flag_begin) { - output_line ("%s%d:;", CB_PREFIX_LABEL, lp->id); - } - if (!lp->flag_dummy_exit - && !lp->flag_dummy_section - && !lp->flag_dummy_paragraph) { - if (cb_flag_c_line_directives) { - output_cobol_info (x); - } - if (cb_flag_c_labels - && (lp->flag_entry || lp->flag_section)) { - /* possibly come back later adding paragraphs, too; - note: these need also a prefix to not break some macro names, - and most important: COBOL allows multiple with the same - name, even in the same section; C allows only one per - function and with our current generation that means - one identical generated paragraph name "per program" */ - unsigned char buff[COB_MINI_BUFF]; - unsigned char *ptr = (unsigned char *)&buff; - cob_encode_program_id ((unsigned char*)lp->orig_name, ptr, - COB_MINI_MAX, COB_FOLD_UPPER); - if (*ptr == '_') ptr++; - if (lp->flag_section) { - output_line ("SECTION_%s:\t%s;", ptr, "cob_nop ()"); - } else if (lp->flag_entry_for_goto) { - if (cb_flag_source_location) { - const char *stmnt_enum - = cb_statement_enum_name[STMT_ENTRY_FOR_GO_TO]; - output_line ("ENTRY_GOTO_%s:\tmodule->statement = %s;", - ptr, stmnt_enum); - } else { - output_line ("ENTRY_GOTO_%s:\t%s;", ptr, "cob_nop ()"); - } - } else { - if (cb_flag_source_location) { - const char *stmnt_enum - = cb_statement_enum_name[STMT_ENTRY]; - output_line ("ENTRY_%s:\tmodule->statement = %s;", - ptr, stmnt_enum); - } else { - output_line ("ENTRY_%s:\t%s;", ptr, "cob_nop ()"); - } - } - if (cb_flag_c_line_directives) { - output_c_info (); - } - } else { - if (cb_flag_c_line_directives) { - output_line ("cob_nop ();"); - output_c_info (); - } - } - } - - /* Check for runtime debug flag */ - if (current_prog->flag_debugging && lp->flag_is_debug_sect) { -#if 0 /* only needed for compilation to GnuCOBOL 2.0-2.2 level (later addition) */ - output_line ("if (!cob_debugging_mode)"); -#else - output_line ("if (!cob_glob_ptr->cob_debugging_mode)"); -#endif - output_line ("\tgoto %s%d;", - CB_PREFIX_LABEL, CB_LABEL (lp->exit_label)->id); - } - - if (cb_flag_trace - || cobc_wants_debug) { - output_section_info (lp); - } - - /* Check procedure debugging */ - if (current_prog->flag_gen_debug && lp->flag_real_label) { - output_stmt (cb_build_debug (cb_debug_name, - (const char*)lp->name, NULL)); - if (current_prog->all_procedure) { - output_perform_call (current_prog->all_procedure, NULL); - } else if (lp->flag_debugging_mode) { - output_perform_call (lp->debug_section, NULL); - } - } - - /* Check ALTER processing */ - if (lp->flag_alter) { - output_alter_check (lp); - } - + case CB_TAG_LABEL: + output_label (CB_LABEL(x)); break; - } + case CB_TAG_FUNCALL: output_prefix (); output_funcall (x); @@ -8525,123 +8910,31 @@ output_stmt (cb_tree x) inside_stack[inside_check - 1] = 1; } break; - case CB_TAG_ASSIGN: { - const struct cb_assign *ap = CB_ASSIGN (x); -#ifdef COB_NON_ALIGNED - /* Nonaligned */ - if (CB_TREE_CLASS (ap->var) == CB_CLASS_POINTER - || CB_TREE_CLASS (ap->val) == CB_CLASS_POINTER) { - /* Pointer assignment */ - output_block_open (); - output_line ("void *temp_ptr;"); - /* temp_ptr = source address; */ - output_prefix (); - if (ap->val == cb_null || ap->val == cb_zero) { - /* MOVE NULL ... */ - output ("temp_ptr = 0;"); - } else if (CB_TREE_TAG (ap->val) == CB_TAG_CAST) { - /* MOVE ADDRESS OF val ... */ - const struct cb_cast *cp = CB_CAST (ap->val); - output ("temp_ptr = "); - switch (cp->cast_type) { - case CB_CAST_ADDRESS: - output_data (cp->val); - break; - case CB_CAST_PROGRAM_POINTER: - output ("cob_call_field ("); - output_param (ap->val, -1); - if (current_prog->nested_prog_list) { - gen_nested_tab = 1; - output (", cob_nest_tab, 0, %d)", - cb_fold_call); - } else { - output (", NULL, 0, %d)", - cb_fold_call); - } - break; - /* LCOV_EXCL_START */ - default: - cobc_err_msg (_("unexpected cast type: %d"), - cp->cast_type); - COBC_ABORT (); - /* LCOV_EXCL_STOP */ - } - output (";"); - } else { - /* MOVE val ... */ - output ("memcpy(&temp_ptr, "); - output_data (ap->val); - output (", sizeof(temp_ptr));"); - } - output_newline (); - - /* Destination address = temp_ptr; */ - output_prefix (); - if (CB_TREE_TAG (ap->var) == CB_TAG_CAST) { - /* SET ADDRESS OF var ... */ - const struct cb_cast *cp = CB_CAST (ap->var); - /* LCOV_EXCL_START */ - if (cp->cast_type != CB_CAST_ADDRESS) { - cobc_err_msg (_("unexpected tree type: %d"), - cp->cast_type); - COBC_ABORT (); - } - /* LCOV_EXCL_STOP */ - output_data (cp->val); - output (" = temp_ptr;"); - } else { - /* MOVE ... TO var */ - output ("memcpy("); - output_data (ap->var); - output (", &temp_ptr, sizeof(temp_ptr));"); - } - output_newline (); - - output_block_close (); - } else { - /* Numeric assignment */ - output_prefix (); - output_integer (ap->var); - output (" = "); - output_integer (ap->val); - if (inside_check == 0) { - output (";"); - output_newline (); - } else { - inside_stack[inside_check - 1] = 1; - } - } -#else /* Nonaligned */ - /* Numeric assignment */ - output_prefix (); - output_integer (ap->var); - output (" = "); - output_integer (ap->val); - if (inside_check == 0) { - output (";"); - output_newline (); - } else { - inside_stack[inside_check - 1] = 1; - } -#endif /* Nonaligned */ + case CB_TAG_ASSIGN: + output_assign (CB_ASSIGN (x)); break; - } + case CB_TAG_INITIALIZE: output_initialize (CB_INITIALIZE (x)); break; + case CB_TAG_SEARCH: output_search (CB_SEARCH (x)); break; + case CB_TAG_CALL: output_call (CB_CALL (x)); break; + case CB_TAG_GOTO: output_goto (CB_GOTO (x)); break; + case CB_TAG_CANCEL: output_cancel (CB_CANCEL (x)); break; + case CB_TAG_SET_ATTR: { const struct cb_set_attr *sap = CB_SET_ATTR (x); output_set_attribute (sap->fld, sap->val_on, sap->val_off); @@ -8650,175 +8943,25 @@ output_stmt (cb_tree x) case CB_TAG_XML_PARSE: output_xml_parse (CB_XML_PARSE (x)); break; + case CB_TAG_ALTER: output_alter (CB_ALTER (x)); break; - case CB_TAG_IF: { - const struct cb_if *ip = CB_IF (x); - char *px; - int skip_else; -#ifdef COBC_HAS_CUTOFF_FLAG /* Note: will be removed completely in 4.x */ - int code; -#endif - if (ip->stmt1 == NULL - && ip->stmt2 == NULL) { - if (ip->statement != STMT_IF) { - output_line ("/* WHEN has code omitted */"); - } else { - output_line ("/* IF has code omitted */"); - } - break; - } - if (ip->statement != STMT_IF) { - output_newline (); - if (ip->test == cb_true - && cb_flag_remove_unreachable) { - output_line ("/* WHEN is always TRUE */"); - } else if (ip->test == cb_false - && cb_flag_remove_unreachable) { - output_line ("/* WHEN is always FALSE */"); - } else - if (ip->test - && CB_TREE_TAG (ip->test) == CB_TAG_BINARY_OP) { - const struct cb_binary_op *bop = CB_BINARY_OP (ip->test); - cb_tree w = NULL; - if (bop->op == '!') { - w = bop->x; - } else if (bop->y) { - w = bop->y; - } else if (bop->x) { - w = bop->x; - } - if (w == cb_true) { - output_line ("/* WHEN is always %s */", bop->op == '!'?"FALSE":"TRUE"); - } else if (w == cb_false) { - output_line ("/* WHEN is always %s */", bop->op != '!'?"FALSE":"TRUE"); - } else if (ip->test->source_line || (w && w->source_line)) { - if (ip->test->source_line) { - w = ip->test; - } - output_source_reference (w, STMT_WHEN); - } else { - output_line ("/* WHEN */"); - } - } else if (ip->test->source_line) { - output_source_reference (ip->test, STMT_WHEN); - } else { - output_line ("/* WHEN */"); - } - output_newline (); - } -#ifdef COBC_HAS_CUTOFF_FLAG /* Note: will be removed completely in 4.x */ - gen_if_level++; - code = 0; -#endif - output_prefix (); - /* Really PRESENT WHEN for Report field/line */ - if (ip->statement == STMT_PRESENT_WHEN - && ip->stmt1 == NULL - && ip->stmt2 != NULL) { - struct cb_field *p2 = (struct cb_field *)ip->stmt2; - const char *target; - if (p2->report_flag & COB_REPORT_LINE) { - px = (char*)CB_PREFIX_REPORT_LINE; - target = "Line"; - } else { - px = (char*)CB_PREFIX_REPORT_FIELD; - target = "Field"; - } - output_line ("/* PRESENT WHEN %s: %d */", target, p2->common.source_line); - output_prefix (); - output ("if ("); - output_cond (ip->test, 0); - output (")"); - output_newline (); - output_line ("{"); - output_line ("\t%s%d.suppress = 0;", px, p2->id); - output_line ("} else {"); - output_line ("\t%s%d.suppress = 1;", px, p2->id); - output_line ("}"); -#ifdef COBC_HAS_CUTOFF_FLAG /* Note: will be removed completely in 4.x */ - gen_if_level--; -#endif - break; - } - if (ip->test == cb_false - && ip->stmt1 == NULL - && cb_flag_remove_unreachable) { - output_line (" /* FALSE condition and code omitted */"); - skip_else = 1; - } else { - skip_else = 0; - output ("if ("); - output_cond (ip->test, 0); - output (")"); - output_newline (); - output_block_open (); - if (ip->stmt1) { - output_stmt (ip->stmt1); - } else { - output_line ("; /* Nothing */"); - } -#ifdef COBC_HAS_CUTOFF_FLAG /* Note: will be removed completely in 4.x */ - if (gen_if_level > cb_if_cutoff) { - if (ip->stmt2) { - code = cb_id++; - output_line ("goto %s%d;", CB_PREFIX_LABEL, code); - } - } -#endif - output_block_close (); - } -#ifdef COBC_HAS_CUTOFF_FLAG /* Note: will be removed completely in 4.x */ - if (ip->stmt2) { - if (gen_if_level <= cb_if_cutoff) { - if (!skip_else) { - output_line ("else"); - } - output_line ("{"); - output_indent_level += 2; - } - if (ip->statement == STMT_IF) { - output_line ("/* ELSE */"); - } else { - output_line ("/* WHEN */"); - } - output_stmt (ip->stmt2); - if (gen_if_level <= cb_if_cutoff) { - output_indent_level -= 2; - output_line ("}"); - } else { - output_line ("l_%d:;", CB_PREFIX_LABEL, code); - } - } - gen_if_level--; -#else /* ifdef COBC_HAS_CUTOFF_FLAG */ - if (ip->stmt2) { - if (!skip_else) { - output_line ("else"); - } - output_line ("{"); - output_indent_level += 2; - if (ip->statement == STMT_IF) { - output_line ("/* ELSE */"); - } else { - output_line ("/* WHEN */"); - } - output_stmt (ip->stmt2); - output_indent_level -= 2; - output_line ("}"); - } -#endif + + case CB_TAG_IF: + output_if CB_IF (x); break; - } + case CB_TAG_PERFORM: output_perform (CB_PERFORM (x)); break; + /* "common" CONTINUE, note: CONTINUE AFTER exp SECONDS is already translated into a funcall */ case CB_TAG_CONTINUE: output_line (";"); break; + case CB_TAG_LIST: if (cb_flag_extra_brace) { output_block_open (); @@ -8830,9 +8973,11 @@ output_stmt (cb_tree x) output_block_close (); } break; + case CB_TAG_REFERENCE: output_stmt (CB_REFERENCE(x)->value); break; + case CB_TAG_DIRECT: if (CB_DIRECT (x)->flag_is_direct) { if (CB_DIRECT (x)->flag_new_line) { @@ -8848,97 +8993,14 @@ output_stmt (cb_tree x) /* setting DEBUG-ITEM */ case CB_TAG_DEBUG: if (current_prog->flag_gen_debug) { - const struct cb_debug *dbg = CB_DEBUG (x); - const size_t size = cb_code_field (dbg->target)->size; - const size_t copy_size = dbg->size > size ? size : dbg->size; - if (dbg->value) { - /* pre-defined string */ - output_prefix (); - output ("memcpy ("); - output_data (dbg->target); - output (", "); - output ("%s%d", CB_PREFIX_STRING, lookup_string (dbg->value)); - output (", %u);", (unsigned int)copy_size); - output_newline (); - /* ... filled up with space */ - if (copy_size != size) { - output_prefix (); - output ("memset ("); - output_data (dbg->target); - output (" + %u, ' ', %u);", - (unsigned int)dbg->size, (unsigned int)(size - dbg->size)); - output_newline (); - } - } else { - /* content of variable */ - struct cb_field *f = CB_FIELD_PTR (dbg->fld); - /* address may change so we may have NULL or invalid pointer */ - if (f->flag_item_based || f->storage == CB_STORAGE_LINKAGE) { -#if 0 /* FIXME: this should be replaced in 4.x by a call to libcob - which checks for NULL, and for invalid access via handler, - then outputs the appropriate value */ - struct cb_field * ff = real_field_founder (f); - output_prefix (); - output ("cob_set_verified_data ("); - output_data (dbg->target); - output (", "); - output_data (CB_TREE(ff)); - output (", " CB_FMT_LLU ", %u); ", f->offset, size); - output_newline (); -#else - - const char *null_rep = ""; - f = real_field_founder (f); - /* in this case - pre-fill with space, then set var / null_rep */ - output_prefix (); - output ("memset ("); - output_data (dbg->target); - output (", ' ', %u);", (unsigned int)size); - output_newline (); - output_prefix (); - output ("if ("); - output_data (CB_TREE (f)); - output (" == NULL)"); - output_newline (); - output_prefix (); - output ("\t""memcpy ("); - output_data (dbg->target); - output (", %s%d", CB_PREFIX_STRING, lookup_string (null_rep)); - output (", %u);", (unsigned int)strlen (null_rep)); - output_newline (); - output_line ("else"); - output_prefix (); - output ("\t""memcpy ("); - output_data (dbg->target); - output (", "); - output_data (dbg->fld); - output (", %u);", (unsigned int)copy_size); - output_newline (); -#endif - } else { - /* normal field without changing address, copy data up to max*/ - output ("memcpy ("); - output_data (dbg->target); - output (", "); - output_data (dbg->fld); - output (", %u);", (unsigned int)copy_size); - output_newline (); - /* ... filled up with space */ - if (copy_size != size) { - output_prefix (); - output ("memset ("); - output_data (dbg->target); - output (" + %u, ' ', %u);", - (unsigned int)dbg->size, (unsigned int)(size - dbg->size)); - output_newline (); - } - } - } + output_debug_item (CB_DEBUG (x)); } break; + case CB_TAG_DEBUG_CALL: output_perform_call (CB_DEBUG_CALL(x)->target, NULL); break; + case CB_TAG_ML_SUPPRESS_CHECKS: output_ml_suppress_checks (CB_ML_SUPPRESS_CHECKS (x)); break; diff --git a/cobc/parser.y b/cobc/parser.y index e01ce5fbb..85662c481 100644 --- a/cobc/parser.y +++ b/cobc/parser.y @@ -2203,7 +2203,7 @@ static void error_if_following_every_clause (void) { if (ml_suppress_list - && CB_ML_SUPPRESS (CB_VALUE (ml_suppress_list))->target == CB_ML_SUPPRESS_TYPE) { + && CB_ML_SUPPRESS (CB_VALUE (ml_suppress_list))->target == CB_ML_SUPPRESS_TYPE) { cb_error (_("WHEN clause must follow EVERY clause")); } } @@ -2238,9 +2238,9 @@ add_when_to_ml_suppress_conds (cb_tree when_list) */ if (ml_suppress_list) { last_suppress_clause = CB_ML_SUPPRESS (CB_VALUE (ml_suppress_list)); - if ((last_suppress_clause->target == CB_ML_SUPPRESS_IDENTIFIER - || last_suppress_clause->target == CB_ML_SUPPRESS_TYPE) - && !last_suppress_clause->when_list) { + if ( (last_suppress_clause->target == CB_ML_SUPPRESS_IDENTIFIER + || last_suppress_clause->target == CB_ML_SUPPRESS_TYPE) + && !last_suppress_clause->when_list) { last_suppress_clause->when_list = when_list; return; } @@ -15719,7 +15719,7 @@ search_body: table_name _search_varying _search_at_end search_whens { - cb_emit_search ($1, $2, $3, $4); + $$ = cb_emit_search ($1, $2, $3, $4); } ; @@ -15728,7 +15728,7 @@ search_all_body: WHEN expr statement_list { - cb_emit_search_all ($1, $2, $4, $5); + $$ = cb_emit_search_all ($1, $2, $4, $5); } ; @@ -15779,12 +15779,26 @@ _end_search: { TERMINATOR_WARNING ($-2, SEARCH); } -| END_SEARCH +| END_SEARCH end_search_pos_token { + cb_tree x = $-0; + if (x) { + struct cb_search *p = CB_SEARCH ($-0); + if (p->at_end == NULL) { + cb_tree brk = cb_build_direct ("break;", 0); + p->at_end = CB_BUILD_PAIR ($2, brk); + } + } TERMINATOR_CLEAR ($-2, SEARCH); } ; +end_search_pos_token: + { + $$ = cb_build_comment ("END-SEARCH"); + } +; + /* SEND statement (COMMUNICATION SECTION) */ @@ -17903,6 +17917,12 @@ _count_in: /* Expressions */ +/* CHECKME: How can we integrate source references here + to correctly attach #line directives in the code + within codegen.c (output_cond) ? + Possibly directly add in push_expr? + This may also allows us to drop cb_exp_line */ + condition: expr { diff --git a/cobc/tree.h b/cobc/tree.h index 29cb4014e..f00e290e3 100644 --- a/cobc/tree.h +++ b/cobc/tree.h @@ -2474,9 +2474,9 @@ extern void cb_emit_return (cb_tree, cb_tree); extern void cb_emit_rollback (void); -extern void cb_emit_search (cb_tree, cb_tree, +extern cb_tree cb_emit_search (cb_tree, cb_tree, cb_tree, cb_tree); -extern void cb_emit_search_all (cb_tree, cb_tree, +extern cb_tree cb_emit_search_all (cb_tree, cb_tree, cb_tree, cb_tree); extern void cb_emit_setenv (cb_tree, cb_tree); diff --git a/cobc/typeck.c b/cobc/typeck.c index 0859d07b8..19ba8d1a8 100644 --- a/cobc/typeck.c +++ b/cobc/typeck.c @@ -87,11 +87,6 @@ struct expr_node { #define dpush(x) CB_ADD_TO_CHAIN (x, decimal_stack) -#define cb_emit(x) \ - current_statement->body = cb_list_add (current_statement->body, x) -#define cb_emit_list(l) \ - current_statement->body = cb_list_append (current_statement->body, l) - /* Global variables */ cb_tree cb_debug_item; @@ -1045,6 +1040,20 @@ cb_check_integer_value (cb_tree x) return cb_error_node; } +static COB_INLINE COB_A_INLINE cb_tree +cb_emit (cb_tree x) +{ + current_statement->body = cb_list_add (current_statement->body, x); + return x; +} + +static COB_INLINE COB_A_INLINE cb_tree +cb_emit_list (cb_tree l) +{ + current_statement->body = cb_list_append (current_statement->body, l); + return l; +} + static void cb_emit_incompat_data_checks (cb_tree x) { @@ -12689,22 +12698,22 @@ cb_build_search_all (cb_tree table, cb_tree cond) return cb_build_cond (c1); } -void +cb_tree cb_emit_search (cb_tree table, cb_tree varying, cb_tree at_end, cb_tree whens) { if (cb_validate_one (table) || cb_validate_one (varying) || whens == cb_error_node) { - return; + return NULL; } whens = cb_list_reverse (whens); if (at_end) { cb_check_needs_break (CB_PAIR_Y (at_end)); } - cb_emit (cb_build_search (0, table, varying, at_end, whens)); + return cb_emit (cb_build_search (0, table, varying, at_end, whens)); } -void +cb_tree cb_emit_search_all (cb_tree table, cb_tree at_end, cb_tree when, cb_tree stmts) { cb_tree x; @@ -12712,19 +12721,19 @@ cb_emit_search_all (cb_tree table, cb_tree at_end, cb_tree when, cb_tree stmts) if (cb_validate_one (table) || when == cb_error_node) { - return; + return NULL; } x = cb_build_search_all (table, when); if (!x) { - return; + return NULL; } stmt_lis = cb_check_needs_break (stmts); if (at_end) { cb_check_needs_break (CB_PAIR_Y (at_end)); } - cb_emit (cb_build_search (1, table, NULL, at_end, - cb_build_if (x, stmt_lis, NULL, STMT_WHEN))); + x = cb_build_if (x, stmt_lis, NULL, STMT_WHEN); + return cb_emit (cb_build_search (1, table, NULL, at_end, x)); } /* SET statement */ diff --git a/tests/testsuite.src/run_misc.at b/tests/testsuite.src/run_misc.at index 8d060acf5..29fa684b2 100644 --- a/tests/testsuite.src/run_misc.at +++ b/tests/testsuite.src/run_misc.at @@ -7615,6 +7615,10 @@ AT_DATA([caller.cob], [ WHEN tkey(tidx) = 'C' CONTINUE END-SEARCH + SEARCH ALL tentry + WHEN tkey(tidx) = 'X' + CONTINUE + END-SEARCH *> STOP RUN. ]) @@ -7869,7 +7873,9 @@ Program-Id: caller Statement: MOVE Line: 51 Program-Id: caller Statement: SEARCH ALL Line: 55 Program-Id: caller Statement: WHEN Line: 58 Program-Id: caller Statement: CONTINUE Line: 59 -Program-Id: caller Statement: STOP RUN Line: 62 +Program-Id: caller Statement: SEARCH ALL Line: 61 +Program-Id: caller Statement: AT END Line: 64 +Program-Id: caller Statement: STOP RUN Line: 66 ]) AT_CHECK([$COBC -ftraceall callee1.cob], [0], [], []) @@ -7999,7 +8005,11 @@ Program-Id: caller SEARCH VARYING Line: Program-Id: caller SEARCH VARYING Line: 55 Program-Id: caller WHEN Line: 58 Program-Id: caller CONTINUE Line: 59 -Program-Id: caller STOP RUN Line: 62 +Program-Id: caller SEARCH ALL Line: 61 +Program-Id: caller SEARCH VARYING Line: 61 +Program-Id: caller SEARCH VARYING Line: 61 +Program-Id: caller AT END Line: 64 +Program-Id: caller STOP RUN Line: 66 ]) AT_CLEANUP