From bcd743e7380eecbf9d9a81a646fec7c0a972b4a0 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Fri, 18 Nov 2022 20:08:03 +0000 Subject: [PATCH] Store pragmas with design units. Issue #572 --- src/lexer.l | 4 ++++ src/lower.c | 20 ++++++++++++---- src/object.c | 2 +- src/object.h | 5 ++-- src/opt.c | 1 - src/opt.h | 1 - src/parse.c | 42 +++++++++++++++++++-------------- src/rt/cover.c | 55 +++++++++++++++++++++++++++++++++++++++++--- src/rt/cover.h | 2 ++ src/scan.h | 2 ++ src/tree.c | 38 +++++++++++++++++++++++------- src/tree.h | 12 ++++++++++ test/lower/cover.vhd | 3 +++ test/test_lower.c | 6 +++++ test/test_parse.c | 13 +++++++---- test/test_util.c | 1 - 16 files changed, 163 insertions(+), 44 deletions(-) diff --git a/src/lexer.l b/src/lexer.l index edaadce0..0bee065b 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -99,6 +99,8 @@ TICK \' PRAGMA --[ \t]* SYNTH_OFF {PRAGMA}(?i:synthesis)[ \t]+(?i:translate_off).* SYNTH_ON {PRAGMA}(?i:synthesis)[ \t]+(?i:translate_on).* +COVERAGE_OFF {PRAGMA}(?i:coverage)[ \t]+(?i:off).* +COVERAGE_ON {PRAGMA}(?i:coverage)[ \t]+(?i:on).* %x COMMENT C_COMMENT @@ -212,6 +214,8 @@ PARAMETER ?i:parameter {SYNTH_OFF} { TOKEN(tSYNTHOFF); } {SYNTH_ON} { TOKEN(tSYNTHON); } +{COVERAGE_OFF} { TOKEN(tCOVERAGEOFF); } +{COVERAGE_ON} { TOKEN(tCOVERAGEON); } {COMMENT} { BEGIN(COMMENT); } diff --git a/src/lower.c b/src/lower.c index dd1cc4e4..edc27810 100644 --- a/src/lower.c +++ b/src/lower.c @@ -1500,8 +1500,9 @@ static void lower_branch_coverage(tree_t b, unsigned int flags, { assert(cover_enabled(cover_tags, COVER_MASK_BRANCH)); - int32_t tag = cover_add_tag(b, NULL, cover_tags, TAG_BRANCH, flags)->tag; - emit_cover_branch(hit_reg, tag); + cover_tag_t *tag = cover_add_tag(b, NULL, cover_tags, TAG_BRANCH, flags); + if (tag != NULL) + emit_cover_branch(hit_reg, tag->tag); } static int32_t lower_toggle_tag_for(type_t type, tree_t where, ident_t prefix, int dims) @@ -6450,8 +6451,9 @@ static void lower_stmt(tree_t stmt, loop_stack_t *loops) cover_push_scope(cover_tags, stmt); if (cover_enabled(cover_tags, COVER_MASK_STMT) && cover_is_stmt(stmt)) { - int32_t tag = cover_add_tag(stmt, NULL, cover_tags, TAG_STMT, 0)->tag; - emit_cover_stmt(tag); + cover_tag_t *tag = cover_add_tag(stmt, NULL, cover_tags, TAG_STMT, 0); + if (tag != NULL) + emit_cover_stmt(tag->tag); } emit_debug_info(tree_loc(stmt)); @@ -7615,6 +7617,14 @@ static void lower_instantiated_package(tree_t decl, vcode_unit_t context) lower_put_vcode_obj(tree_decl(decl, i), var | INSTANCE_BIT, top_scope); } +static void lower_hier_decl(tree_t decl) +{ + top_scope->hier = decl; + + if (cover_tags != NULL) + cover_exclude_from_pragmas(cover_tags, tree_ref(decl)); +} + static void lower_decl(tree_t decl, vcode_unit_t context) { PUSH_DEBUG_INFO(decl); @@ -7642,7 +7652,7 @@ static void lower_decl(tree_t decl, vcode_unit_t context) break; case T_HIER: - top_scope->hier = decl; + lower_hier_decl(decl); break; case T_TYPE_DECL: diff --git a/src/object.c b/src/object.c index a8261b1f..4521effe 100644 --- a/src/object.c +++ b/src/object.c @@ -57,7 +57,7 @@ static const char *item_text_map[] = { "I_GENERICS", "I_PARAMS", "I_GENMAPS", "I_WAVES", "I_CONDS", "I_TYPE", "I_SUBKIND", "I_DELAY", "I_REJECT", "I_POS", "I_REF", "I_FILE_MODE", "I_ASSOCS", "I_CONTEXT", "I_TRIGGERS", - "????", "I_CLASS", "I_RANGES", "I_NAME", "????", + "????", "I_CLASS", "I_RANGES", "I_NAME", "I_PRAGMAS", "I_DVAL", "I_SPEC", "????", "I_INDEXCON", "I_BASE", "I_ELEM", "I_FILE", "I_ACCESS", "I_RESOLUTION", "I_RESULT", "I_UNITS", "I_LITERALS", "I_DIMS", "I_FIELDS", "????", diff --git a/src/object.h b/src/object.h index 55dcd0d6..95f7a854 100644 --- a/src/object.h +++ b/src/object.h @@ -63,7 +63,7 @@ typedef uint64_t imask_t; #define I_CLASS ONE_HOT(26) #define I_RANGES ONE_HOT(27) #define I_NAME ONE_HOT(28) -// Unused ONE_HOT(29) +#define I_PRAGMAS ONE_HOT(29) #define I_DVAL ONE_HOT(30) #define I_SPEC ONE_HOT(31) // Unused ONE_HOT(32) @@ -106,7 +106,8 @@ typedef uint64_t imask_t; | I_WAVES | I_CONDS | I_TRIGGERS | I_CONSTR \ | I_PARAMS | I_GENMAPS | I_ASSOCS | I_CONTEXT \ | I_LITERALS | I_FIELDS | I_UNITS | I_CHARS \ - | I_DIMS | I_RANGES | I_INDEXCON | I_PARTS) + | I_DIMS | I_RANGES | I_INDEXCON | I_PARTS \ + | I_PRAGMAS) #define ITEM_INT64 (I_POS | I_IVAL) #define ITEM_INT32 (I_SUBKIND | I_CLASS | I_FLAGS) #define ITEM_DOUBLE (I_DVAL) diff --git a/src/opt.c b/src/opt.c index 2d6ea4fe..edf5a8a5 100644 --- a/src/opt.c +++ b/src/opt.c @@ -119,7 +119,6 @@ void set_default_options(void) opt_set_int(OPT_IGNORE_TIME, 0); opt_set_int(OPT_VERBOSE, 0); opt_set_int(OPT_RT_PROFILE, 0); - opt_set_int(OPT_SYNTHESIS, 0); opt_set_int(OPT_MISSING_BODY, 1); opt_set_int(OPT_ERROR_LIMIT, -1); opt_set_int(OPT_IEEE_WARNINGS, 1); diff --git a/src/opt.h b/src/opt.h index 7fa0cf2c..99ecc84a 100644 --- a/src/opt.h +++ b/src/opt.h @@ -38,7 +38,6 @@ typedef enum { OPT_VERBOSE, OPT_RT_PROFILE, OPT_RT_TRACE, - OPT_SYNTHESIS, OPT_MISSING_BODY, OPT_IEEE_WARNINGS, OPT_ARENA_SIZE, diff --git a/src/parse.c b/src/parse.c index 3e8e6fef..c9c85175 100644 --- a/src/parse.c +++ b/src/parse.c @@ -93,9 +93,9 @@ static yylval_t last_lval; static token_t opt_hist[8]; static int nopt_hist = 0; static cond_state_t *cond_state = NULL; -static bool translate_on = true; static nametab_t *nametab = NULL; static bool bootstrapping = false; +static tree_list_t pragmas = AINIT; loc_t yylloc; int yylex(void); @@ -171,6 +171,7 @@ static tree_t p_record_element_constraint(type_t base); static bool consume(token_t tok); static bool optional(token_t tok); +static token_t conditional_yylex(void); static void _pop_state(const state_t *s) { @@ -225,6 +226,16 @@ static const char *token_str(token_t tok) return token_strs[tok]; } +static token_t skip_pragma(pragma_kind_t kind) +{ + tree_t p = tree_new(T_PRAGMA); + tree_set_loc(p, &yylloc); + tree_set_subkind(p, kind); + + APUSH(pragmas, p); + return conditional_yylex(); +} + static token_t conditional_yylex(void) { const token_t token = yylex(); @@ -311,23 +322,17 @@ static token_t conditional_yylex(void) return conditional_yylex(); } - case tSYNTHOFF: - { - BEGIN("synthesis translate_off"); - - if (opt_get_int(OPT_SYNTHESIS)) - translate_on = false; + case tSYNTHON: + return skip_pragma(PRAGMA_SYNTHESIS_ON); - return conditional_yylex(); - } + case tSYNTHOFF: + return skip_pragma(PRAGMA_SYNTHESIS_OFF); - case tSYNTHON: - { - BEGIN("synthesis translate_off"); + case tCOVERAGEON: + return skip_pragma(PRAGMA_COVERAGE_ON); - translate_on = true; - return conditional_yylex(); - } + case tCOVERAGEOFF: + return skip_pragma(PRAGMA_COVERAGE_OFF); case tEOF: if (cond_state != NULL) { @@ -338,7 +343,7 @@ static token_t conditional_yylex(void) return tEOF; default: - if (translate_on && (cond_state == NULL || cond_state->result)) + if (cond_state == NULL || cond_state->result) return token; else return conditional_yylex(); @@ -10282,6 +10287,10 @@ tree_t parse(void) nametab_finish(nametab); nametab = NULL; + for (int i = 0; i < pragmas.count; i++) + tree_add_pragma(unit, pragmas.items[i]); + ACLEAR(pragmas); + if (tree_kind(unit) == T_DESIGN_UNIT) return NULL; @@ -10290,7 +10299,6 @@ tree_t parse(void) void reset_vhdl_parser(void) { - translate_on = true; bootstrapping = opt_get_int(OPT_BOOTSTRAP); if (tokenq == NULL) { diff --git a/src/rt/cover.c b/src/rt/cover.c index 69501cda..b910ba02 100644 --- a/src/rt/cover.c +++ b/src/rt/cover.c @@ -21,10 +21,10 @@ #include "cover.h" #include "lib.h" #include "opt.h" -#include "type.h" -#include "rt.h" +#include "rt/model.h" +#include "rt/rt.h" #include "rt/structs.h" -#include "model.h" +#include "type.h" #include #include @@ -40,7 +40,13 @@ #define MARGIN_LEFT "20%%" #define SIDEBAR_WIDTH "15%%" +typedef struct { + int start; + int end; +} line_range_t; + typedef A(cover_tag_t) tag_array_t; +typedef A(line_range_t) range_array_t; typedef struct _cover_report_ctx cover_report_ctx_t; typedef struct _cover_file cover_file_t; @@ -51,6 +57,7 @@ typedef struct _cover_scope { int branch_label; int stmt_label; cover_scope_t *parent; + range_array_t exclude_lines; } cover_scope_t; struct _cover_tagging { @@ -172,6 +179,19 @@ cover_tag_t *cover_add_tag(tree_t t, ident_t suffix, cover_tagging_t *ctx, int *cnt; assert (ctx != NULL); + // TODO: need a better way to determine if a scope comes from an instance + cover_scope_t *excl_scope = ctx->top_scope; + for (; excl_scope->exclude_lines.count == 0 && excl_scope->parent; + excl_scope = excl_scope->parent) + ; + + const loc_t *loc = tree_loc(t); + for (int i = 0; i < excl_scope->exclude_lines.count; i++) { + line_range_t *lr = &(excl_scope->exclude_lines.items[i]); + if (loc->first_line > lr->start && loc->first_line <= lr->end) + return NULL; + } + switch (kind) { case TAG_STMT: cnt = &(ctx->next_stmt_tag); break; case TAG_BRANCH: cnt = &(ctx->next_branch_tag); break; @@ -362,6 +382,8 @@ void cover_pop_scope(cover_tagging_t *tagging) assert(tagging->top_scope != NULL); + ACLEAR(tagging->top_scope->exclude_lines); + cover_scope_t *tmp = tagging->top_scope->parent; free(tagging->top_scope); tagging->top_scope = tmp; @@ -374,6 +396,33 @@ void cover_pop_scope(cover_tagging_t *tagging) assert(tagging->hier != NULL); } +void cover_exclude_from_pragmas(cover_tagging_t *tagging, tree_t unit) +{ + assert(tagging->top_scope != NULL); + + range_array_t *excl = &(tagging->top_scope->exclude_lines); + bool state = true; + const int npragmas = tree_pragmas(unit); + for (int i = 0; i < npragmas; i++) { + tree_t p = tree_pragma(unit, i); + const pragma_kind_t kind = tree_subkind(p); + if (kind != PRAGMA_COVERAGE_OFF && kind != PRAGMA_COVERAGE_ON) + continue; + + if (kind == PRAGMA_COVERAGE_OFF && state) { + line_range_t lr = { tree_loc(p)->first_line, INT_MAX }; + APUSH(*excl, lr); + state = false; + } + else if (kind == PRAGMA_COVERAGE_ON && !state) { + assert(excl->count > 0); + assert(excl->items[excl->count - 1].end == INT_MAX); + excl->items[excl->count - 1].end = tree_loc(p)->first_line; + state = true; + } + } +} + void cover_read_header(fbuf_t *f, cover_tagging_t *tagging) { assert(tagging != NULL); diff --git a/src/rt/cover.h b/src/rt/cover.h index ea65d947..f091542f 100644 --- a/src/rt/cover.h +++ b/src/rt/cover.h @@ -120,6 +120,8 @@ void cover_reset_scope(cover_tagging_t *tagging, ident_t hier); void cover_push_scope(cover_tagging_t *tagging, tree_t t); void cover_pop_scope(cover_tagging_t *tagging); +void cover_exclude_from_pragmas(cover_tagging_t *tagging, tree_t unit); + bool cover_is_stmt(tree_t t); fbuf_t *cover_open_lib_file(tree_t top, fbuf_mode_t mode, bool check_null); diff --git a/src/scan.h b/src/scan.h index 46fbcfde..bb940bd6 100644 --- a/src/scan.h +++ b/src/scan.h @@ -203,6 +203,8 @@ typedef enum { tAT, tQUESTION, tPARAMETER, + tCOVERAGEON, + tCOVERAGEOFF, } token_t; #endif // _SCAN_H diff --git a/src/tree.c b/src/tree.c index 944cd0c1..b43d479f 100644 --- a/src/tree.c +++ b/src/tree.c @@ -27,10 +27,10 @@ static const imask_t has_map[T_LAST_TREE_KIND] = { // T_ENTITY - (I_IDENT | I_PORTS | I_GENERICS | I_CONTEXT | I_DECLS | I_STMTS), + (I_IDENT | I_PORTS | I_GENERICS | I_CONTEXT | I_DECLS | I_STMTS | I_PRAGMAS), // T_ARCH - (I_IDENT | I_IDENT2 | I_DECLS | I_STMTS | I_CONTEXT | I_PRIMARY), + (I_IDENT | I_IDENT2 | I_DECLS | I_STMTS | I_CONTEXT | I_PRIMARY | I_PRAGMAS), // T_PORT_DECL (I_IDENT | I_VALUE | I_TYPE | I_SUBKIND | I_CLASS | I_FLAGS), @@ -63,7 +63,7 @@ static const imask_t has_map[T_LAST_TREE_KIND] = { (I_IDENT | I_VALUE | I_TARGET), // T_PACKAGE - (I_IDENT | I_DECLS | I_CONTEXT | I_GENERICS | I_GENMAPS), + (I_IDENT | I_DECLS | I_CONTEXT | I_GENERICS | I_GENMAPS | I_PRAGMAS), // T_SIGNAL_ASSIGN (I_IDENT | I_TARGET | I_WAVES | I_REJECT), @@ -108,7 +108,7 @@ static const imask_t has_map[T_LAST_TREE_KIND] = { (I_IDENT), // T_PACK_BODY - (I_IDENT | I_DECLS | I_CONTEXT | I_PRIMARY), + (I_IDENT | I_DECLS | I_CONTEXT | I_PRIMARY | I_PRAGMAS), // T_FUNC_BODY (I_IDENT | I_DECLS | I_STMTS | I_PORTS | I_TYPE | I_FLAGS | I_GENERICS @@ -224,13 +224,13 @@ static const imask_t has_map[T_LAST_TREE_KIND] = { (I_CONTEXT), // T_CONFIGURATION - (I_IDENT | I_IDENT2 | I_DECLS | I_PRIMARY), + (I_IDENT | I_IDENT2 | I_DECLS | I_PRIMARY | I_PRAGMAS), // T_PROT_BODY (I_IDENT | I_TYPE | I_DECLS), // T_CONTEXT - (I_CONTEXT | I_IDENT), + (I_CONTEXT | I_IDENT | I_PRAGMAS), // T_CONTEXT_REF (I_IDENT | I_REF), @@ -278,7 +278,7 @@ static const imask_t has_map[T_LAST_TREE_KIND] = { (I_IDENT | I_STMTS | I_DECLS), // T_PACK_INST - (I_IDENT | I_REF | I_DECLS | I_CONTEXT | I_GENERICS | I_GENMAPS), + (I_IDENT | I_REF | I_DECLS | I_CONTEXT | I_GENERICS | I_GENMAPS | I_PRAGMAS), // T_GENERIC_DECL (I_IDENT | I_VALUE | I_TYPE | I_CLASS | I_SUBKIND | I_FLAGS | I_PORTS), @@ -323,6 +323,9 @@ static const imask_t has_map[T_LAST_TREE_KIND] = { // T_PATH_ELT (I_SUBKIND | I_IDENT | I_VALUE), + + // T_PRAGMA + (I_SUBKIND), }; static const char *kind_text_map[T_LAST_TREE_KIND] = { @@ -358,7 +361,7 @@ static const char *kind_text_map[T_LAST_TREE_KIND] = { "T_PARAM_DECL", "T_EXTERNAL_NAME", "T_FORCE", "T_RELEASE", "T_PROT_REF", "T_MATCH_CASE", "T_FUNC_INST", "T_PROC_INST", "T_ELEM_CONSTRAINT", - "T_STRING", "T_PATH_ELT", + "T_STRING", "T_PATH_ELT", "T_PRAGMA", }; static const change_allowed_t change_allowed[] = { @@ -939,6 +942,25 @@ void tree_add_context(tree_t t, tree_t ctx) object_write_barrier(&(t->object), &(ctx->object)); } +unsigned tree_pragmas(tree_t t) +{ + item_t *item = lookup_item(&tree_object, t, I_PRAGMAS); + return obj_array_count(item->obj_array); +} + +tree_t tree_pragma(tree_t t, unsigned n) +{ + item_t *item = lookup_item(&tree_object, t, I_PRAGMAS); + return tree_array_nth(item, n); +} + +void tree_add_pragma(tree_t t, tree_t p) +{ + assert(p->object.kind == T_PRAGMA); + tree_array_add(lookup_item(&tree_object, t, I_PRAGMAS), p); + object_write_barrier(&(t->object), &(p->object)); +} + unsigned tree_assocs(tree_t t) { item_t *item = lookup_item(&tree_object, t, I_ASSOCS); diff --git a/src/tree.h b/src/tree.h index 2fd4a4ad..a727c7d3 100644 --- a/src/tree.h +++ b/src/tree.h @@ -224,6 +224,13 @@ typedef enum { IMPLICIT_GUARD, } implicit_kind_t; +typedef enum { + PRAGMA_COVERAGE_ON, + PRAGMA_COVERAGE_OFF, + PRAGMA_SYNTHESIS_ON, + PRAGMA_SYNTHESIS_OFF, +} pragma_kind_t; + typedef enum tree_kind { T_ENTITY, T_ARCH, @@ -323,6 +330,7 @@ typedef enum tree_kind { T_ELEM_CONSTRAINT, T_STRING, T_PATH_ELT, + T_PRAGMA, T_LAST_TREE_KIND } tree_kind_t; @@ -446,6 +454,10 @@ unsigned tree_contexts(tree_t t); tree_t tree_context(tree_t t, unsigned n); void tree_add_context(tree_t t, tree_t ctx); +unsigned tree_pragmas(tree_t t); +tree_t tree_pragma(tree_t t, unsigned n); +void tree_add_pragma(tree_t t, tree_t p); + unsigned tree_assocs(tree_t t); tree_t tree_assoc(tree_t t, unsigned n); void tree_add_assoc(tree_t t, tree_t a); diff --git a/test/lower/cover.vhd b/test/lower/cover.vhd index 7c01d507..80349031 100644 --- a/test/lower/cover.vhd +++ b/test/lower/cover.vhd @@ -17,6 +17,9 @@ begin if s = 1 or s > 10 then v := 2; end if; + -- coverage off + s <= 1; + -- coverage on wait; end process; diff --git a/test/test_lower.c b/test/test_lower.c index 941f907e..db5a8836 100644 --- a/test/test_lower.c +++ b/test/test_lower.c @@ -2046,6 +2046,12 @@ START_TEST(test_cover) CHECK_BB(2); EXPECT_BB(3) = { + { VCODE_OP_VAR_UPREF, .name = "S", .hops = 1 }, + { VCODE_OP_LOAD_INDIRECT }, + { VCODE_OP_CONST, .value = 1 }, + { VCODE_OP_CONST, .value = 0 }, + { VCODE_OP_CONST, .value = 1 }, + { VCODE_OP_SCHED_WAVEFORM }, { VCODE_OP_COVER_STMT, .tag = 3 }, { VCODE_OP_WAIT, .target = 4 } }; diff --git a/test/test_parse.c b/test/test_parse.c index bd697219..9fcaba01 100644 --- a/test/test_parse.c +++ b/test/test_parse.c @@ -3431,8 +3431,6 @@ END_TEST; START_TEST(test_synth) { - opt_set_int(OPT_SYNTHESIS, 1); - input_from_file(TESTDIR "/parse/synth.vhd"); tree_t e = parse(); @@ -3443,9 +3441,14 @@ START_TEST(test_synth) tree_t a = parse(); fail_if(a == NULL); fail_unless(tree_kind(a) == T_ARCH); - fail_unless(tree_decls(a) == 1); - fail_unless(tree_ident(tree_decl(a, 0)) == ident_new("Y")); - fail_unless(tree_stmts(a) == 1); + fail_unless(tree_decls(a) == 2); + fail_unless(tree_ident(tree_decl(a, 1)) == ident_new("Y")); + fail_unless(tree_stmts(a) == 2); + fail_unless(tree_pragmas(a) == 4); + + tree_t p0 = tree_pragma(a, 0); + fail_unless(tree_kind(p0) == T_PRAGMA); + fail_unless(tree_subkind(p0) == PRAGMA_SYNTHESIS_OFF); fail_if(parse() != NULL); diff --git a/test/test_util.c b/test/test_util.c index f97fc7ac..ed1a2e7e 100644 --- a/test/test_util.c +++ b/test/test_util.c @@ -69,7 +69,6 @@ static void setup(void) opt_set_str(OPT_DUMP_VCODE, getenv("NVC_LOWER_VERBOSE")); opt_set_int(OPT_IGNORE_TIME, 0); opt_set_int(OPT_VERBOSE, 0); - opt_set_int(OPT_SYNTHESIS, 0); opt_set_int(OPT_ERROR_LIMIT, -1); opt_set_int(OPT_ARENA_SIZE, 1 << 20); opt_set_str(OPT_GC_VERBOSE, getenv("NVC_GC_VERBOSE")); -- 2.39.2