Adding coverage support:

The -coverage option causes all values have AST_FLAG_BLOCK_COVERAGE
set by default.
The coverage attribute can be used to control coverage:
It takes an optional list of coverage types, currently only "block"
and "none" is recognized.
[[coverage]] defaults to [[coverage(block)]].
Use [[coverage(none)]] or [[coverage()]] to disable.
This commit is contained in:
Wolfgang Bumiller 2014-01-07 14:33:26 +01:00
parent 5a160b0e88
commit 4ff68e07e8
7 changed files with 133 additions and 19 deletions

6
ast.c
View file

@ -113,8 +113,10 @@ static void ast_expression_init(ast_expression *self,
self->outr = NULL;
self->params = NULL;
self->count = 0;
self->flags = 0;
self->varparam = NULL;
self->flags = 0;
if (OPTS_OPTION_BOOL(OPTION_COVERAGE))
self->flags |= AST_FLAG_BLOCK_COVERAGE;
}
static void ast_expression_delete(ast_expression *self)
@ -1430,6 +1432,8 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
self->ir_v->flags |= IR_FLAG_INCLUDE_DEF;
if (self->expression.flags & AST_FLAG_ERASEABLE)
self->ir_v->flags |= IR_FLAG_ERASEABLE;
if (self->expression.flags & AST_FLAG_BLOCK_COVERAGE)
func->flags |= IR_FLAG_BLOCK_COVERAGE;
/* The function is filled later on ast_function_codegen... */
return true;
}

40
ast.h
View file

@ -56,27 +56,37 @@ typedef struct ast_goto_s ast_goto;
typedef struct ast_argpipe_s ast_argpipe;
enum {
AST_FLAG_VARIADIC = 1 << 0,
AST_FLAG_NORETURN = 1 << 1,
AST_FLAG_INLINE = 1 << 2,
AST_FLAG_INITIALIZED = 1 << 3,
AST_FLAG_DEPRECATED = 1 << 4,
AST_FLAG_INCLUDE_DEF = 1 << 5,
AST_FLAG_IS_VARARG = 1 << 6,
AST_FLAG_ALIAS = 1 << 7,
AST_FLAG_ERASEABLE = 1 << 8,
AST_FLAG_ACCUMULATE = 1 << 9,
AST_FLAG_VARIADIC = 1 << 0,
AST_FLAG_NORETURN = 1 << 1,
AST_FLAG_INLINE = 1 << 2,
AST_FLAG_INITIALIZED = 1 << 3,
AST_FLAG_DEPRECATED = 1 << 4,
AST_FLAG_INCLUDE_DEF = 1 << 5,
AST_FLAG_IS_VARARG = 1 << 6,
AST_FLAG_ALIAS = 1 << 7,
AST_FLAG_ERASEABLE = 1 << 8,
AST_FLAG_ACCUMULATE = 1 << 9,
/*
* An array declared as []
/* An array declared as []
* so that the size is taken from the initializer
*/
AST_FLAG_ARRAY_INIT = 1 << 10,
AST_FLAG_ARRAY_INIT = 1 << 10,
AST_FLAG_FINAL_DECL = 1 << 11,
AST_FLAG_FINAL_DECL = 1 << 11,
/* Several coverage options
* AST_FLAG_COVERAGE means there was an explicit [[coverage]] attribute,
* which will overwrite the default set via the commandline switches.
* BLOCK_COVERAGE inserts coverage() calls into every basic block.
* In the future there might be more options like tracking variable access
* by creating get/set wrapper functions.
*/
AST_FLAG_COVERAGE = 1 << 12,
AST_FLAG_BLOCK_COVERAGE = 1 << 13,
AST_FLAG_LAST,
AST_FLAG_TYPE_MASK = (AST_FLAG_VARIADIC | AST_FLAG_NORETURN)
AST_FLAG_TYPE_MASK = (AST_FLAG_VARIADIC | AST_FLAG_NORETURN),
AST_FLAG_COVERAGE_MASK = (AST_FLAG_BLOCK_COVERAGE)
};
enum {

6
ir.c
View file

@ -355,6 +355,8 @@ ir_builder* ir_builder_new(const char *modulename)
}
self->reserved_va_count = NULL;
self->coverage_func = NULL;
self->code = code_init();
return self;
@ -602,6 +604,10 @@ ir_block* ir_function_create_block(lex_ctx_t ctx, ir_function *self, const char
ir_block* bn = ir_block_new(self, label);
bn->context = ctx;
vec_push(self->blocks, bn);
if ((self->flags & IR_FLAG_BLOCK_COVERAGE) && self->owner->coverage_func)
(void)ir_block_create_call(bn, ctx, NULL, self->owner->coverage_func, false);
return bn;
}

2
ir.h
View file

