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)
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;
}
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,
-
- /*
- * An array declared as []
+ 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 []
* 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 {
}
self->reserved_va_count = NULL;
+ self->coverage_func = NULL;
+
self->code = code_init();
return self;
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;
}
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),
/* 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.
*/
" -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;
}
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 */
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 */
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 ]] */
}
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;
/*
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");
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))