more sparse context tracking improvements
cover more sparse context tracking improvements
These patches make further changes to the contex tracking:
1) add __exact_context__
2) allow context() attribute on variables
3) evaluate/expand context expressions
4) revert the conditional_context patch
5) check context expressions as expressions
The changes are described in more detail within the patches.
johannes
PATCH 1/5 add __exact_context__
We also need a statement to indicate that an exact context is required, most notably the next patch will require it so that it can translate attributes on variables into statements.
--- ident-list.h | 1 linearize.c | 1 linearize.h | 2 - parse.c | 20 ++++++++++++- parse.h | 1 sparse.1 | 2 - sparse.c | 14 ++++++--- validation/context-exact.c | 67 +++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 101 insertions(+), 7 deletions(-)
ident-list.h +1 -0
@@ -96,6 +96,7 @@ __IDENT(__PRETTY_FUNCTION___ident, "__PR/* Sparse commands */ IDENT_RESERVED (__context__); +IDENT_RESERVED (__exact_context__);IDENT_RESERVED (__range__);/* Magic function names we recognize */
linearize.c +1 -0
@@ -1681,6 +1681,7 @@ static pseudo_t linearize_context(struct value = expr->value; insn->required = value; + insn->exact = stmt->exact; insn->context_expr = stmt->context;add_one_insn (ep, insn);
parse.c +19 -1
@@ -52,6 +52,7 @@ static struct token *parse_while_statemestatic struct token *parse_do_statement (struct token *token,struct statement *stmt);static struct token *parse_goto_statement (struct token *token,struct statement *stmt);static struct token *parse_context_statement (struct token *token,struct statement *stmt); +static struct token *parse_exact_context_statement (struct token *token,struct statement *stmt);static struct token *parse_range_statement (struct token *token,struct statement *stmt);static struct token *parse_asm_statement (struct token *token,struct statement *stmt);static struct token *toplevel_asm_declaration (struct token *token,struct symbol_list **list);
@@ -149,6 +150,10 @@ static struct symbol_op __context___op = .statement = parse_context_statement, }; +static struct symbol_op __exact_context___op = { + .statement = parse_exact_context_statement, +}; +static struct symbol_op range_op = { .statement = parse_range_statement, };
@@ -254,6 +259,7 @@ static struct init_keyword { { "do", NS_KEYWORD, .op = &do_op }, { "goto", NS_KEYWORD, .op = &goto_op }, { "__context__",NS_KEYWORD, .op = &__context___op }, + { "__exact_context__",NS_KEYWORD, .op = &__exact_context___op }, { "__range__", NS_KEYWORD, .op = &range_op }, { "asm", NS_KEYWORD, .op = &asm_op }, { "__asm", NS_KEYWORD, .op = &asm_op },
@@ -1810,7 +1816,7 @@ static struct token *parse_goto_statemenreturn expect (token,';' ,"at end of statement" ); } -static struct token *parse_context_statement (struct token *token,struct statement *stmt) +static struct token *_parse_context_statement (struct token *token,struct statement *stmt,int exact) {struct expression *args[3 ];int argc =0 ;
@@ -1835,6 +1841,8 @@ static struct token *parse_context_state stmt->expression = args[0 ]; stmt->context =NULL ; + stmt->exact = exact; +switch (argc) {case 0 :sparse_error (token->pos,"__context__ statement needs argument(s)" );
@@ -1864,6 +1872,16 @@ static struct token *parse_context_statereturn expect (token,')' ,"at end of __context__" ); } +static struct token *parse_context_statement (struct token *token,struct statement *stmt) +{ +return _parse_context_statement (token, stmt,0 ); +} + +static struct token *parse_exact_context_statement (struct token *token,struct statement *stmt) +{ +return _parse_context_statement (token, stmt,1 ); +} +static struct token *parse_range_statement (struct token *token,struct statement *stmt) { stmt->type =STMT_RANGE ;
sparse.1 +1 -1
@@ -90,7 +90,7 @@ To indicate that a function requires .BI exactly a certain lock context (not "at least" as above), use the form .BI __attribute__((exact_context( [expression ,] in_context , out_context )) -There currently is no corresponding +There is also the corresponding .BI __exact_context__( [expression , ]adjust_value[ , required] ) statement.
sparse.c +10 -4
@@ -239,7 +239,7 @@ static int handle_context(struct entrypo struct context_check_list **combined) {struct context_check *c; -const char *name; +const char *name, *cmp;char *buf;int val, ok;
@@ -256,7 +256,13 @@ static int handle_context(struct entrypo } }END_FOR_EACH_PTR (c); - ok = insn->required <= val; +if (insn->exact) { + ok = insn->required == val; + cmp ="" ; + }else { + ok = insn->required <= val; + cmp =">= " ; + }if (!ok && Wcontext) {get_context_string (&buf, &name);
@@ -266,8 +272,8 @@ static int handle_context(struct entrypo"__context__ statement expected different context" , show_ident(ep->name->ident)); -info (insn->pos,"%swanted >= %d, got %d" , - name, insn->required, val); +info (insn->pos,"%swanted %s%d, got %d" , + name, cmp, insn->required, val);free (buf);return -1 ;
validation/context-exact.c new file +67 -0
67 lines not on this page · raw message
linearize.h +1 -1
@@ -116,7 +116,7 @@ struct instruction {struct pseudo_list *arguments; };struct /* context */ { -int increment, required, inc_false; +int increment, required, inc_false, exact;struct expression *context_expr; };struct /* asm */ {
parse.h +1 -0
@@ -43,6 +43,7 @@ struct statement {struct expression *expression;struct expression *context;struct expression *required; +int exact; };struct /* return_statement */ {struct expression *ret_value;
PATCH 2/5 allow context() attribute on variables
In reply to Johannes BergThis patch makes it possible to add the context attribute on variables, to warn for example in this case:
struct something {
int x __attribute__((context(L,1,1)));
};
extern struct something *s;
static void warn_access13(void)
{
s->x = 7;
}This is achieved by translating the context attribute on variables that are loaded from/stored to into a context expression internally. A number of tests are included, including tests for a struct member (as above) and array accesses.
To distinguish between reads and writes (the default is to check both) use the fourth parameter to the context attribute:
__attribute__((context(L,1,1,read))) or __attribute__((context(L,1,1,write)))
---
The exact_context() attribute is also allowed so we have the same max/no-max semantics as functions.
linearize.c | 92 ++++++++++++++++- linearize.h | 1 parse.c | 25 ++++ sparse.1 | 7 + sparse.c | 18 ++- symbol.h | 7 + validation/context-exact.c | 2 validation/context-on-vars.c | 219 +++++++++++++++++++++++++++++++++++++++++ validation/context-statement.c | 6 - 9 files changed, 365 insertions(+), 12 deletions(-)
sparse.c +14 -4
@@ -114,6 +114,7 @@ static struct context_check_list *checke }#define IMBALANCE_IN "context imbalance in '%s': " +#define CONTEXT_PROB "context problem in '%s': "#define DEFAULT_CONTEXT_DESCR " default context: "static void get_context_string (char **buf,const char **name)
@@ -267,10 +268,19 @@ static int handle_context(struct entrypoif (!ok && Wcontext) {get_context_string (&buf, &name); -warning (insn->pos, -IMBALANCE_IN -"__context__ statement expected different context" , -show_ident (ep->name->ident)); +if (insn->access_var) { +char *symname =strdup (show_ident (insn->access_var->ident)); +warning (insn->pos, +CONTEXT_PROB +"access to '%s' requires different context" , +show_ident (ep->name->ident), symname); +free (symname); + }else { +warning (insn->pos, +CONTEXT_PROB +"__context__ statement expected different context" , +show_ident (ep->name->ident)); + }info (insn->pos,"%swanted %s%d, got %d" , name, cmp, insn->required, val);
validation/context-on-vars.c new file +219 -0
219 lines not on this page · raw message
linearize.c +90 -2
122 lines not on this page · raw message
linearize.h +1 -0
@@ -118,6 +118,7 @@ struct instruction {struct /* context */ {int increment, required, inc_false, exact;struct expression *context_expr; +struct symbol *access_var; };struct /* asm */ {const char *string;
validation/context-statement.c +3 -3
@@ -59,11 +59,11 @@ static void bad_macro3(void)* check-error-start context-statement.c:16:8: warning: context imbalance in 'bad_arr': unexpected unlock context-statement.c:16:8: context 'LOCK': wanted 0, got -1 -context-statement.c:38:5: warning: context imbalance in 'bad_macro1': __context__ statement expected different context +context-statement.c:38:5: warning: context problem in 'bad_macro1': __context__ statement expected different context context-statement.c:38:5: context 'LOCK': wanted >= 1, got 0 -context-statement.c:47:5: warning: context imbalance in 'bad_macro2': __context__ statement expected different context +context-statement.c:47:5: warning: context problem in 'bad_macro2': __context__ statement expected different context context-statement.c:47:5: context 'LOCK': wanted >= 1, got 0 -context-statement.c:53:5: warning: context imbalance in 'bad_macro3': __context__ statement expected different context +context-statement.c:53:5: warning: context problem in 'bad_macro3': __context__ statement expected different context context-statement.c:53:5: context 'LOCK': wanted >= 0, got -1 * check-error-end */
sparse.1 +7 -0
@@ -94,6 +94,13 @@ There is also the corresponding .BI __exact_context__( [expression , ]adjust_value[ , required] ) statement. +Both these can also be added to variable accesses but it is not recommended +to make variable accesses modify the context. For variables, it is possible +to distinguish between reads and writes (the regular context attribute will +be required on both reads and writes) by using either the token "read" or +the token "write" for an optional fourth argument: +.BI __attribute__((context( expression , in_context , out_context , read|write )). + To indicate that a certain function acquires a context depending on its return value, use .BI __attribute__((conditional_context( [expression ,] in_context , out_success , out_failure ))
validation/context-exact.c +1 -1
@@ -61,7 +61,7 @@ static void good_5(void)* check-name: Check __exact_context__ statement with required context * * check-error-start -context-exact.c:46:2: warning: context imbalance in 'warn_1': __context__ statement expected different context +context-exact.c:46:2: warning: context problem in 'warn_1': __context__ statement expected different context context-exact.c:46:2: context 'TEST': wanted 1, got 2 * check-error-end */
parse.c +23 -2
@@ -883,16 +883,18 @@ static struct token *attribute_mode(strustatic struct token *_attribute_context (struct token *token,struct symbol *attr,struct ctype *ctype,int exact) {struct context *context =alloc_context (); -struct expression *args[3 ]; +struct expression *args[4 ];int argc =0 ; +struct token *last =NULL ; token =expect (token,'(' ,"after context attribute" );while (!match_op (token,')' )) {struct expression *expr =NULL ; + last = token; token =conditional_expression (token, &expr);if (!expr)break ; -if (argc <3 ) +if (argc <4 ) args[argc++] = expr;else argc++;
@@ -917,6 +919,25 @@ static struct token *_attribute_context( context->in =get_expression_value (args[1 ]); context->out =get_expression_value (args[2 ]);break ; +case 4 : { +const char *rw; + context->context = args[0 ]; + context->in =get_expression_value (args[1 ]); + context->out =get_expression_value (args[2 ]); + +if (last &&token_type (last) ==TOKEN_IDENT ) + rw =show_token (last); +else + rw =NULL ; + +if (rw &&strcmp (rw,"read" ) ==0 ) + context->rws =CTX_RWS_READ ; +else if (rw &&strcmp (rw,"write" ) ==0 ) + context->rws =CTX_RWS_WRITE ; +else +sparse_error (last->pos,"invalid read/write specifier" ); +break ; + }default :sparse_error (token->pos,"too many arguments to context attribute" );break ;
symbol.h +7 -0
@@ -69,10 +69,17 @@ enum keyword { KW_MODE = 1 << 7, }; +enum context_read_write_specifier { +CTX_RWS_BOTH =0 , +CTX_RWS_READ , +CTX_RWS_WRITE , +}; +struct context {struct expression *context;unsigned int in, out, out_false;int exact; +enum context_read_write_specifier rws; };extern struct context *alloc_context (void );
PATCH 3/5 evaluate/expand context expressions
In reply to Johannes BergBut still allow having a standalone symbol as the context expression, also evaluate the context change/requirement directly in the parser and pass them up as integers. Also fixes a number of bugs e.g. in the expression copier and a segfault when the default context is used as such:
__context__(1,1);
--- evaluate.c | 9 ++++++++- expand.c | 2 +- inline.c | 19 ++++++++++++------- linearize.c | 17 +++-------------- parse.c | 22 +++++++++++++--------- parse.h | 6 ++++-- validation/context.c | 28 ++++++++++++++++++++++++++++ 7 files changed, 69 insertions(+), 34 deletions(-)
evaluate.c +8 -1
@@ -3356,7 +3356,14 @@ struct symbol *evaluate_statement(structevaluate_asm_statement (stmt);return NULL ;case STMT_CONTEXT : -evaluate_expression (stmt->expression); +/* +* If this is an unknown symbol accept it as-is +* as a context name. +*/ +if (stmt->context && + (stmt->context->type !=EXPR_SYMBOL || + stmt->context->symbol)) +evaluate_expression (stmt->context);return NULL ;case STMT_RANGE :evaluate_expression (stmt->range_expression);
expand.c +1 -1
@@ -1169,7 +1169,7 @@ static int expand_statement(struct state/* FIXME! Do the asm parameter evaluation! */ break ;case STMT_CONTEXT : -expand_expression (stmt->expression); +expand_expression (stmt->context);break ;case STMT_RANGE :expand_expression (stmt->range_expression);
inline.c +12 -7
@@ -328,7 +328,18 @@ static struct statement *copy_one_statem stmt = newstmt;break ; } -case STMT_CONTEXT : +case STMT_CONTEXT : { +struct expression *expr =copy_expression (stmt->context); +struct statement *newstmt; +if (expr == stmt->context) +break ; + newstmt =dup_statement (stmt); + newstmt->context = expr; + newstmt->change = stmt->change; + newstmt->required = stmt->required; + stmt = newstmt; +break ; + }case STMT_EXPRESSION : {struct expression *expr =copy_expression (stmt->expression);struct statement *newstmt;
@@ -336,12 +347,6 @@ static struct statement *copy_one_statembreak ; newstmt =dup_statement (stmt); newstmt->expression = expr; -if (stmt->required) { - expr =copy_expression (stmt->required); -if (expr == stmt->required) -break ; - newstmt->required = expr; - } stmt = newstmt;break ; }
linearize.c +3 -14
@@ -1753,22 +1753,11 @@ static pseudo_t linearize_inlined_call(sstatic pseudo_t linearize_context (struct entrypoint *ep,struct statement *stmt) {struct instruction *insn =alloc_instruction (OP_CONTEXT ,0 ); -struct expression *expr = stmt->expression; -int value =0 ; -if (expr->type ==EXPR_VALUE ) - value = expr->value; + insn->increment = stmt->change; + insn->inc_false = stmt->change; - insn->increment = value; - insn->inc_false = value; - - expr = stmt->required; - value =0 ; - -if (expr && expr->type ==EXPR_VALUE ) - value = expr->value; - - insn->required = value; + insn->required = stmt->required; insn->exact = stmt->exact; insn->context_expr = stmt->context;
parse.c +13 -9
@@ -1859,9 +1859,7 @@ static struct token *_parse_context_stat token = token->next; } - stmt->expression = args[0 ]; stmt->context =NULL ; - stmt->exact = exact;switch (argc) {
@@ -1869,21 +1867,27 @@ static struct token *_parse_context_statsparse_error (token->pos,"__context__ statement needs argument(s)" );return token;case 1 : -/* already done */ + stmt->change =get_expression_value (args[0 ]);break ;case 2 : -if (args[0 ]->type !=STMT_EXPRESSION ) { +/* +* We should actually check whether we can evalulate +* it as a constant expression and if so use as the +* 'change' value. I hope nobody gives a calculation +* for the number. +*/ +if (args[0 ]->type !=EXPR_VALUE ) { stmt->context = args[0 ]; - stmt->expression = args[1 ]; + stmt->change =get_expression_value (args[1 ]); }else { - stmt->expression = args[0 ]; - stmt->required = args[1 ]; + stmt->change =get_expression_value (args[0 ]); + stmt->required =get_expression_value (args[1 ]); }break ;case 3 : stmt->context = args[0 ]; - stmt->expression = args[1 ]; - stmt->required = args[2 ]; + stmt->change =get_expression_value (args[1 ]); + stmt->required =get_expression_value (args[2 ]);break ;default :sparse_error (token->pos,"too many arguments for __context__ statement" );
parse.h +4 -2
@@ -39,10 +39,12 @@ struct statement {struct symbol *label;struct statement *label_statement; }; -struct {/* __context__ */ +struct {/* expression */ struct expression *expression; + }; +struct {/* __context__ */ struct expression *context; -struct expression *required; +int change, required;int exact; };struct /* return_statement */ {
validation/context.c +28 -0
@@ -380,6 +380,32 @@ static int warn_conditional(void)return 0 ; } +static void areq (void ) __attribute__((context (1 ,2 ))) +{ +__context__ (1 ,1 ); +} + +static void good_reqlock (void ) +{ +a (); +areq (); +r (); +r (); +} + +static void warn_reqlock (void ) +{ +areq (); +r (); +} + + +static void dummy1 (void ) __attribute__((context (p,0 ,1 ))) +{ +void *p; +__context__ (p,1 ); +} +/* * check-name: Check -Wcontext *
@@ -418,5 +444,7 @@ context.c:360:10: warning: context problcontext.c:360:10: default context: wanted >= 1, got 0 context.c:380:12: warning: context imbalance in 'warn_conditional': wrong count at exit context.c:380:12: default context: wanted 0, got 1 +context.c:398:6: warning: context problem in 'warn_reqlock': 'areq' expected different context +context.c:398:6: default context: wanted >= 1, got 0 * check-error-end */
PATCH 4/5 revert the conditional_context patch
In reply to Johannes BergThis patch removes the conditional_context attribute again, it turned out that my attempt to do this was rather misguided and contrary to what I thought we do not gain anything at all over using macros for it as the kernel and the tests have been doing for a while.
--- linearize.c | 32 +++++--- linearize.h | 2 parse.c | 52 ------------- sparse.1 | 9 -- sparse.c | 39 +++------ symbol.h | 2 validation/context-dynamic.c | 171 ------------------------------------------- 7 files changed, 37 insertions(+), 270 deletions(-)
linearize.c +20 -12
@@ -437,7 +437,7 @@ const char *show_instruction(struct instbreak ;case OP_CONTEXT : - buf +=sprintf (buf,"%s%d,%d" ,"" , insn->increment, insn->inc_false); + buf +=sprintf (buf,"%d" , insn->increment);break ;case OP_RANGE : buf +=sprintf (buf,"%s between %s..%s" ,show_pseudo (insn->src1),show_pseudo (insn->src2),show_pseudo (insn->src3));
@@ -958,7 +958,7 @@ static pseudo_t linearize_store_gen(strucontinue ; insn =alloc_instruction (OP_CONTEXT ,0 ); insn->required = context->in; - insn->increment = insn->inc_false = context->out - context->in; + insn->increment = context->out - context->in; insn->context_expr = context->context; insn->access_var = ad->source_type; insn->exact = context->exact;
@@ -971,7 +971,7 @@ static pseudo_t linearize_store_gen(strucontinue ; insn =alloc_instruction (OP_CONTEXT ,0 ); insn->required = context->in; - insn->increment = insn->inc_false = context->out - context->in; + insn->increment = context->out - context->in; insn->context_expr = context->context; insn->access_var = ad->result_type; insn->exact = context->exact;
@@ -986,7 +986,7 @@ static pseudo_t linearize_store_gen(strucontinue ; insn =alloc_instruction (OP_CONTEXT ,0 ); insn->required = context->in; - insn->increment = insn->inc_false = context->out - context->in; + insn->increment = context->out - context->in; insn->context_expr = context->context; insn->access_var = ad->address->sym; insn->exact = context->exact;
@@ -1047,7 +1047,7 @@ static pseudo_t linearize_load_gen(struccontinue ; insn =alloc_instruction (OP_CONTEXT ,0 ); insn->required = context->in; - insn->increment = insn->inc_false = context->out - context->in; + insn->increment = context->out - context->in; insn->context_expr = context->context; insn->access_var = ad->source_type; insn->exact = context->exact;
@@ -1060,7 +1060,7 @@ static pseudo_t linearize_load_gen(struccontinue ; insn =alloc_instruction (OP_CONTEXT ,0 ); insn->required = context->in; - insn->increment = insn->inc_false = context->out - context->in; + insn->increment = context->out - context->in; insn->context_expr = context->context; insn->access_var = ad->result_type; insn->exact = context->exact;
@@ -1075,7 +1075,7 @@ static pseudo_t linearize_load_gen(struccontinue ; insn =alloc_instruction (OP_CONTEXT ,0 ); insn->required = context->in; - insn->increment = insn->inc_false = context->out - context->in; + insn->increment = context->out - context->in; insn->context_expr = context->context; insn->access_var = ad->address->sym; insn->exact = context->exact;
@@ -1320,12 +1320,21 @@ static pseudo_t linearize_call_expressio FOR_EACH_PTR(ctype->contexts, context) {int in = context->in;int out = context->out; - -if (out - in || context->out_false - in) { +int check =0 ; +int context_diff; +if (in <0 ) { + check =1 ; + in =0 ; + } +if (out <0 ) { + check =0 ; + out =0 ; + } + context_diff = out - in; +if (check || context_diff) { insn =alloc_instruction (OP_CONTEXT ,0 ); - insn->increment = out - in; + insn->increment = context_diff; insn->context_expr = context->context; - insn->inc_false = context->out_false - in;add_one_insn (ep, insn); } }END_FOR_EACH_PTR (context);
@@ -1755,7 +1764,6 @@ static pseudo_t linearize_context(structstruct instruction *insn =alloc_instruction (OP_CONTEXT ,0 ); insn->increment = stmt->change; - insn->inc_false = stmt->change; insn->required = stmt->required; insn->exact = stmt->exact;
parse.c +0 -52
82 lines not on this page · raw message
sparse.1 +0 -9
@@ -101,15 +101,6 @@ be required on both reads and writes) by the token "write" for an optional fourth argument: .BI __attribute__((context( expression , in_context , out_context , read|write )). -To indicate that a certain function acquires a context depending on its -return value, use -.BI __attribute__((conditional_context( [expression ,] in_context , out_success , out_failure )) -where \fIout_success\fR and \fIout_failure\fR indicate the context change -done depending on success (non-zero) or failure (zero) return of the -function. Note that currently, using this attribute on a function means that -the function itself won't be checked for context handling at all. See the -testsuite for examples. - Sparse will warn when it sees a function change a context without indicating this with a \fBcontext\fR or \fBexact_context\fR attribute, either by decreasing a context below zero (such as by releasing a lock without acquiring
sparse.c +15 -24
@@ -25,7 +25,7 @@#include "linearize.h" struct context_check { -int val, val_false; +int val;char name[32 ]; };
@@ -44,7 +44,7 @@ static const char *context_name(struct c }static void context_add (struct context_check_list **ccl,const char *name, -int offs,int offs_false) +int offs) {struct context_check *check, *found =NULL ;
@@ -62,7 +62,6 @@ static void context_add(struct context_cadd_ptr_list (ccl, found); } found->val += offs; - found->val_false += offs_false; } static int context_list_has(struct context_check_list *ccl,
@@ -73,12 +72,11 @@ static int context_list_has(struct conte FOR_EACH_PTR(ccl, check) {if (strcmp (c->name, check->name))continue ; -return check->val == c->val && - check->val_false == c->val_false; +return check->val == c->val; }END_FOR_EACH_PTR (check);/* not found is equal to 0 */ -return c->val ==0 && c->val_false ==0 ; +return c->val ==0 ; } static int context_lists_equal(struct context_check_list *ccl1,
@@ -107,7 +105,7 @@ static struct context_check_list *checkestruct context_check *c; FOR_EACH_PTR(ccl, c) { -context_add (&result, c->name, c->val_false, c->val_false); +context_add (&result, c->name, c->val); }END_FOR_EACH_PTR (c);return result;
@@ -140,7 +138,7 @@ static int context_list_check(struct ent/* make sure the loop below checks all */ FOR_EACH_PTR(ccl_target, c1) { -context_add (&ccl_cur, c1->name,0 ,0 ); +context_add (&ccl_cur, c1->name,0 ); }END_FOR_EACH_PTR (c1); FOR_EACH_PTR(ccl_cur, c1) {
@@ -289,15 +287,14 @@ static int handle_context(struct entryporeturn -1 ; } -context_add (combined, name, insn->increment, insn->inc_false); +context_add (combined, name, insn->increment);return 0 ; }static int check_bb_context (struct entrypoint *ep,struct basic_block *bb,struct context_check_list *ccl_in, -struct context_check_list *ccl_target, -int in_false) +struct context_check_list *ccl_target) {struct context_check_list *combined =NULL , *done;struct context_check *c;
@@ -327,10 +324,7 @@ static int check_bb_context(struct entry* for the conditional_context() attribute. */ FOR_EACH_PTR(ccl_in, c) { -if (in_false) -context_add (&combined, c->name, c->val_false, c->val_false); -else -context_add (&combined, c->name, c->val, c->val); +context_add (&combined, c->name, c->val); }END_FOR_EACH_PTR (c);/* Add the new context to the list of already-checked contexts */
@@ -356,18 +350,18 @@ static int check_bb_context(struct entrycase OP_BR :if (insn->bb_true)if (check_bb_context (ep, insn->bb_true, - combined, ccl_target,0 )) + combined, ccl_target)) goto out;if (insn->bb_false)if (check_bb_context (ep, insn->bb_false, - combined, ccl_target,1 )) + combined, ccl_target)) goto out;break ;case OP_SWITCH :case OP_COMPUTEDGOTO : FOR_EACH_PTR(insn->multijmp_list, mj) {if (check_bb_context (ep, mj->target, - combined, ccl_target,0 )) + combined, ccl_target)) goto out; }END_FOR_EACH_PTR (mj);break ;
@@ -579,14 +573,11 @@ static void check_context(struct entrypo FOR_EACH_PTR(sym->ctype.contexts, context) {const char *name =context_name (context); -context_add (&ccl_in, name, context->in, context->in); -context_add (&ccl_target, name, context->out, context->out_false); -/* we don't currently check the body of trylock functions */ -if (context->out != context->out_false) -return ; +context_add (&ccl_in, name, context->in); +context_add (&ccl_target, name, context->out); }END_FOR_EACH_PTR (context); -check_bb_context (ep, ep->entry->bb, ccl_in, ccl_target,0 ); +check_bb_context (ep, ep->entry->bb, ccl_in, ccl_target);free_ptr_list (&ccl_in);free_ptr_list (&ccl_target);free_bb_context_lists (ep->entry->bb);
symbol.h +1 -1
@@ -77,7 +77,7 @@ enum context_read_write_specifier {struct context {struct expression *context; -unsigned int in, out, out_false; +unsigned int in, out;int exact;enum context_read_write_specifier rws; };
validation/context-dynamic.c deleted +0 -171
171 lines not on this page · raw message
linearize.h +1 -1
@@ -116,7 +116,7 @@ struct instruction {struct pseudo_list *arguments; };struct /* context */ { -int increment, required, inc_false, exact; +int increment, required, exact;struct expression *context_expr;struct symbol *access_var; };
PATCH 5/5 check context expressions as expressions
In reply to Johannes BergThis patch makes sparse evaluate context expressions allowing this:
struct test {
lock_t lock;
int i;
};
extern void r(struct test *t) __attribute__((context(&t->lock,0,1)));
extern struct test *find(int i) __attribute__((context(&RESULT->lock,1,0)));
void test(void)
{
struct test *x = find(42);
r(x);
r(x);
}to work the way you think it would.
This works by rewriting the given attribute expression into __context__ statements and evaluating those in the caller scope.
---
Somewhat inspired by Philipp's patch. When it warns, the expression is given as evaluated, e.g. in above case would result in a warning about "**x+0". We can fix that later by keeping the original expression and not expanding/evaluating it, but we need to do both for it to work.
evaluate.c | 18 ++- expand.c | 6 + expression.c | 272 +++++++++++++++++++++++++++++++++++++++++++++- expression.h | 7 + ident-list.h | 1 inline.c | 4 linearize.c | 63 ++++++---- linearize.h | 1 parse.c | 17 ++ parse.h | 1 sparse.c | 144 ++++++------------------ symbol.h | 4 validation/context-vars.c | 171 ++++++++++++++++++++++++++++ 13 files changed, 575 insertions(+), 134 deletions(-)
expression.c +271 -1
@@ -27,6 +27,8 @@#include "expression.h" #include "target.h" +struct expression *current_assignment_expression =NULL ; +static int match_oplist (int op, ...) {va_list args;
@@ -509,6 +511,63 @@ static struct token *expression_list(strreturn token; } +static int ident_equal (struct ident *ident1,struct ident *ident2) +{ +if (ident1 == ident2) +return 1 ; +if (!ident1 || !ident2) +return 0 ; + +return ident1->len == ident2->len && + !strncmp (ident1->name, ident2->name, ident1->len); +} + +/* TODO: this is probably not complete */ +void replace_ident (struct expression **_in,struct ident *s,struct expression *r) +{ +struct expression *in = *_in; + +switch (in->type) { +case EXPR_SYMBOL : +if (ident_equal (in->symbol_name, s)) + *_in = r; +break ; +case EXPR_IDENTIFIER : +if (ident_equal (in->expr_ident, s)) + *_in = r; +break ; +case EXPR_BINOP : +case EXPR_COMMA : +case EXPR_ASSIGNMENT : +case EXPR_COMPARE : +case EXPR_LOGICAL : +replace_ident (&in->left, s, r); +replace_ident (&in->right, s, r); +break ; +case EXPR_DEREF : +replace_ident (&in->deref, s, r); +break ; +case EXPR_PREOP : +case EXPR_POSTOP : +replace_ident (&in->unop, s, r); +break ; +case EXPR_CAST : +case EXPR_FORCE_CAST : +case EXPR_IMPLIED_CAST : +case EXPR_SIZEOF : +replace_ident (&in->cast_expression, s, r); +break ; +case EXPR_SLICE : +replace_ident (&in->base, s, r); +break ; +case EXPR_CONDITIONAL : +replace_ident (&in->conditional, s, r); +replace_ident (&in->cond_true, s, r); +replace_ident (&in->cond_false, s, r); +break ; + } +} +/* * extend to deal with the ambiguous C grammar for parsing * a cast expressions followed by an initializer.
@@ -570,10 +629,50 @@ static struct token *postfix_expression(case '(' : {/* Function call */ struct expression *call =alloc_expression (token->pos,EXPR_CALL ); +struct symbol *fn =NULL ; +struct context *c; + +if (expr->symbol_name) + fn =lookup_symbol (expr->symbol_name,NS_SYMBOL ); call->op ='(' ; call->fn = expr; token =expression_list (token->next, &call->args); token =expect (token,')' ,"in function call" ); + +if (fn && fn->ctype.base_type) { + FOR_EACH_PTR(fn->ctype.contexts, c) { +struct context *copy =alloc_context (); +struct symbol *fn_arg; +struct expression *passed_arg; + + copy->in = c->in; + copy->out = c->out; + copy->exact = c->exact; +add_ptr_list (&call->contexts, copy); + +if (!c->token) +continue ; + +/* re-parse the token at this place */ +conditional_expression (c->token, ©->context); + + copy->context->pos = expr->pos; + +if (current_assignment_expression) +replace_ident (©->context, &RESULT_ident, + current_assignment_expression); + +/* and replace the arg */ +PREPARE_PTR_LIST (fn->ctype.base_type->arguments, fn_arg); +FOR_EACH_PTR (call->args, passed_arg); +if (fn_arg) +replace_ident (©->context, fn_arg->ident, passed_arg); +NEXT_PTR_LIST (fn_arg); +END_FOR_EACH_PTR (passed_arg); +FINISH_PTR_LIST (fn_arg); + }END_FOR_EACH_PTR (c); + } + expr = call;continue ; }
@@ -894,6 +993,7 @@ struct token *conditional_expression(strstruct token *assignment_expression (struct token *token,struct expression **tree) { +struct expression *old_cae = current_assignment_expression; token =conditional_expression (token, tree);if (*tree &&token_type (token) ==TOKEN_SPECIAL ) { static const int assignments[] = {
@@ -904,15 +1004,19 @@ struct token *assignment_expression(struSPECIAL_SHR_ASSIGN ,SPECIAL_AND_ASSIGN , SPECIAL_OR_ASSIGN, SPECIAL_XOR_ASSIGN };int i, op = token->special; +if (op =='=' ) + current_assignment_expression = *tree;for (i =0 ; i <sizeof (assignments)/sizeof (int ); i++)if (assignments[i] == op) {struct expression * expr =alloc_expression (token->pos,EXPR_ASSIGNMENT ); expr->left = *tree; expr->op = op; *tree = expr; -return assignment_expression (token->next, &expr->right); + token =assignment_expression (token->next, &expr->right); +break ; } } + current_assignment_expression = old_cae;return token; }
@@ -929,4 +1033,170 @@ struct token *parse_expression(struct toreturn comma_expression (token,tree); } +int expressions_equal (const struct expression *expr1, +const struct expression *expr2) +{ +if (expr1 == expr2) +return 1 ; + +if (expr1 ==NULL || expr2 ==NULL ) +return 0 ; + +/* Is this the right way to handle casts? */ +if (expr1->type ==EXPR_CAST || + expr1->type ==EXPR_FORCE_CAST || + expr1->type ==EXPR_IMPLIED_CAST ) +return expressions_equal (expr1->cast_expression, expr2); + +if (expr2->type ==EXPR_CAST || + expr2->type ==EXPR_FORCE_CAST || + expr2->type ==EXPR_IMPLIED_CAST ) +return expressions_equal (expr2->cast_expression, expr1); + +if (expr1->type != expr2->type) +return 0 ; + +switch (expr1->type) { +case EXPR_SYMBOL : +return ident_equal (expr1->symbol_name, expr2->symbol_name); + +case EXPR_VALUE : +return expr1->value == expr2->value; + +case EXPR_FVALUE : +return expr1->fvalue == expr2->fvalue; + +case EXPR_STRING : +return expr1->string->length == expr2->string->length && + !strncmp (expr1->string->data, expr2->string->data, expr1->string->length); + +case EXPR_BINOP : +return expr1->op == expr2->op && +expressions_equal (expr1->left, expr2->left) && +expressions_equal (expr1->right, expr2->right); + +case EXPR_COMMA : +case EXPR_ASSIGNMENT : +return expressions_equal (expr1->left, expr2->left) && +expressions_equal (expr1->right, expr2->right); + +case EXPR_DEREF : +return expressions_equal (expr1->deref, expr2->deref) && +ident_equal (expr1->member, expr2->member); + +case EXPR_PREOP : +case EXPR_POSTOP : +return expr1->op == expr2->op && +expressions_equal (expr1->unop, expr2->unop); + +/* Not needed right now, but for sake of completness ... +case EXPR_LABEL: +case EXPR_STATEMENT: +case EXPR_CALL: +case EXPR_LOGICAL: +case EXPR_COMPARE: +case EXPR_SELECT: +case EXPR_CONDITIONAL: +case EXPR_CAST: +case EXPR_FORCE_CAST: +case EXPR_IMPLIED_CAST: +case EXPR_SLICE: +case EXPR_INITIALIZER: +case EXPR_POS: +*/ + +default : +/* nothing, we should already have had a warning */ + ; + } + +return 0 ; +} + +static int ident_str (struct ident *ident,char *buffer,int length) +{ +if (!ident) +return 0 ; +return snprintf (buffer, length,"%.*s" , ident->len, ident->name); +} + +int expression_str (const struct expression *expr, +char *buffer,int length) +{ +int n; + +memset (buffer,0 , length); + +if (!expr) +return 0 ; + +/* TODO, think about necessary parentheses () */ +switch (expr->type) { +case EXPR_SYMBOL : +return ident_str (expr->symbol_name, buffer, length); + +case EXPR_VALUE : +return snprintf (buffer, length,"%llu" , expr->value); + +case EXPR_FVALUE : +return snprintf (buffer, length,"%Lf" , expr->fvalue); + +case EXPR_STRING : +return snprintf (buffer, length,"\"%.*s\"" , expr->string->length, expr->string->data); + +case EXPR_BINOP : + n =expression_str (expr->left, buffer, length); + n +=snprintf (buffer+n, length-n,"%c" , expr->op); + n +=expression_str (expr->right, buffer+n, length-n); +return n; + +case EXPR_COMMA : + n =expression_str (expr->left, buffer, length); + n +=snprintf (buffer+n, length-n,"," ); + n +=expression_str (expr->right, buffer+n, length-n); +return n; + +case EXPR_ASSIGNMENT : + n =expression_str (expr->left, buffer, length); + n +=snprintf (buffer+n, length-n,"=" ); + n +=expression_str (expr->right, buffer+n, length-n); +return n; + +case EXPR_DEREF : +if (expr->left->type ==EXPR_PREOP && + expr->left->op =='*' ) { + n =expression_str (expr->left->unop, buffer, length); + n +=snprintf (buffer+n, length-n,"->" ); + n +=ident_str (expr->member, buffer+n, length-n); + }else { + n =expression_str (expr->left, buffer, length); + n +=snprintf (buffer+n, length-n,"." ); + n +=ident_str (expr->member, buffer+n, length-n); + } +return n; + +case EXPR_PREOP : + n =snprintf (buffer, length,"%c" , expr->op); + n +=expression_str (expr->unop, buffer+n, length-n); +return n; + +case EXPR_POSTOP : + n =expression_str (expr->unop, buffer, length); + n +=snprintf (buffer+n, length-n,"%c" , expr->op); +return n; + +case EXPR_CAST : +case EXPR_FORCE_CAST : +case EXPR_IMPLIED_CAST : +/* todo: print out the cast type's ctype */ + *buffer++ ='(' ; length--; + *buffer++ =')' ; length--; +return expression_str (expr->cast_expression, buffer, length) +2 ; + +default : +printf ("Missing code in expression_str for %d\n" , expr->type); + } + +return 0 ; +}
expression.h +7 -0
@@ -121,6 +121,7 @@ struct expression {struct /* call_expr */ {struct expression *fn;struct expression_list *args; +struct context_list *contexts; };// EXPR_LABEL struct /* label_expr */ {
@@ -216,4 +217,10 @@ struct token *compound_statement(structvoid cast_value (struct expression *expr,struct symbol *newtype,struct expression *old,struct symbol *oldtype); +int expressions_equal (const struct expression *expr1, +const struct expression *expr2); +int expression_str (const struct expression *,char *buf,int buflen); +struct expression *copy_expression (struct expression *expr); +void replace_ident (struct expression **in,struct ident *s,struct expression *r); +extern struct expression *current_assignment_expression;#endif
sparse.c +39 -105
272 lines not on this page · raw message
validation/context-vars.c new file +171 -0
171 lines not on this page · raw message
evaluate.c +17 -1
@@ -2954,8 +2954,16 @@ struct symbol *evaluate_expression(strucreturn evaluate_alignof (expr);case EXPR_DEREF :return evaluate_member_dereference (expr); -case EXPR_CALL : +case EXPR_CALL : { +struct context *c; + FOR_EACH_PTR(expr->contexts, c) { +if (c->context && + (c->context->type !=EXPR_SYMBOL || + c->context->symbol)) +evaluate_expression (c->context); + }END_FOR_EACH_PTR (c);return evaluate_call (expr); + }case EXPR_SELECT :case EXPR_CONDITIONAL :return evaluate_conditional_expression (expr);
@@ -3025,6 +3033,7 @@ static void check_duplicates(struct symbstatic struct symbol *evaluate_symbol (struct symbol *sym) {struct symbol *base_type; +struct context *c;if (!sym)return sym;
@@ -3054,6 +3063,13 @@ static struct symbol *evaluate_symbol(stevaluate_statement (base_type->stmt); current_fn = curr; + + FOR_EACH_PTR(sym->ctype.contexts, c) { +if (c->in_fn && + (c->in_fn->type !=EXPR_SYMBOL || + c->in_fn->symbol)) +evaluate_expression (c->in_fn); + }END_FOR_EACH_PTR (c); }return base_type;
expand.c +6 -0
@@ -1034,8 +1034,14 @@ int expand_symbol(struct symbol *sym) retval =expand_expression (sym->initializer);/* expand the body of the symbol */ if (base_type->type ==SYM_FN ) { +struct context *c; +if (base_type->stmt)expand_statement (base_type->stmt); + + FOR_EACH_PTR(sym->ctype.contexts, c) { +expand_expression (c->in_fn); + }END_FOR_EACH_PTR (c); }return retval; }
parse.c +16 -1
@@ -879,12 +879,13 @@ static struct token *_attribute_context(struct context *context =alloc_context ();struct expression *args[4 ];int argc =0 ; -struct token *last =NULL ; +struct token *first =NULL , *last =NULL ; token =expect (token,'(' ,"after context attribute" );while (!match_op (token,')' )) {struct expression *expr =NULL ; last = token; + first = first ? : token; token =conditional_expression (token, &expr);if (!expr)break ;
@@ -910,12 +911,14 @@ static struct token *_attribute_context(break ;case 3 : context->context = args[0 ]; + context->token = first; context->in =get_expression_value (args[1 ]); context->out =get_expression_value (args[2 ]);break ;case 4 : {const char *rw; context->context = args[0 ]; + context->token = first; context->in =get_expression_value (args[1 ]); context->out =get_expression_value (args[2 ]);
@@ -2096,6 +2099,7 @@ static struct token *parse_function_bodystruct symbol *base_type = decl->ctype.base_type;struct statement *stmt, **p;struct symbol *arg; +struct context *c; old_symbol_list = function_symbol_list;if (decl->ctype.modifiers &MOD_INLINE ) {
@@ -2124,6 +2128,11 @@ static struct token *parse_function_body token =compound_statement (token->next, stmt); + FOR_EACH_PTR(decl->ctype.contexts, c) { +if (c->token) +conditional_expression (c->token, &c->in_fn); + }END_FOR_EACH_PTR (c); +end_function (decl);if (!(decl->ctype.modifiers &MOD_INLINE ))add_symbol (list, decl);
@@ -2286,11 +2295,17 @@ struct token *external_declaration(strucfor (;;) {if (!is_typedef &&match_op (token,'=' )) { +struct expression *old_cae = current_assignment_expression;if (decl->ctype.modifiers &MOD_EXTERN ) {warning (decl->pos,"symbol with external linkage has initializer" ); decl->ctype.modifiers &= ~MOD_EXTERN ; } + current_assignment_expression =alloc_expression (token->pos,EXPR_SYMBOL ); + current_assignment_expression->symbol_name = decl->ident; + current_assignment_expression->symbol = +lookup_symbol (decl->ident,NS_SYMBOL |NS_TYPEDEF ); token =initializer (&decl->initializer, token->next); + current_assignment_expression = old_cae; }if (!is_typedef) {if (!(decl->ctype.modifiers & (MOD_EXTERN |MOD_INLINE ))) {
symbol.h +4 -0
@@ -77,6 +77,10 @@ enum context_read_write_specifier {struct context {struct expression *context; +/* store the token pointer */ +struct token *token; +/* to re-evaluate this context within the function it is for */ +struct expression *in_fn;unsigned int in, out;int exact;enum context_read_write_specifier rws;
inline.c +3 -1
@@ -56,7 +56,7 @@ static struct symbol_list *copy_symbol_lreturn dst; } -static struct expression *copy_expression (struct expression *expr) +struct expression *copy_expression (struct expression *expr) {if (!expr)return NULL ;
@@ -474,6 +474,7 @@ void copy_statement(struct statement *sr dst->args =copy_one_statement (src->args); dst->ret =copy_symbol (src->pos, src->ret); dst->inline_fn = src->inline_fn; + dst->inlined_fn_expr = src->inlined_fn_expr; }static struct symbol *create_copy_symbol (struct symbol *orig)
@@ -555,6 +556,7 @@ int inline_function(struct expression *e stmt->args = decl; } stmt->inline_fn = sym; + stmt->inlined_fn_expr = expr;unset_replace_list (fn_symbol_list);
linearize.c +38 -25
113 lines not on this page · raw message
parse.h +1 -0
@@ -61,6 +61,7 @@ struct statement {struct symbol *ret;struct symbol *inline_fn;struct statement *args; +struct expression *inlined_fn_expr; };struct /* labeled_struct */ {struct symbol *label_identifier;
linearize.h +1 -0
@@ -119,6 +119,7 @@ struct instruction {int increment, required, exact;struct expression *context_expr;struct symbol *access_var; +struct symbol *called_fn; };struct /* asm */ {const char *string;
ident-list.h +1 -0
@@ -98,6 +98,7 @@ __IDENT(__PRETTY_FUNCTION___ident, "__PRIDENT_RESERVED (__context__);IDENT_RESERVED (__exact_context__);IDENT_RESERVED (__range__); +IDENT_RESERVED (RESULT );/* Magic function names we recognize */ IDENT (memset);IDENT (memcpy);
[PATCH 6/5] test conditional result locking
To test a function that can return a locked struct or NULL, a macro has to be invented. Add a test case for that.
---
This is a bit awkward but I don't see any good way to do it differently since otherwise we'd have to track the assignment.
validation/context-vars.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-)
validation/context-vars.c +24 -4
@@ -1,3 +1,5 @@ +#include <stddef.h> +static void a (void *p) __attribute__((context (p,0 ,1 ))) {__context__ (p,1 );
@@ -159,13 +161,31 @@ static void good_locked_val(void) ; } + +extern struct test *_search (int key); + +#define search (res, key) do {\ + res = _search((key)); \ + (res ? ({ __context__(&res->lock,1); res; }) : NULL); \ + } while (0) + +static void test (void ) +{ +struct test *x; + +search (x,32 ); +if (x) +unlock (x); +} + +/* * check-name: Check -Wcontext with lock variables * * check-error-start -context-vars.c:53:7: warning: context imbalance in 'warn_lock1': wrong count at exit -context-vars.c:53:7: context '**v+4': wanted 0, got 1 -context-vars.c:137:11: warning: context problem in 'warn_unlock': 'unlock' expected different context -context-vars.c:137:11: context '*t+0': wanted >= 1, got 0 +context-vars.c:55:7: warning: context imbalance in 'warn_lock1': wrong count at exit +context-vars.c:55:7: context '**v+4': wanted 0, got 1 +context-vars.c:139:11: warning: context problem in 'warn_unlock': 'unlock' expected different context +context-vars.c:139:11: context '*t+0': wanted >= 1, got 0 * check-error-end */
[PATCH v2 6/5] test conditional result locking
To test a function that can return a locked struct or NULL, a macro has to be invented. Add a test case for that.
---
The macro was a mess, this one's much better. The key is to pass in the result variable and use it in __context__.
validation/context-vars.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-)
validation/context-vars.c +25 -4
@@ -1,3 +1,5 @@ +#include <stddef.h> +static void a (void *p) __attribute__((context (p,0 ,1 ))) {__context__ (p,1 );
@@ -159,13 +161,32 @@ static void good_locked_val(void) ; } + +extern struct test *_search (int key); + +#define search (res, key) do { \ + (res) = _search((key)); \ + if (res) \ + __context__(&(res)->lock,1);\ + } while (0) + +static void test (void ) +{ +struct test **x; + +search (*x,32 ); +if (*x) +unlock (*x); +} + +/* * check-name: Check -Wcontext with lock variables * * check-error-start -context-vars.c:53:7: warning: context imbalance in 'warn_lock1': wrong count at exit -context-vars.c:53:7: context '**v+4': wanted 0, got 1 -context-vars.c:137:11: warning: context problem in 'warn_unlock': 'unlock' expected different context -context-vars.c:137:11: context '*t+0': wanted >= 1, got 0 +context-vars.c:55:7: warning: context imbalance in 'warn_lock1': wrong count at exit +context-vars.c:55:7: context '**v+4': wanted 0, got 1 +context-vars.c:139:11: warning: context problem in 'warn_unlock': 'unlock' expected different context +context-vars.c:139:11: context '*t+0': wanted >= 1, got 0 * check-error-end */