@ -48,6 +48,7 @@ enum {
IR_FLAG_HAS_GOTO = 1 << 2,
IR_FLAG_INCLUDE_DEF = 1 << 3,
IR_FLAG_ERASEABLE = 1 << 4,
IR_FLAG_BLOCK_COVERAGE = 1 << 5,
IR_FLAG_LAST,
IR_FLAG_MASK_NO_OVERLAP = (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED),
@ -273,6 +274,7 @@ struct ir_builder_s {
/* there should just be this one nil */
ir_value *nil;
ir_value *reserved_va_count;
ir_value *coverage_func;
/* some virtual instructions require temps, and their code is isolated
* so that we don't need to keep track of their liveness.
*/

5
main.c
View file

@ -85,6 +85,7 @@ static int usage(void) {
" -Ono-<name> disable specific optimization\n"
" -Ohelp list optimizations\n");
con_out(" -force-crc=num force a specific checksum into the header\n");
con_out(" -coverage add coverage support\n");
return -1;
}
@ -280,6 +281,10 @@ static bool options_parse(int argc, char **argv) {
con_color(0);
continue;
}
if (!strcmp(argv[0]+1, "coverage")) {
OPTS_OPTION_BOOL(OPTION_COVERAGE) = true;
continue;
}
switch (argv[0][1]) {
/* -h, show usage but exit with 0 */

View file

@ -134,6 +134,7 @@
GMQCC_DEFINE_FLAG(CORRECTION)
GMQCC_DEFINE_FLAG(STATISTICS)
GMQCC_DEFINE_FLAG(PROGSRC)
GMQCC_DEFINE_FLAG(COVERAGE)
#endif
/* some cleanup so we don't have to */

View file

@ -2944,6 +2944,47 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
return false;
}
}
else if (!strcmp(parser_tokval(parser), "coverage") && !(flags & AST_FLAG_COVERAGE)) {
flags |= AST_FLAG_COVERAGE;
if (!parser_next(parser)) {
error_in_coverage:
parseerror(parser, "parse error in coverage attribute");
*cvq = CV_WRONG;
return false;
}
if (parser->tok == '(') {
if (!parser_next(parser)) {
bad_coverage_arg:
parseerror(parser, "invalid parameter for coverage() attribute\n"
"valid are: block");
*cvq = CV_WRONG;
return false;
}
if (parser->tok != ')') {
do {
if (parser->tok != TOKEN_IDENT)
goto bad_coverage_arg;
if (!strcmp(parser_tokval(parser), "block"))
flags |= AST_FLAG_BLOCK_COVERAGE;
else if (!strcmp(parser_tokval(parser), "none"))
flags &= ~(AST_FLAG_COVERAGE_MASK);
else
goto bad_coverage_arg;
if (!parser_next(parser))
goto error_in_coverage;
if (parser->tok == ',') {
if (!parser_next(parser))
goto error_in_coverage;
}
} while (parser->tok != ')');
}
if (parser->tok != ')' || !parser_next(parser))
goto error_in_coverage;
} else {
/* without parameter [[coverage]] equals [[coverage(block)]] */
flags |= AST_FLAG_BLOCK_COVERAGE;
}
}
else
{
/* Skip tokens until we hit a ]] */
@ -5163,6 +5204,8 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
}
var->cvq = qualifier;
if (qflags & AST_FLAG_COVERAGE) /* specified in QC, drop our default */
var->expression.flags &= ~(AST_FLAG_COVERAGE_MASK);
var->expression.flags |= qflags;
/*
@ -6201,11 +6244,50 @@ void parser_cleanup(parser_t *parser)
mem_d(parser);
}
static bool parser_set_coverage_func(parser_t *parser, ir_builder *ir) {
ast_expression *expr;
ast_value *cov;
ast_function *func;
if (!OPTS_OPTION_BOOL(OPTION_COVERAGE))
return true;
func = NULL;
for (size_t i = 0; i != vec_size(parser->functions); ++i) {
if (!strcmp(parser->functions[i]->name, "coverage")) {
func = parser->functions[i];
break;
}
}
if (!func) {
if (OPTS_OPTION_BOOL(OPTION_COVERAGE)) {
con_out("coverage support requested but no coverage() builtin declared\n");
ir_builder_delete(ir);
return false;
}
return true;
}
cov = func->vtype;
expr = (ast_expression*)cov;
if (expr->vtype != TYPE_FUNCTION || vec_size(expr->params) != 0) {
char ty[1024];
ast_type_to_string(expr, ty, sizeof(ty));
con_out("invalid type for coverage(): %s\n", ty);
ir_builder_delete(ir);
return false;
}
ir->coverage_func = func->ir_func->value;
return true;
}
bool parser_finish(parser_t *parser, const char *output)
{
size_t i;
ir_builder *ir;
bool retval = true;
size_t i;
ir_builder *ir;
bool retval = true;
if (compile_errors) {
con_out("*** there were compile errors\n");
@ -6287,6 +6369,10 @@ bool parser_finish(parser_t *parser, const char *output)
if (!fold_generate(parser->fold, ir))
return false;
/* before generating any functions we need to set the coverage_func */
if (!parser_set_coverage_func(parser, ir))
return false;
for (i = 0; i < vec_size(parser->globals); ++i) {
ast_value *asvalue;
if (!ast_istype(parser->globals[i], ast_value))