aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--linearize.c1
-rw-r--r--linearize.h4
-rw-r--r--sparse.c128
-rw-r--r--validation/context.c14
4 files changed, 131 insertions, 16 deletions
diff --git a/linearize.c b/linearize.c
index ec48dac..fce1ae8 100644
--- a/linearize.c
+++ b/linearize.c
@@ -66,7 +66,6 @@ static struct entrypoint *alloc_entrypoint(void)
static struct basic_block *alloc_basic_block(struct entrypoint *ep, struct position pos)
{
struct basic_block *bb = __alloc_basic_block(0);
- bb->context = -1;
bb->pos = pos;
bb->ep = ep;
return bb;
diff --git a/linearize.h b/linearize.h
index 563bf3e..32b1c1a 100644
--- a/linearize.h
+++ b/linearize.h
@@ -219,11 +219,13 @@ enum opcode {
struct basic_block_list;
struct instruction_list;
+struct context_list_list;
struct basic_block {
struct position pos;
unsigned long generation;
- int context;
+ int context_check_recursion;
+ struct context_list_list *checked_contexts;
struct entrypoint *ep;
struct basic_block_list *parents; /* sources */
struct basic_block_list *children; /* destinations */
diff --git a/sparse.c b/sparse.c
index c01e8cd..785a6f6 100644
--- a/sparse.c
+++ b/sparse.c
@@ -31,6 +31,7 @@ struct context_check {
DECLARE_ALLOCATOR(context_check);
DECLARE_PTR_LIST(context_check_list, struct context_check);
+DECLARE_PTR_LIST(context_list_list, struct context_check_list);
ALLOCATOR(context_check, "context check list");
static const char *unnamed_context = "<unnamed>";
@@ -64,6 +65,54 @@ static void context_add(struct context_check_list **ccl, const char *name,
found->val_false += offs_false;
}
+static int context_list_has(struct context_check_list *ccl,
+ struct context_check *c)
+{
+ struct context_check *check;
+
+ FOR_EACH_PTR(ccl, check) {
+ if (strcmp(c->name, check->name))
+ continue;
+ return check->val == c->val &&
+ check->val_false == c->val_false;
+ } END_FOR_EACH_PTR(check);
+
+ /* not found is equal to 0 */
+ return c->val == 0 && c->val_false == 0;
+}
+
+static int context_lists_equal(struct context_check_list *ccl1,
+ struct context_check_list *ccl2)
+{
+ struct context_check *check;
+
+ /* can be optimised... */
+
+ FOR_EACH_PTR(ccl1, check) {
+ if (!context_list_has(ccl2, check))
+ return 0;
+ } END_FOR_EACH_PTR(check);
+
+ FOR_EACH_PTR(ccl2, check) {
+ if (!context_list_has(ccl1, check))
+ return 0;
+ } END_FOR_EACH_PTR(check);
+
+ return 1;
+}
+
+static struct context_check_list *checked_copy(struct context_check_list *ccl)
+{
+ struct context_check_list *result = NULL;
+ struct context_check *c;
+
+ FOR_EACH_PTR(ccl, c) {
+ context_add(&result, c->name, c->val_false, c->val_false);
+ } END_FOR_EACH_PTR(c);
+
+ return result;
+}
+
#define IMBALANCE_IN "context imbalance in '%s': "
#define DEFAULT_CONTEXT_DESCR " default context: "
@@ -234,18 +283,26 @@ static int check_bb_context(struct entrypoint *ep, struct basic_block *bb,
struct context_check_list *ccl_target,
int in_false)
{
- struct context_check_list *combined = NULL;
+ struct context_check_list *combined = NULL, *done;
struct context_check *c;
struct instruction *insn;
struct multijmp *mj;
+ int err = -1;
/*
- * recurse in once to catch bad loops,
- * bb->context is initialised to -1.
+ * Recurse in once to catch bad loops.
*/
- if (bb->context > 0)
+ if (bb->context_check_recursion > 1)
return 0;
- bb->context++;
+ bb->context_check_recursion++;
+
+ /*
+ * Abort if we have already checked this block out of the same context.
+ */
+ FOR_EACH_PTR(bb->checked_contexts, done) {
+ if (context_lists_equal(done, ccl_in))
+ return 0;
+ } END_FOR_EACH_PTR(done);
/*
* We're starting with a completely new local list of contexts, so
@@ -260,6 +317,10 @@ static int check_bb_context(struct entrypoint *ep, struct basic_block *bb,
context_add(&combined, c->name, c->val, c->val);
} END_FOR_EACH_PTR(c);
+ /* Add the new context to the list of already-checked contexts */
+ done = checked_copy(combined);
+ add_ptr_list(&bb->checked_contexts, done);
+
/*
* Now walk the instructions for this block, recursing into any
* instructions that have children. We need to have the right
@@ -270,28 +331,28 @@ static int check_bb_context(struct entrypoint *ep, struct basic_block *bb,
case OP_INLINED_CALL:
case OP_CALL:
if (handle_call(ep, bb, insn, combined))
- return -1;
+ goto out;
break;
case OP_CONTEXT:
if (handle_context(ep, bb, insn, &combined))
- return -1;
+ goto out;
break;
case OP_BR:
if (insn->bb_true)
if (check_bb_context(ep, insn->bb_true,
combined, ccl_target, 0))
- return -1;
+ goto out;
if (insn->bb_false)
if (check_bb_context(ep, insn->bb_false,
combined, ccl_target, 1))
- return -1;
+ 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))
- return -1;
+ goto out;
} END_FOR_EACH_PTR(mj);
break;
}
@@ -299,15 +360,53 @@ static int check_bb_context(struct entrypoint *ep, struct basic_block *bb,
insn = last_instruction(bb->insns);
if (!insn)
- return 0;
+ goto out_good;
- if (insn->opcode == OP_RET)
- return context_list_check(ep, insn->pos, combined, ccl_target);
+ if (insn->opcode == OP_RET) {
+ err = context_list_check(ep, insn->pos, combined, ccl_target);
+ goto out;
+ }
+ out_good:
+ err = 0;
+ out:
/* contents will be freed once we return out of recursion */
free_ptr_list(&combined);
+ bb->context_check_recursion--;
+ return err;
+}
- return 0;
+static void free_bb_context_lists(struct basic_block *bb)
+{
+ struct context_check_list *done;
+ struct instruction *insn;
+ struct multijmp *mj;
+
+ if (!bb->checked_contexts)
+ return;
+
+ FOR_EACH_PTR(bb->checked_contexts, done) {
+ free_ptr_list(&done);
+ } END_FOR_EACH_PTR(done);
+
+ free_ptr_list(&bb->checked_contexts);
+
+ FOR_EACH_PTR(bb->insns, insn) {
+ switch (insn->opcode) {
+ case OP_BR:
+ if (insn->bb_true)
+ free_bb_context_lists(insn->bb_true);
+ if (insn->bb_false)
+ free_bb_context_lists(insn->bb_false);
+ break;
+ case OP_SWITCH:
+ case OP_COMPUTEDGOTO:
+ FOR_EACH_PTR(insn->multijmp_list, mj) {
+ free_bb_context_lists(mj->target);
+ } END_FOR_EACH_PTR(mj);
+ break;
+ }
+ } END_FOR_EACH_PTR(insn);
}
static void check_cast_instruction(struct instruction *insn)
@@ -474,6 +573,7 @@ static void check_context(struct entrypoint *ep)
check_bb_context(ep, ep->entry->bb, ccl_in, ccl_target, 0);
free_ptr_list(&ccl_in);
free_ptr_list(&ccl_target);
+ free_bb_context_lists(ep->entry->bb);
clear_context_check_alloc();
}
diff --git a/validation/context.c b/validation/context.c
index 4250e95..0b45ba3 100644
--- a/validation/context.c
+++ b/validation/context.c
@@ -368,6 +368,18 @@ static void warn_huge_switch(void)
}
}
+static int warn_conditional(void)
+{
+ if (condition)
+ return 0;
+
+ a();
+ if (condition == 0)
+ return 1;
+ r();
+ return 0;
+}
+
/*
* check-name: Check -Wcontext
*
@@ -404,5 +416,7 @@ context.c:325:10: warning: context problem in 'warn_odd_looping': 'r' expected d
context.c:325:10: default context: wanted >= 1, got 0
context.c:360:10: warning: context problem in 'warn_huge_switch': 'r' expected different context
context.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
* check-error-end
*/