mirror of https://github.com/torvalds/linux.git
kconfig: use hash table to reuse expressions
Currently, every expression in Kconfig files produces a new abstract
syntax tree (AST), even if it is identical to a previously encountered
one.
Consider the following code:
config FOO
bool "FOO"
depends on (A || B) && C
config BAR
bool "BAR"
depends on (A || B) && C
config BAZ
bool "BAZ"
depends on A || B
The "depends on" lines are similar, but currently a separate AST is
allocated for each one.
The current data structure looks like this:
FOO->dep ==> AND BAR->dep ==> AND BAZ->dep ==> OR
/ \ / \ / \
OR C OR C A B
/ \ / \
A B A B
This is redundant; FOO->dep and BAR->dep have identical ASTs but
different memory instances.
We can optimize this; FOO->dep and BAR->dep can share the same AST, and
BAZ->dep can reference its sub tree.
The optimized data structure looks like this:
FOO->dep, BAR->dep ==> AND
/ \
BAZ->dep ==> OR C
/ \
A B
This commit introduces a hash table to keep track of allocated
expressions. If an identical expression is found, it is reused.
This does not necessarily result in memory savings, as menu_finalize()
transforms expressions without freeing up stale ones. This will be
addressed later.
One optimization that can be easily implemented is caching the
expression's value. Once FOO's dependency, (A || B) && C, is calculated,
it can be cached, eliminating the need to recalculate it for BAR.
This commit also reverts commit e983b7b17a ("kconfig/menu.c: fix
multiple references to expressions in menu_add_prop()").
Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
This commit is contained in:
parent
440f67ccdc
commit
f93d6bfbd2
|
|
@ -12,4 +12,17 @@ static inline unsigned int hash_str(const char *s)
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* simplified version of functions from include/linux/hash.h */
|
||||||
|
#define GOLDEN_RATIO_32 0x61C88647
|
||||||
|
|
||||||
|
static inline unsigned int hash_32(unsigned int val)
|
||||||
|
{
|
||||||
|
return 0x61C88647 * val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int hash_ptr(const void *ptr)
|
||||||
|
{
|
||||||
|
return hash_32((unsigned int)(unsigned long)ptr);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* HASH_H */
|
#endif /* HASH_H */
|
||||||
|
|
|
||||||
|
|
@ -9,45 +9,68 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <hash.h>
|
||||||
#include <xalloc.h>
|
#include <xalloc.h>
|
||||||
|
#include "internal.h"
|
||||||
#include "lkc.h"
|
#include "lkc.h"
|
||||||
|
|
||||||
#define DEBUG_EXPR 0
|
#define DEBUG_EXPR 0
|
||||||
|
|
||||||
|
HASHTABLE_DEFINE(expr_hashtable, EXPR_HASHSIZE);
|
||||||
|
|
||||||
static struct expr *expr_eliminate_yn(struct expr *e);
|
static struct expr *expr_eliminate_yn(struct expr *e);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* expr_lookup - return the expression with the given type and sub-nodes
|
||||||
|
* This looks up an expression with the specified type and sub-nodes. If such
|
||||||
|
* an expression is found in the hash table, it is returned. Otherwise, a new
|
||||||
|
* expression node is allocated and added to the hash table.
|
||||||
|
* @type: expression type
|
||||||
|
* @l: left node
|
||||||
|
* @r: right node
|
||||||
|
* return: expression
|
||||||
|
*/
|
||||||
|
static struct expr *expr_lookup(enum expr_type type, void *l, void *r)
|
||||||
|
{
|
||||||
|
struct expr *e;
|
||||||
|
int hash;
|
||||||
|
|
||||||
|
hash = hash_32((unsigned int)type ^ hash_ptr(l) ^ hash_ptr(r));
|
||||||
|
|
||||||
|
hash_for_each_possible(expr_hashtable, e, node, hash) {
|
||||||
|
if (e->type == type && e->left._initdata == l &&
|
||||||
|
e->right._initdata == r)
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
e = xmalloc(sizeof(*e));
|
||||||
|
e->type = type;
|
||||||
|
e->left._initdata = l;
|
||||||
|
e->right._initdata = r;
|
||||||
|
|
||||||
|
hash_add(expr_hashtable, &e->node, hash);
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
struct expr *expr_alloc_symbol(struct symbol *sym)
|
struct expr *expr_alloc_symbol(struct symbol *sym)
|
||||||
{
|
{
|
||||||
struct expr *e = xcalloc(1, sizeof(*e));
|
return expr_lookup(E_SYMBOL, sym, NULL);
|
||||||
e->type = E_SYMBOL;
|
|
||||||
e->left.sym = sym;
|
|
||||||
return e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct expr *expr_alloc_one(enum expr_type type, struct expr *ce)
|
struct expr *expr_alloc_one(enum expr_type type, struct expr *ce)
|
||||||
{
|
{
|
||||||
struct expr *e = xcalloc(1, sizeof(*e));
|
return expr_lookup(type, ce, NULL);
|
||||||
e->type = type;
|
|
||||||
e->left.expr = ce;
|
|
||||||
return e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2)
|
struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2)
|
||||||
{
|
{
|
||||||
struct expr *e = xcalloc(1, sizeof(*e));
|
return expr_lookup(type, e1, e2);
|
||||||
e->type = type;
|
|
||||||
e->left.expr = e1;
|
|
||||||
e->right.expr = e2;
|
|
||||||
return e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2)
|
struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2)
|
||||||
{
|
{
|
||||||
struct expr *e = xcalloc(1, sizeof(*e));
|
return expr_lookup(type, s1, s2);
|
||||||
e->type = type;
|
|
||||||
e->left.sym = s1;
|
|
||||||
e->right.sym = s2;
|
|
||||||
return e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct expr *expr_alloc_and(struct expr *e1, struct expr *e2)
|
struct expr *expr_alloc_and(struct expr *e1, struct expr *e2)
|
||||||
|
|
@ -64,76 +87,6 @@ struct expr *expr_alloc_or(struct expr *e1, struct expr *e2)
|
||||||
return e2 ? expr_alloc_two(E_OR, e1, e2) : e1;
|
return e2 ? expr_alloc_two(E_OR, e1, e2) : e1;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct expr *expr_copy(const struct expr *org)
|
|
||||||
{
|
|
||||||
struct expr *e;
|
|
||||||
|
|
||||||
if (!org)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
e = xmalloc(sizeof(*org));
|
|
||||||
memcpy(e, org, sizeof(*org));
|
|
||||||
switch (org->type) {
|
|
||||||
case E_SYMBOL:
|
|
||||||
e->left = org->left;
|
|
||||||
break;
|
|
||||||
case E_NOT:
|
|
||||||
e->left.expr = expr_copy(org->left.expr);
|
|
||||||
break;
|
|
||||||
case E_EQUAL:
|
|
||||||
case E_GEQ:
|
|
||||||
case E_GTH:
|
|
||||||
case E_LEQ:
|
|
||||||
case E_LTH:
|
|
||||||
case E_UNEQUAL:
|
|
||||||
e->left.sym = org->left.sym;
|
|
||||||
e->right.sym = org->right.sym;
|
|
||||||
break;
|
|
||||||
case E_AND:
|
|
||||||
case E_OR:
|
|
||||||
e->left.expr = expr_copy(org->left.expr);
|
|
||||||
e->right.expr = expr_copy(org->right.expr);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "can't copy type %d\n", e->type);
|
|
||||||
free(e);
|
|
||||||
e = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
void expr_free(struct expr *e)
|
|
||||||
{
|
|
||||||
if (!e)
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch (e->type) {
|
|
||||||
case E_SYMBOL:
|
|
||||||
break;
|
|
||||||
case E_NOT:
|
|
||||||
expr_free(e->left.expr);
|
|
||||||
break;
|
|
||||||
case E_EQUAL:
|
|
||||||
case E_GEQ:
|
|
||||||
case E_GTH:
|
|
||||||
case E_LEQ:
|
|
||||||
case E_LTH:
|
|
||||||
case E_UNEQUAL:
|
|
||||||
break;
|
|
||||||
case E_OR:
|
|
||||||
case E_AND:
|
|
||||||
expr_free(e->left.expr);
|
|
||||||
expr_free(e->right.expr);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "how to free type %d?\n", e->type);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
free(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int trans_count;
|
static int trans_count;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -146,16 +99,24 @@ static int trans_count;
|
||||||
*/
|
*/
|
||||||
static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct expr **ep2)
|
static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct expr **ep2)
|
||||||
{
|
{
|
||||||
|
struct expr *l, *r;
|
||||||
|
|
||||||
/* Recurse down to leaves */
|
/* Recurse down to leaves */
|
||||||
|
|
||||||
if ((*ep1)->type == type) {
|
if ((*ep1)->type == type) {
|
||||||
__expr_eliminate_eq(type, &(*ep1)->left.expr, ep2);
|
l = (*ep1)->left.expr;
|
||||||
__expr_eliminate_eq(type, &(*ep1)->right.expr, ep2);
|
r = (*ep1)->right.expr;
|
||||||
|
__expr_eliminate_eq(type, &l, ep2);
|
||||||
|
__expr_eliminate_eq(type, &r, ep2);
|
||||||
|
*ep1 = expr_alloc_two(type, l, r);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ((*ep2)->type == type) {
|
if ((*ep2)->type == type) {
|
||||||
__expr_eliminate_eq(type, ep1, &(*ep2)->left.expr);
|
l = (*ep2)->left.expr;
|
||||||
__expr_eliminate_eq(type, ep1, &(*ep2)->right.expr);
|
r = (*ep2)->right.expr;
|
||||||
|
__expr_eliminate_eq(type, ep1, &l);
|
||||||
|
__expr_eliminate_eq(type, ep1, &r);
|
||||||
|
*ep2 = expr_alloc_two(type, l, r);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,7 +132,6 @@ static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct e
|
||||||
/* *ep1 and *ep2 are equal leaves. Prepare them for elimination. */
|
/* *ep1 and *ep2 are equal leaves. Prepare them for elimination. */
|
||||||
|
|
||||||
trans_count++;
|
trans_count++;
|
||||||
expr_free(*ep1); expr_free(*ep2);
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case E_OR:
|
case E_OR:
|
||||||
*ep1 = expr_alloc_symbol(&symbol_no);
|
*ep1 = expr_alloc_symbol(&symbol_no);
|
||||||
|
|
@ -271,14 +231,10 @@ bool expr_eq(struct expr *e1, struct expr *e2)
|
||||||
return expr_eq(e1->left.expr, e2->left.expr);
|
return expr_eq(e1->left.expr, e2->left.expr);
|
||||||
case E_AND:
|
case E_AND:
|
||||||
case E_OR:
|
case E_OR:
|
||||||
e1 = expr_copy(e1);
|
|
||||||
e2 = expr_copy(e2);
|
|
||||||
old_count = trans_count;
|
old_count = trans_count;
|
||||||
expr_eliminate_eq(&e1, &e2);
|
expr_eliminate_eq(&e1, &e2);
|
||||||
res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
|
res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
|
||||||
e1->left.sym == e2->left.sym);
|
e1->left.sym == e2->left.sym);
|
||||||
expr_free(e1);
|
|
||||||
expr_free(e2);
|
|
||||||
trans_count = old_count;
|
trans_count = old_count;
|
||||||
return res;
|
return res;
|
||||||
case E_RANGE:
|
case E_RANGE:
|
||||||
|
|
@ -297,7 +253,7 @@ bool expr_eq(struct expr *e1, struct expr *e2)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Recursively performs the following simplifications in-place (as well as the
|
* Recursively performs the following simplifications (as well as the
|
||||||
* corresponding simplifications with swapped operands):
|
* corresponding simplifications with swapped operands):
|
||||||
*
|
*
|
||||||
* expr && n -> n
|
* expr && n -> n
|
||||||
|
|
@ -309,79 +265,39 @@ bool expr_eq(struct expr *e1, struct expr *e2)
|
||||||
*/
|
*/
|
||||||
static struct expr *expr_eliminate_yn(struct expr *e)
|
static struct expr *expr_eliminate_yn(struct expr *e)
|
||||||
{
|
{
|
||||||
struct expr *tmp;
|
struct expr *l, *r;
|
||||||
|
|
||||||
if (e) switch (e->type) {
|
if (e) switch (e->type) {
|
||||||
case E_AND:
|
case E_AND:
|
||||||
e->left.expr = expr_eliminate_yn(e->left.expr);
|
l = expr_eliminate_yn(e->left.expr);
|
||||||
e->right.expr = expr_eliminate_yn(e->right.expr);
|
r = expr_eliminate_yn(e->right.expr);
|
||||||
if (e->left.expr->type == E_SYMBOL) {
|
if (l->type == E_SYMBOL) {
|
||||||
if (e->left.expr->left.sym == &symbol_no) {
|
if (l->left.sym == &symbol_no)
|
||||||
expr_free(e->left.expr);
|
return l;
|
||||||
expr_free(e->right.expr);
|
else if (l->left.sym == &symbol_yes)
|
||||||
e->type = E_SYMBOL;
|
return r;
|
||||||
e->left.sym = &symbol_no;
|
|
||||||
e->right.expr = NULL;
|
|
||||||
return e;
|
|
||||||
} else if (e->left.expr->left.sym == &symbol_yes) {
|
|
||||||
free(e->left.expr);
|
|
||||||
tmp = e->right.expr;
|
|
||||||
*e = *(e->right.expr);
|
|
||||||
free(tmp);
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (e->right.expr->type == E_SYMBOL) {
|
|
||||||
if (e->right.expr->left.sym == &symbol_no) {
|
|
||||||
expr_free(e->left.expr);
|
|
||||||
expr_free(e->right.expr);
|
|
||||||
e->type = E_SYMBOL;
|
|
||||||
e->left.sym = &symbol_no;
|
|
||||||
e->right.expr = NULL;
|
|
||||||
return e;
|
|
||||||
} else if (e->right.expr->left.sym == &symbol_yes) {
|
|
||||||
free(e->right.expr);
|
|
||||||
tmp = e->left.expr;
|
|
||||||
*e = *(e->left.expr);
|
|
||||||
free(tmp);
|
|
||||||
return e;
|
|
||||||
}
|
}
|
||||||
|
if (r->type == E_SYMBOL) {
|
||||||
|
if (r->left.sym == &symbol_no)
|
||||||
|
return r;
|
||||||
|
else if (r->left.sym == &symbol_yes)
|
||||||
|
return l;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case E_OR:
|
case E_OR:
|
||||||
e->left.expr = expr_eliminate_yn(e->left.expr);
|
l = expr_eliminate_yn(e->left.expr);
|
||||||
e->right.expr = expr_eliminate_yn(e->right.expr);
|
r = expr_eliminate_yn(e->right.expr);
|
||||||
if (e->left.expr->type == E_SYMBOL) {
|
if (l->type == E_SYMBOL) {
|
||||||
if (e->left.expr->left.sym == &symbol_no) {
|
if (l->left.sym == &symbol_no)
|
||||||
free(e->left.expr);
|
return r;
|
||||||
tmp = e->right.expr;
|
else if (l->left.sym == &symbol_yes)
|
||||||
*e = *(e->right.expr);
|
return l;
|
||||||
free(tmp);
|
|
||||||
return e;
|
|
||||||
} else if (e->left.expr->left.sym == &symbol_yes) {
|
|
||||||
expr_free(e->left.expr);
|
|
||||||
expr_free(e->right.expr);
|
|
||||||
e->type = E_SYMBOL;
|
|
||||||
e->left.sym = &symbol_yes;
|
|
||||||
e->right.expr = NULL;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (e->right.expr->type == E_SYMBOL) {
|
|
||||||
if (e->right.expr->left.sym == &symbol_no) {
|
|
||||||
free(e->right.expr);
|
|
||||||
tmp = e->left.expr;
|
|
||||||
*e = *(e->left.expr);
|
|
||||||
free(tmp);
|
|
||||||
return e;
|
|
||||||
} else if (e->right.expr->left.sym == &symbol_yes) {
|
|
||||||
expr_free(e->left.expr);
|
|
||||||
expr_free(e->right.expr);
|
|
||||||
e->type = E_SYMBOL;
|
|
||||||
e->left.sym = &symbol_yes;
|
|
||||||
e->right.expr = NULL;
|
|
||||||
return e;
|
|
||||||
}
|
}
|
||||||
|
if (r->type == E_SYMBOL) {
|
||||||
|
if (r->left.sym == &symbol_no)
|
||||||
|
return l;
|
||||||
|
else if (r->left.sym == &symbol_yes)
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
@ -399,7 +315,7 @@ static struct expr *expr_join_or(struct expr *e1, struct expr *e2)
|
||||||
struct symbol *sym1, *sym2;
|
struct symbol *sym1, *sym2;
|
||||||
|
|
||||||
if (expr_eq(e1, e2))
|
if (expr_eq(e1, e2))
|
||||||
return expr_copy(e1);
|
return e1;
|
||||||
if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
|
if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
|
if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
|
||||||
|
|
@ -464,7 +380,7 @@ static struct expr *expr_join_and(struct expr *e1, struct expr *e2)
|
||||||
struct symbol *sym1, *sym2;
|
struct symbol *sym1, *sym2;
|
||||||
|
|
||||||
if (expr_eq(e1, e2))
|
if (expr_eq(e1, e2))
|
||||||
return expr_copy(e1);
|
return e1;
|
||||||
if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
|
if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
|
if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
|
||||||
|
|
@ -561,18 +477,24 @@ static struct expr *expr_join_and(struct expr *e1, struct expr *e2)
|
||||||
*/
|
*/
|
||||||
static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2)
|
static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2)
|
||||||
{
|
{
|
||||||
struct expr *tmp;
|
struct expr *tmp, *l, *r;
|
||||||
|
|
||||||
/* Recurse down to leaves */
|
/* Recurse down to leaves */
|
||||||
|
|
||||||
if ((*ep1)->type == type) {
|
if ((*ep1)->type == type) {
|
||||||
expr_eliminate_dups1(type, &(*ep1)->left.expr, ep2);
|
l = (*ep1)->left.expr;
|
||||||
expr_eliminate_dups1(type, &(*ep1)->right.expr, ep2);
|
r = (*ep1)->right.expr;
|
||||||
|
expr_eliminate_dups1(type, &l, ep2);
|
||||||
|
expr_eliminate_dups1(type, &r, ep2);
|
||||||
|
*ep1 = expr_alloc_two(type, l, r);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ((*ep2)->type == type) {
|
if ((*ep2)->type == type) {
|
||||||
expr_eliminate_dups1(type, ep1, &(*ep2)->left.expr);
|
l = (*ep2)->left.expr;
|
||||||
expr_eliminate_dups1(type, ep1, &(*ep2)->right.expr);
|
r = (*ep2)->right.expr;
|
||||||
|
expr_eliminate_dups1(type, ep1, &l);
|
||||||
|
expr_eliminate_dups1(type, ep1, &r);
|
||||||
|
*ep2 = expr_alloc_two(type, l, r);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -582,7 +504,6 @@ static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct
|
||||||
case E_OR:
|
case E_OR:
|
||||||
tmp = expr_join_or(*ep1, *ep2);
|
tmp = expr_join_or(*ep1, *ep2);
|
||||||
if (tmp) {
|
if (tmp) {
|
||||||
expr_free(*ep1); expr_free(*ep2);
|
|
||||||
*ep1 = expr_alloc_symbol(&symbol_no);
|
*ep1 = expr_alloc_symbol(&symbol_no);
|
||||||
*ep2 = tmp;
|
*ep2 = tmp;
|
||||||
trans_count++;
|
trans_count++;
|
||||||
|
|
@ -591,7 +512,6 @@ static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct
|
||||||
case E_AND:
|
case E_AND:
|
||||||
tmp = expr_join_and(*ep1, *ep2);
|
tmp = expr_join_and(*ep1, *ep2);
|
||||||
if (tmp) {
|
if (tmp) {
|
||||||
expr_free(*ep1); expr_free(*ep2);
|
|
||||||
*ep1 = expr_alloc_symbol(&symbol_yes);
|
*ep1 = expr_alloc_symbol(&symbol_yes);
|
||||||
*ep2 = tmp;
|
*ep2 = tmp;
|
||||||
trans_count++;
|
trans_count++;
|
||||||
|
|
@ -621,12 +541,15 @@ struct expr *expr_eliminate_dups(struct expr *e)
|
||||||
|
|
||||||
oldcount = trans_count;
|
oldcount = trans_count;
|
||||||
do {
|
do {
|
||||||
|
struct expr *l, *r;
|
||||||
|
|
||||||
trans_count = 0;
|
trans_count = 0;
|
||||||
switch (e->type) {
|
switch (e->type) {
|
||||||
case E_OR: case E_AND:
|
case E_OR: case E_AND:
|
||||||
e->left.expr = expr_eliminate_dups(e->left.expr);
|
l = expr_eliminate_dups(e->left.expr);
|
||||||
e->right.expr = expr_eliminate_dups(e->right.expr);
|
r = expr_eliminate_dups(e->right.expr);
|
||||||
expr_eliminate_dups1(e->type, &e->left.expr, &e->right.expr);
|
expr_eliminate_dups1(e->type, &l, &r);
|
||||||
|
e = expr_alloc_two(e->type, l, r);
|
||||||
default:
|
default:
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
@ -668,8 +591,6 @@ struct expr *expr_eliminate_dups(struct expr *e)
|
||||||
*/
|
*/
|
||||||
struct expr *expr_transform(struct expr *e)
|
struct expr *expr_transform(struct expr *e)
|
||||||
{
|
{
|
||||||
struct expr *tmp;
|
|
||||||
|
|
||||||
if (!e)
|
if (!e)
|
||||||
return NULL;
|
return NULL;
|
||||||
switch (e->type) {
|
switch (e->type) {
|
||||||
|
|
@ -682,8 +603,9 @@ struct expr *expr_transform(struct expr *e)
|
||||||
case E_SYMBOL:
|
case E_SYMBOL:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
e->left.expr = expr_transform(e->left.expr);
|
e = expr_alloc_two(e->type,
|
||||||
e->right.expr = expr_transform(e->right.expr);
|
expr_transform(e->left.expr),
|
||||||
|
expr_transform(e->right.expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (e->type) {
|
switch (e->type) {
|
||||||
|
|
@ -692,23 +614,18 @@ struct expr *expr_transform(struct expr *e)
|
||||||
break;
|
break;
|
||||||
if (e->right.sym == &symbol_no) {
|
if (e->right.sym == &symbol_no) {
|
||||||
// A=n -> !A
|
// A=n -> !A
|
||||||
e->type = E_NOT;
|
e = expr_alloc_one(E_NOT, expr_alloc_symbol(e->left.sym));
|
||||||
e->left.expr = expr_alloc_symbol(e->left.sym);
|
|
||||||
e->right.sym = NULL;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (e->right.sym == &symbol_mod) {
|
if (e->right.sym == &symbol_mod) {
|
||||||
// A=m -> n
|
// A=m -> n
|
||||||
printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name);
|
printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name);
|
||||||
e->type = E_SYMBOL;
|
e = expr_alloc_symbol(&symbol_no);
|
||||||
e->left.sym = &symbol_no;
|
|
||||||
e->right.sym = NULL;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (e->right.sym == &symbol_yes) {
|
if (e->right.sym == &symbol_yes) {
|
||||||
// A=y -> A
|
// A=y -> A
|
||||||
e->type = E_SYMBOL;
|
e = expr_alloc_symbol(e->left.sym);
|
||||||
e->right.sym = NULL;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -717,23 +634,18 @@ struct expr *expr_transform(struct expr *e)
|
||||||
break;
|
break;
|
||||||
if (e->right.sym == &symbol_no) {
|
if (e->right.sym == &symbol_no) {
|
||||||
// A!=n -> A
|
// A!=n -> A
|
||||||
e->type = E_SYMBOL;
|
e = expr_alloc_symbol(e->left.sym);
|
||||||
e->right.sym = NULL;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (e->right.sym == &symbol_mod) {
|
if (e->right.sym == &symbol_mod) {
|
||||||
// A!=m -> y
|
// A!=m -> y
|
||||||
printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name);
|
printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name);
|
||||||
e->type = E_SYMBOL;
|
e = expr_alloc_symbol(&symbol_yes);
|
||||||
e->left.sym = &symbol_yes;
|
|
||||||
e->right.sym = NULL;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (e->right.sym == &symbol_yes) {
|
if (e->right.sym == &symbol_yes) {
|
||||||
// A!=y -> !A
|
// A!=y -> !A
|
||||||
e->type = E_NOT;
|
e = expr_alloc_one(E_NOT, e->left.expr);
|
||||||
e->left.expr = expr_alloc_symbol(e->left.sym);
|
|
||||||
e->right.sym = NULL;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -741,82 +653,51 @@ struct expr *expr_transform(struct expr *e)
|
||||||
switch (e->left.expr->type) {
|
switch (e->left.expr->type) {
|
||||||
case E_NOT:
|
case E_NOT:
|
||||||
// !!A -> A
|
// !!A -> A
|
||||||
tmp = e->left.expr->left.expr;
|
e = e->left.expr->left.expr;
|
||||||
free(e->left.expr);
|
|
||||||
free(e);
|
|
||||||
e = tmp;
|
|
||||||
e = expr_transform(e);
|
|
||||||
break;
|
break;
|
||||||
case E_EQUAL:
|
case E_EQUAL:
|
||||||
case E_UNEQUAL:
|
case E_UNEQUAL:
|
||||||
// !(A=B) -> A!=B
|
// !(A=B) -> A!=B
|
||||||
tmp = e->left.expr;
|
e = expr_alloc_comp(e->left.expr->type == E_EQUAL ? E_UNEQUAL : E_EQUAL,
|
||||||
free(e);
|
e->left.expr->left.sym,
|
||||||
e = tmp;
|
e->left.expr->right.sym);
|
||||||
e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL;
|
|
||||||
break;
|
break;
|
||||||
case E_LEQ:
|
case E_LEQ:
|
||||||
case E_GEQ:
|
case E_GEQ:
|
||||||
// !(A<=B) -> A>B
|
// !(A<=B) -> A>B
|
||||||
tmp = e->left.expr;
|
e = expr_alloc_comp(e->left.expr->type == E_LEQ ? E_GTH : E_LTH,
|
||||||
free(e);
|
e->left.expr->left.sym,
|
||||||
e = tmp;
|
e->left.expr->right.sym);
|
||||||
e->type = e->type == E_LEQ ? E_GTH : E_LTH;
|
|
||||||
break;
|
break;
|
||||||
case E_LTH:
|
case E_LTH:
|
||||||
case E_GTH:
|
case E_GTH:
|
||||||
// !(A<B) -> A>=B
|
// !(A<B) -> A>=B
|
||||||
tmp = e->left.expr;
|
e = expr_alloc_comp(e->left.expr->type == E_LTH ? E_GEQ : E_LEQ,
|
||||||
free(e);
|
e->left.expr->left.sym,
|
||||||
e = tmp;
|
e->left.expr->right.sym);
|
||||||
e->type = e->type == E_LTH ? E_GEQ : E_LEQ;
|
|
||||||
break;
|
break;
|
||||||
case E_OR:
|
case E_OR:
|
||||||
// !(A || B) -> !A && !B
|
// !(A || B) -> !A && !B
|
||||||
tmp = e->left.expr;
|
e = expr_alloc_and(expr_alloc_one(E_NOT, e->left.expr->left.expr),
|
||||||
e->type = E_AND;
|
expr_alloc_one(E_NOT, e->left.expr->right.expr));
|
||||||
e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
|
|
||||||
tmp->type = E_NOT;
|
|
||||||
tmp->right.expr = NULL;
|
|
||||||
e = expr_transform(e);
|
e = expr_transform(e);
|
||||||
break;
|
break;
|
||||||
case E_AND:
|
case E_AND:
|
||||||
// !(A && B) -> !A || !B
|
// !(A && B) -> !A || !B
|
||||||
tmp = e->left.expr;
|
e = expr_alloc_or(expr_alloc_one(E_NOT, e->left.expr->left.expr),
|
||||||
e->type = E_OR;
|
expr_alloc_one(E_NOT, e->left.expr->right.expr));
|
||||||
e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
|
|
||||||
tmp->type = E_NOT;
|
|
||||||
tmp->right.expr = NULL;
|
|
||||||
e = expr_transform(e);
|
e = expr_transform(e);
|
||||||
break;
|
break;
|
||||||
case E_SYMBOL:
|
case E_SYMBOL:
|
||||||
if (e->left.expr->left.sym == &symbol_yes) {
|
if (e->left.expr->left.sym == &symbol_yes)
|
||||||
// !'y' -> 'n'
|
// !'y' -> 'n'
|
||||||
tmp = e->left.expr;
|
e = expr_alloc_symbol(&symbol_no);
|
||||||
free(e);
|
else if (e->left.expr->left.sym == &symbol_mod)
|
||||||
e = tmp;
|
|
||||||
e->type = E_SYMBOL;
|
|
||||||
e->left.sym = &symbol_no;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (e->left.expr->left.sym == &symbol_mod) {
|
|
||||||
// !'m' -> 'm'
|
// !'m' -> 'm'
|
||||||
tmp = e->left.expr;
|
e = expr_alloc_symbol(&symbol_mod);
|
||||||
free(e);
|
else if (e->left.expr->left.sym == &symbol_no)
|
||||||
e = tmp;
|
|
||||||
e->type = E_SYMBOL;
|
|
||||||
e->left.sym = &symbol_mod;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (e->left.expr->left.sym == &symbol_no) {
|
|
||||||
// !'n' -> 'y'
|
// !'n' -> 'y'
|
||||||
tmp = e->left.expr;
|
e = expr_alloc_symbol(&symbol_yes);
|
||||||
free(e);
|
|
||||||
e = tmp;
|
|
||||||
e->type = E_SYMBOL;
|
|
||||||
e->left.sym = &symbol_yes;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
;
|
;
|
||||||
|
|
@ -940,18 +821,18 @@ struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symb
|
||||||
case E_EQUAL:
|
case E_EQUAL:
|
||||||
if (type == E_EQUAL) {
|
if (type == E_EQUAL) {
|
||||||
if (sym == &symbol_yes)
|
if (sym == &symbol_yes)
|
||||||
return expr_copy(e);
|
return e;
|
||||||
if (sym == &symbol_mod)
|
if (sym == &symbol_mod)
|
||||||
return expr_alloc_symbol(&symbol_no);
|
return expr_alloc_symbol(&symbol_no);
|
||||||
if (sym == &symbol_no)
|
if (sym == &symbol_no)
|
||||||
return expr_alloc_one(E_NOT, expr_copy(e));
|
return expr_alloc_one(E_NOT, e);
|
||||||
} else {
|
} else {
|
||||||
if (sym == &symbol_yes)
|
if (sym == &symbol_yes)
|
||||||
return expr_alloc_one(E_NOT, expr_copy(e));
|
return expr_alloc_one(E_NOT, e);
|
||||||
if (sym == &symbol_mod)
|
if (sym == &symbol_mod)
|
||||||
return expr_alloc_symbol(&symbol_yes);
|
return expr_alloc_symbol(&symbol_yes);
|
||||||
if (sym == &symbol_no)
|
if (sym == &symbol_no)
|
||||||
return expr_copy(e);
|
return e;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case E_SYMBOL:
|
case E_SYMBOL:
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,21 @@ enum expr_type {
|
||||||
};
|
};
|
||||||
|
|
||||||
union expr_data {
|
union expr_data {
|
||||||
struct expr *expr;
|
struct expr * const expr;
|
||||||
struct symbol *sym;
|
struct symbol * const sym;
|
||||||
|
void *_initdata;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct expr - expression
|
||||||
|
*
|
||||||
|
* @node: link node for the hash table
|
||||||
|
* @type: expressoin type
|
||||||
|
* @left: left node
|
||||||
|
* @right: right node
|
||||||
|
*/
|
||||||
struct expr {
|
struct expr {
|
||||||
|
struct hlist_node node;
|
||||||
enum expr_type type;
|
enum expr_type type;
|
||||||
union expr_data left, right;
|
union expr_data left, right;
|
||||||
};
|
};
|
||||||
|
|
@ -275,8 +285,6 @@ struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e
|
||||||
struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2);
|
struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2);
|
||||||
struct expr *expr_alloc_and(struct expr *e1, struct expr *e2);
|
struct expr *expr_alloc_and(struct expr *e1, struct expr *e2);
|
||||||
struct expr *expr_alloc_or(struct expr *e1, struct expr *e2);
|
struct expr *expr_alloc_or(struct expr *e1, struct expr *e2);
|
||||||
struct expr *expr_copy(const struct expr *org);
|
|
||||||
void expr_free(struct expr *e);
|
|
||||||
void expr_eliminate_eq(struct expr **ep1, struct expr **ep2);
|
void expr_eliminate_eq(struct expr **ep1, struct expr **ep2);
|
||||||
bool expr_eq(struct expr *e1, struct expr *e2);
|
bool expr_eq(struct expr *e1, struct expr *e2);
|
||||||
tristate expr_calc_value(struct expr *e);
|
tristate expr_calc_value(struct expr *e);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,10 @@ extern HASHTABLE_DECLARE(sym_hashtable, SYMBOL_HASHSIZE);
|
||||||
#define for_all_symbols(sym) \
|
#define for_all_symbols(sym) \
|
||||||
hash_for_each(sym_hashtable, sym, node)
|
hash_for_each(sym_hashtable, sym, node)
|
||||||
|
|
||||||
|
#define EXPR_HASHSIZE (1U << 14)
|
||||||
|
|
||||||
|
extern HASHTABLE_DECLARE(expr_hashtable, EXPR_HASHSIZE);
|
||||||
|
|
||||||
struct menu;
|
struct menu;
|
||||||
|
|
||||||
extern struct menu *current_menu, *current_entry;
|
extern struct menu *current_menu, *current_entry;
|
||||||
|
|
|
||||||
|
|
@ -107,12 +107,13 @@ static struct expr *rewrite_m(struct expr *e)
|
||||||
|
|
||||||
switch (e->type) {
|
switch (e->type) {
|
||||||
case E_NOT:
|
case E_NOT:
|
||||||
e->left.expr = rewrite_m(e->left.expr);
|
e = expr_alloc_one(E_NOT, rewrite_m(e->left.expr));
|
||||||
break;
|
break;
|
||||||
case E_OR:
|
case E_OR:
|
||||||
case E_AND:
|
case E_AND:
|
||||||
e->left.expr = rewrite_m(e->left.expr);
|
e = expr_alloc_two(e->type,
|
||||||
e->right.expr = rewrite_m(e->right.expr);
|
rewrite_m(e->left.expr),
|
||||||
|
rewrite_m(e->right.expr));
|
||||||
break;
|
break;
|
||||||
case E_SYMBOL:
|
case E_SYMBOL:
|
||||||
/* change 'm' into 'm' && MODULES */
|
/* change 'm' into 'm' && MODULES */
|
||||||
|
|
@ -192,21 +193,11 @@ struct property *menu_add_prompt(enum prop_type type, const char *prompt,
|
||||||
struct menu *menu = current_entry;
|
struct menu *menu = current_entry;
|
||||||
|
|
||||||
while ((menu = menu->parent) != NULL) {
|
while ((menu = menu->parent) != NULL) {
|
||||||
struct expr *dup_expr;
|
|
||||||
|
|
||||||
if (!menu->visibility)
|
if (!menu->visibility)
|
||||||
continue;
|
continue;
|
||||||
/*
|
|
||||||
* Do not add a reference to the menu's visibility
|
|
||||||
* expression but use a copy of it. Otherwise the
|
|
||||||
* expression reduction functions will modify
|
|
||||||
* expressions that have multiple references which
|
|
||||||
* can cause unwanted side effects.
|
|
||||||
*/
|
|
||||||
dup_expr = expr_copy(menu->visibility);
|
|
||||||
|
|
||||||
prop->visible.expr = expr_alloc_and(prop->visible.expr,
|
prop->visible.expr = expr_alloc_and(prop->visible.expr,
|
||||||
dup_expr);
|
menu->visibility);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -322,7 +313,7 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
|
||||||
*/
|
*/
|
||||||
basedep = rewrite_m(menu->dep);
|
basedep = rewrite_m(menu->dep);
|
||||||
basedep = expr_transform(basedep);
|
basedep = expr_transform(basedep);
|
||||||
basedep = expr_alloc_and(expr_copy(parent->dep), basedep);
|
basedep = expr_alloc_and(parent->dep, basedep);
|
||||||
basedep = expr_eliminate_dups(basedep);
|
basedep = expr_eliminate_dups(basedep);
|
||||||
menu->dep = basedep;
|
menu->dep = basedep;
|
||||||
|
|
||||||
|
|
@ -366,7 +357,7 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
|
||||||
*/
|
*/
|
||||||
dep = rewrite_m(prop->visible.expr);
|
dep = rewrite_m(prop->visible.expr);
|
||||||
dep = expr_transform(dep);
|
dep = expr_transform(dep);
|
||||||
dep = expr_alloc_and(expr_copy(basedep), dep);
|
dep = expr_alloc_and(basedep, dep);
|
||||||
dep = expr_eliminate_dups(dep);
|
dep = expr_eliminate_dups(dep);
|
||||||
prop->visible.expr = dep;
|
prop->visible.expr = dep;
|
||||||
|
|
||||||
|
|
@ -377,11 +368,11 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
|
||||||
if (prop->type == P_SELECT) {
|
if (prop->type == P_SELECT) {
|
||||||
struct symbol *es = prop_get_symbol(prop);
|
struct symbol *es = prop_get_symbol(prop);
|
||||||
es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
|
es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
|
||||||
expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
|
expr_alloc_and(expr_alloc_symbol(menu->sym), dep));
|
||||||
} else if (prop->type == P_IMPLY) {
|
} else if (prop->type == P_IMPLY) {
|
||||||
struct symbol *es = prop_get_symbol(prop);
|
struct symbol *es = prop_get_symbol(prop);
|
||||||
es->implied.expr = expr_alloc_or(es->implied.expr,
|
es->implied.expr = expr_alloc_or(es->implied.expr,
|
||||||
expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
|
expr_alloc_and(expr_alloc_symbol(menu->sym), dep));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -441,22 +432,18 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
|
||||||
*/
|
*/
|
||||||
dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no);
|
dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no);
|
||||||
dep = expr_eliminate_dups(expr_transform(dep));
|
dep = expr_eliminate_dups(expr_transform(dep));
|
||||||
dep2 = expr_copy(basedep);
|
dep2 = basedep;
|
||||||
expr_eliminate_eq(&dep, &dep2);
|
expr_eliminate_eq(&dep, &dep2);
|
||||||
expr_free(dep);
|
|
||||||
if (!expr_is_yes(dep2)) {
|
if (!expr_is_yes(dep2)) {
|
||||||
/* Not superset, quit */
|
/* Not superset, quit */
|
||||||
expr_free(dep2);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* Superset, put in submenu */
|
/* Superset, put in submenu */
|
||||||
expr_free(dep2);
|
|
||||||
next:
|
next:
|
||||||
_menu_finalize(menu, false);
|
_menu_finalize(menu, false);
|
||||||
menu->parent = parent;
|
menu->parent = parent;
|
||||||
last_menu = menu;
|
last_menu = menu;
|
||||||
}
|
}
|
||||||
expr_free(basedep);
|
|
||||||
if (last_menu) {
|
if (last_menu) {
|
||||||
parent->list = parent->next;
|
parent->list = parent->next;
|
||||||
parent->next = last_menu->next;
|
parent->next = last_menu->next;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue