11 #include "kconfig/lkc.h"
12 #include "lcp_utils.h"
38 printf("Usage: kconfig2sat [options/actions]\n"
41 " -b, --baseconf <.config> Linux .config file with base configuration\n"
42 " --env <.conf.mk> Set env. variables such as ARCH from .conf.mk\n"
43 " -k, --kconfig <Kconfig> Kconfig file (with path)\n"
44 " --varfile <file> File listing variable options (one option per line)\n"
47 " --dump Dump internal data (for debugging)\n"
48 " --cnf <file> Generate CNF representation to <file>\n"
52 typedef unique_ptr<char> upchar;
54 upchar expr_to_string(struct expr *e);
56 upchar sym_to_string(struct symbol *sym)
61 asprintf(&ret, sym->name);
63 asprintf(&ret, "NONAME%d", sym->lcp.id);
67 upchar expr_binop_to_string(struct expr *e, const char *op_str)
69 upchar l = expr_to_string(e->left.expr);
70 upchar r = expr_to_string(e->right.expr);
73 asprintf(&ret, "(%s %s %s)", l.get(), op_str, r.get());
77 upchar expr_comp_to_string(struct expr *e, const char *op_str)
79 upchar l = sym_to_string(e->left.sym);
80 upchar r = sym_to_string(e->right.sym);
83 asprintf(&ret, "(%s %s %s)", l.get(), op_str, r.get());
87 upchar expr_to_string(struct expr *e)
96 asprintf(&ret, "()%" PRIdPTR, (intptr_t)(e->right.expr));
99 return expr_binop_to_string(e, "||");
101 return expr_binop_to_string(e, "&&");
103 asprintf(&ret, "!%s", expr_to_string(e->left.expr).get());
106 return expr_comp_to_string(e, "==");
108 return expr_comp_to_string(e, "!=");
110 return expr_comp_to_string(e, "<");
112 return expr_comp_to_string(e, "<=");
114 return expr_comp_to_string(e, ">");
116 return expr_comp_to_string(e, ">=");
118 fprintf(stderr, "LIST not implemented\n");
122 return sym_to_string(e->left.sym);
124 fprintf(stderr, "RANGE not implemented\n");
134 void dump_symbol(struct symbol *sym)
136 struct property *prop;
137 upchar name = sym_to_string(sym);
138 const char *type = sym_type_name(sym->type);
139 const char *prompt = NULL;
140 char flags[256] = "fl(";
143 if (sym->type == S_BOOLEAN || sym->type == S_TRISTATE) {
144 switch (sym->curr.tri) {
145 case no: val = 'n'; break;
146 case mod: val = 'm'; break;
147 case yes: val = 'y'; break;
151 #define FLAG(f) do { if (sym->flags & (SYMBOL_##f)) strcat(flags, #f " "); } while (0)
168 flags[strlen(flags)-1] = ')'; /* Replace trailing space */
171 for_all_properties(sym, prop, P_PROMPT) {
175 printf("%-40s %c %-8s %-50s %s\n", name.get(), val, type, flags, prompt);
178 if (sym->dir_dep.expr)
179 printf(" depends %s\n", expr_to_string(sym->dir_dep.expr).get());
181 for_all_properties(sym, prop, P_SELECT) {
182 assert(prop->expr->type == E_SYMBOL);
183 printf(" selects %s if %s\n", prop->expr->left.sym->name,
184 expr_to_string(prop->visible.expr).get());
193 for_all_symbols(i, sym) {
198 void read_varfile(const char *varfile)
200 FILE *f = fopen(varfile, "r");
209 while (fgets(line, sizeof(line), f)) {
215 if (strncmp(line, CONFIG_, strlen(CONFIG_)) == 0)
216 p += strlen(CONFIG_);
218 const char *sym_name = strtok(p, " =\n");
219 struct symbol *sym = sym_find(sym_name);
221 fprintf(stderr, "%s:%d: Error: Invalid symbol: %s\n", varfile, lineno, sym_name);
224 // if (!(sym->flags & SYMBOL_WRITE)) {
225 // fprintf(stderr, "%s:%d: Error: Symbol %s not visible\n", varfile, lineno, sym_name);
226 // exit(EXIT_FAILURE);
228 if (sym->flags & (SYMBOL_CHOICEVAL | SYMBOL_CHOICE)) {
229 fprintf(stderr, "%s:%d: Error: Choice values not yet supported: %s", varfile, lineno, sym_name);
232 struct property *prop;
233 const char *prompt = NULL;
234 for_all_properties(sym, prop, P_PROMPT) {
238 fprintf(stderr, "%s:%d: Warning: Symbol %s is internal (has no prompt)\n", varfile, lineno, sym_name);
240 sym->lcp.variable = 1;
245 void set_all_symbols_variable()
250 for_all_symbols(i, sym) {
251 if (!(sym->flags & (SYMBOL_CONST | SYMBOL_AUTO)))
252 sym->lcp.variable = 1;
261 SatLiteral(struct symbol *s) : sym(s), expr(nullptr) { assert(sym->lcp.variable && sym->lcp.id); }
262 SatLiteral(struct expr *e) : sym(nullptr), expr(e) { assert(expr->lcp.variable && expr->lcp.id); }
263 sat_id id() const { return sym ? sym->lcp.id : expr->lcp.id; }
264 upchar name() const {
267 assert(sym->name); // TODO
268 asprintf(&ret, "%s", sym->name);
270 asprintf(&ret, "expr%d", expr->lcp.id);
276 typedef vector<sat_id> SatClause;
278 struct Implication: public SatClause {
279 Implication(sat_id a, sat_id b) : SatClause{-a, b} {}
286 SatExpr(sat_id l, enum expr_type o, sat_id r)
287 : left(l), right(r), op(o) {}
288 //SatExpr(enum expr_type o) : left(0), right(0), op(o) { assert(o == E_NONE); }
289 bool operator<(const SatExpr &o) const { return tie(left, right, op) < tie(o.left, o.right, o.op); }
292 class SatLiterals : protected vector<struct SatLiteral>
295 using vector<struct SatLiteral>::begin;
296 using vector<struct SatLiteral>::end;
297 using vector<struct SatLiteral>::push_back;
298 SatLiterals() { reserve(20000); }
299 int count() const { return size(); }
300 const SatLiteral &operator[](size_type i) const { return at(i-1); }
301 const SatLiteral &last() const { return *(--end()); }
302 bool test_set_dot_printed(struct symbol *sym) const {
303 auto l = (*this)[sym->lcp.id];
304 bool ret = l.dot_printed;
308 bool test_set_dot_printed(struct expr *e) const {
309 auto l = (*this)[e->lcp.id];
310 bool ret = l.dot_printed;
318 * bool FOO!=y => !FOO
319 * bool FOO==n => !FOO
322 struct expr *expr_eliminate_eq_yn(struct expr *e)
330 e->left.expr = expr_eliminate_eq_yn(e->left.expr);
331 e->right.expr = expr_eliminate_eq_yn(e->right.expr);
334 // UNKNOWN symbols sometimes occur in dependency -
335 // e.g. ACPI_VIDEO on arm (the symbol is only defined
336 // for ACPI platforms (x86) but some drivers refer to
338 if (e->left.sym->type == S_TRISTATE || e->left.sym->type == S_BOOLEAN || e->left.sym->type == S_UNKNOWN) {
339 if (e->right.sym == &symbol_no) {
343 } else if (e->right.sym == &symbol_yes) {
347 e = expr_alloc_one(E_NOT, e);
352 if (e->left.sym->type == S_TRISTATE || e->left.sym->type == S_BOOLEAN || e->left.sym->type == S_UNKNOWN) {
353 if (e->right.sym == &symbol_yes) {
357 } else if (e->right.sym == &symbol_no) {
361 e = expr_alloc_one(E_NOT, e);
371 bool mark_variable_expressions(struct expr *e)
378 return (e->lcp.variable = e->left.sym->lcp.variable);
380 fprintf(stderr, "NONE not implemented\n");
384 if (mark_variable_expressions(e->left.expr))
385 return (e->lcp.variable = 1);
390 mark_variable_expressions(e->left.expr);
391 mark_variable_expressions(e->right.expr);
393 if (e->left.expr->lcp.variable && e->right.expr->lcp.variable) {
394 return (e->lcp.variable = 1);
395 } else if (!e->left.expr->lcp.variable && !e->right.expr->lcp.variable) {
396 return (e->lcp.variable = 0);
400 if (e->left.expr->lcp.variable) {
401 val = expr_calc_value(e->right.expr);
402 } else if (e->right.expr->lcp.variable) {
403 val = expr_calc_value(e->left.expr);
405 if ((e->type == E_OR && val == yes) ||
406 (e->type == E_AND && val == no))
407 return (e->lcp.variable = 0);
409 return (e->lcp.variable = 1);
411 __builtin_unreachable();
415 if (e->left.sym->lcp.variable && e->right.sym->lcp.variable) {
416 return (e->lcp.variable = 1);
417 } else if (e->left.sym->lcp.variable || e->right.sym->lcp.variable) {
418 fprintf(stderr, "Comparison with fixed symbol not yet supported!\n");
426 if (e->left.sym->lcp.variable || e->right.sym->lcp.variable) {
427 fprintf(stderr, "Comparison of variable symbols not implemented\n");
433 fprintf(stderr, "LIST not implemented\n");
437 fprintf(stderr, "RANGE not implemented\n");
441 fprintf(stderr, "Unexpected situation in mark_variable_expressions\n");
445 void mark_variable_expressions()
449 struct property *prop;
451 for_all_symbols(i, sym) {
452 mark_variable_expressions(sym->dir_dep.expr);
453 mark_variable_expressions(sym->rev_dep.expr);
454 for_all_properties(sym, prop, P_SELECT) {
455 assert(prop->expr->type == E_SYMBOL);
456 mark_variable_expressions(prop->visible.expr);
461 void check_dep_consistency()
467 for_all_symbols(i, sym) {
468 if (sym->lcp.variable && sym->dir_dep.expr && !sym->dir_dep.expr->lcp.variable) {
469 val = expr_calc_value(sym->dir_dep.expr);
471 fprintf(stderr, "Symbol %s cannot be variable because its dependency is always 'no': %s\n",
472 sym->name, expr_to_string(sym->dir_dep.expr).get());
476 if (sym->lcp.variable && sym->rev_dep.expr && !sym->rev_dep.expr->lcp.variable) {
477 val = expr_calc_value(sym->rev_dep.expr);
479 fprintf(stderr, "Symbol %s cannot be variable because it is selected by expression that is always 'yes': %s\n",
480 sym->name, expr_to_string(sym->rev_dep.expr).get());
484 if (!sym->lcp.variable && (sym->type == S_BOOLEAN || sym->type == S_TRISTATE) &&
485 sym->curr.tri == no && sym->rev_dep.expr && sym->rev_dep.expr->lcp.variable) {
486 fprintf(stderr, "Symbol %s cannot be fixed to 'no', because it is selcted by variable expression '%s'\n",
487 sym->name, expr_to_string(sym->rev_dep.expr).get());
497 DotWriter(const char *file_name) {
498 f = fopen(file_name, "w");
505 fprintf(f, "digraph conf {\n");
506 //fprintf(f, "rankdir=LR;\n");
512 void symbol(struct symbol *sym) {
513 // if (sym->lcp.dot_printed)
515 // sym->lcp.dot_printed = 1;
517 const char *opts = "";
518 if (sym->lcp.variable)
519 opts = " [penwidth=3]";
521 opts = " [color=gray]";
522 fprintf(f, "\"%s\"%s;\n", sym->name, opts);
524 void expr(const SatLiteral &l) {
526 const char *label = "???";
527 // if (sym->lcp.dot_printed)
529 // sym->lcp.dot_printed = 1;
530 switch (l.expr->type) {
531 case E_OR: label = "|"; break;
532 case E_AND: label = "&"; break;
533 case E_EQUAL: label = "="; break;
534 case E_UNEQUAL: label = "!="; break;
535 case E_NOT: label = "!"; break;
545 fprintf(stderr, "Expr %d not implemented\n", l.expr->type);
550 fprintf(f, "\"%s\" [shape=diamond,label=\"%s\"];\n", l.name().get(), label);
552 void depends_on(const SatLiteral &l1, const SatLiteral &l2) {
553 fprintf(f, "\"%s\" -> \"%s\" [style=bold,label=d];\n", l1.name().get(), l2.name().get());
555 void selects(const SatLiteral &l1, const SatLiteral &l2) {
556 fprintf(f, "\"%s\" -> \"%s\" [style=bold,arrowhead=open,label=s];\n", l1.name().get(), l2.name().get());
558 void edge(const SatLiteral &from, const SatLiteral &to) {
559 fprintf(f, "\"%s\" -> \"%s\" [arrowhead=empty];\n", from.name().get(), to.name().get());
564 SatLiterals literals = {};
565 vector<SatClause> clauses = {};
566 map<SatExpr, sat_id> expressions = {};
567 DotWriter *dot = nullptr;
570 SatData(const char *dot_fn) {
572 dot = new DotWriter(dot_fn);
574 expressions2literals();
575 expressions2clauses();
581 const SatLiteral &add_literal(struct symbol *sym) {
582 sym->lcp.id = literals.count() + 1;
583 literals.push_back(SatLiteral(sym));
586 return literals.last();
589 const SatLiteral &add_literal(struct expr *e) {
590 e->lcp.id = literals.count() + 1;
591 literals.push_back(SatLiteral(e));
593 dot->expr(literals.last());
594 return literals.last();
597 void symbols2literals() {
601 for_all_symbols(i, sym) {
602 if (sym->lcp.variable)
607 // Assign e->lcp.id to variable literals
608 sat_id expr2literal(struct expr *e) {
609 if (!e || !e->lcp.variable)
616 assert(e->left.sym->lcp.id);
617 e->lcp.id = e->left.sym->lcp.id;
620 fprintf(stderr, "NONE not implemented\n");
624 expr2literal(e->left.expr);
626 SatExpr se(e->left.expr->lcp.id, e->type, 0);
627 auto it = expressions.find(se);
628 if (it == expressions.end()) {
629 auto l = add_literal(e);
630 expressions.insert(pair<SatExpr, sat_id>(se, l.id()));
632 dot->edge(literals[e->left.expr->lcp.id], l);
634 e->lcp.id = it->second;
639 expr2literal(e->left.expr);
640 expr2literal(e->right.expr);
642 if (e->left.expr->lcp.variable && e->right.expr->lcp.variable) {
643 SatExpr se = SatExpr(e->left.expr->lcp.id, e->type, e->right.expr->lcp.id);
644 auto it = expressions.find(se);
645 if (it == expressions.end()) {
646 auto l = add_literal(e);
647 expressions.insert(pair<SatExpr, sat_id>(se, l.id()));
649 dot->edge(literals[e->left.expr->lcp.id], l);
650 dot->edge(literals[e->right.expr->lcp.id], l);
653 e->lcp.id = it->second;
655 if (e->left.expr->lcp.variable) {
656 e->lcp.id = e->left.expr->lcp.id;
657 } else if (e->right.expr->lcp.variable) {
658 e->lcp.id = e->right.expr->lcp.id;
665 if (e->left.sym->lcp.variable && e->right.sym->lcp.variable) {
666 SatExpr se(e->left.sym->lcp.id, e->type, e->right.sym->lcp.id);
667 auto it = expressions.find(se);
668 if (it == expressions.end()) {
669 auto l = add_literal(e);
670 expressions.insert(pair<SatExpr, sat_id>(se, l.id()));
672 dot->edge(literals[e->left.sym->lcp.id], l);
673 dot->edge(literals[e->right.sym->lcp.id], l);
676 e->lcp.id = it->second;
677 } else if (e->left.sym->lcp.variable || e->right.sym->lcp.variable) {
678 fprintf(stderr, "Comparison with fixed symbol not yet supported!\n");
687 if (e->left.sym->lcp.variable || e->right.sym->lcp.variable) {
688 fprintf(stderr, "Comparison of variable symbols not implemented\n");
693 fprintf(stderr, "LIST not implemented\n");
697 fprintf(stderr, "RANGE not implemented\n");
705 void expressions2literals() {
710 const SatLiteral *syml;
712 for_all_symbols(i, sym) {
713 if (sym->lcp.variable &&
714 (sym->type == S_BOOLEAN || sym->type == S_TRISTATE)) {
715 syml = &literals[sym->lcp.id];
716 // Direct dependencies
717 //sym->dir_dep.expr = expr_eliminate_eq_yn(sym->dir_dep.expr);
718 e = sym->dir_dep.expr;
719 id = expr2literal(e);
721 clauses.push_back(Implication(sym->lcp.id, id));
723 dot->depends_on(*syml, literals[id]);
726 // Reverse dependecies
727 e = sym->rev_dep.expr;
728 id = expr2literal(e);
730 clauses.push_back(Implication(id, sym->lcp.id));
732 dot->selects(literals[id], *syml);
738 // Tseitin Transformation - see
739 // https://en.wikipedia.org/wiki/Tseitin_transformation or
740 // http://fmv.jku.at/biere/talks/Biere-TPTPA11.pdf, slide 27.
741 void expressions2clauses() {
742 for (auto expr: expressions) {
743 SatExpr e = expr.first;
744 sat_id e_id = expr.second;
746 if (e.left == 0 || (e.right == 0 && e.op != E_NOT)) {
747 upchar tmp(expr_to_string(literals[e_id].expr));
748 printf("Unexpected sat_id == 0 in %s\n", tmp.get());
757 clauses.push_back(SatClause{ e_id, e.left });
758 clauses.push_back(SatClause{ -e_id, -e.left });
761 clauses.push_back(SatClause{ -e.left, e_id });
762 clauses.push_back(SatClause{ -e.right, e_id });
763 clauses.push_back(SatClause{ -e_id, e.left, e.right });
766 clauses.push_back(SatClause{ -e_id, e.left });
767 clauses.push_back(SatClause{ -e_id, e.right });
768 clauses.push_back(SatClause{ -e.left, -e.right, e_id });
771 clauses.push_back(SatClause{ -e_id, -e.left, e.right });
772 clauses.push_back(SatClause{ -e_id, -e.right, e.left });
773 clauses.push_back(SatClause{ -e.left, -e.right, e_id });
774 clauses.push_back(SatClause{ e.left, e.right, e_id });
777 clauses.push_back(SatClause{ -e_id, -e.left, -e.right });
778 clauses.push_back(SatClause{ -e_id, e.right, e.left });
779 clauses.push_back(SatClause{ -e.left, e.right, e_id });
780 clauses.push_back(SatClause{ e.left, -e.right, e_id });
786 struct expr *e = literals[e_id].expr;
787 upchar tmp(expr_to_string(e));
788 tristate val = expr_calc_value(e);
789 //fprintf(stderr, "Comparison operators not implemented %s = %d\n", tmp.get(), val);
790 //exit(EXIT_FAILURE);
791 clauses.push_back(SatClause{ val == yes ? e_id : -e_id });
795 fprintf(stderr, "LIST not implemented\n");
799 fprintf(stderr, "RANGE not implemented\n");
807 void write_dimacs_cnf(const char *cnf) {
808 FILE *f = fopen(cnf, "w");
815 fprintf(f, "p cnf %d %lu\n", literals.count(), clauses.size());
817 for (auto l: literals) {
819 fprintf(f, "c SYM %d %s\n", l.sym->lcp.id, l.sym->name);
821 fprintf(f, "c EXPR %d %s\n", l.expr->lcp.id, expr_to_string(l.expr).get());
823 for (auto clause: clauses) {
824 for (sat_id id: clause) {
826 fprintf(f, "%d ", id);
833 // void print_dot_oper(FILE *f, struct expr *e, const char *parent, const char *label) {
835 // asprintf(&expr_name, "expr%d", e->lcp.id);
836 // fprintf(f, "\"%s\" -> \"%s\";\n", parent, expr_name);
838 // fprintf(f, "\"%s\" [shape=diamond,label=\"%s\"];\n", expr_name, label);
839 // print_dot_expr(f, e->left.expr, expr_name, "l", "");
840 // print_dot_expr(f, e->right.expr, expr_name, "r", "");
844 // void print_dot_expr(FILE *f, struct expr *e, const char *parent,
845 // const char *suffix, const char *edge_opts) {
846 // if (e->lcp.dot_printed ||
849 // e->lcp.dot_printed = 1;
851 // switch (e->type) {
855 // print_dot_oper(f, e, parent, "|");
858 // print_dot_oper(f, e, parent, "&");
861 // print_dot_expr(f, e->left.expr, parent, suffix, "arrowhead=odot");
864 // print_dot_oper(f, e, parent, "=");
867 // print_dot_oper(f, e, parent, "!=");
870 // fprintf(stderr, "LIST not implemented\n");
871 // exit(EXIT_FAILURE);
874 // if (e->left.sym->name) {
875 // fprintf(f, "\"%s\" -> \"%s\" [%s];\n", parent, e->left.sym->name, edge_opts);
876 // print_dot_symbol(f, e->left.sym);
881 // fprintf(stderr, "RANGE not implemented\n");
882 // exit(EXIT_FAILURE);
893 // void print_dot_symbol(FILE *f, struct symbol *sym) {
894 // if (!sym->lcp.variable ||
895 // sym->lcp.dot_printed)
897 // sym->lcp.dot_printed = 1;
899 // const char *opts = "";
900 // if (sym->lcp.variable)
901 // opts = " [penwidth=3]";
903 // opts = " [color=gray]";
904 // fprintf(f, "\"%s\"%s;\n", sym->name, opts);
905 // if (sym->dir_dep.expr)
906 // print_dot_expr(f, sym->dir_dep.expr, sym->name, "", "");
908 // struct property *prop;
909 // for_all_properties(sym, prop, P_SELECT) {
910 // assert(prop->expr->type == E_SYMBOL);
911 // print_dot_expr(f, prop->expr, sym->name, "", "label=\"selects\"");
912 // if (prop->visible.expr && prop->visible.expr->lcp.variable) {
913 // printf("Not implemented %s\n", expr_to_string(prop->visible.expr).get());
919 // void write_dot(const char *dot) {
921 // FILE *f = fopen(dot, "w");
925 // exit(EXIT_FAILURE);
928 // fprintf(f, "digraph conf {\n");
930 // struct symbol *sym;
931 // for_all_symbols(i, sym)
932 // print_dot_symbol(f, sym);
933 // fprintf(f, "}\n");
938 int main(int argc, char **argv)
942 char *baseconf = NULL;
945 char *kconfig = NULL;
946 char *varfile = NULL;
950 int option_index = 0;
951 static struct option long_options[] = {
952 {"baseconf", required_argument, 0, OPT_BASECONFIG },
953 {"cnf", required_argument, 0, OPT_CNF },
954 {"dot", required_argument, 0, OPT_DOT },
955 {"env", required_argument, 0, OPT_ENV },
956 {"dump", no_argument, &dump, true },
957 {"help", no_argument, 0, OPT_HELP },
958 {"kconfig", required_argument, 0, OPT_KCONFIG },
959 {"varfile", required_argument, 0, OPT_VARFILE },
960 // {"varopt", required_argument, 0, OPT_VAROPTION },
961 {"verbose", no_argument, 0, OPT_VERBOSE },
965 c = getopt_long(argc, argv, "b:hk:v",
966 long_options, &option_index);
971 case 0: /* long option with flag != NULL */
984 set_missing_env(optarg);
1005 printf("?? getopt returned character code 0%o ??\n", c);
1009 if (optind < argc) {
1010 fprintf(stderr, "Unexpected option '%s'\n", argv[optind]);
1016 if (kconfig == NULL) {
1017 fprintf(stderr, "--kconfig option missing\n");
1021 setlocale(LC_ALL, "");
1022 bindtextdomain(PACKAGE, LOCALEDIR);
1023 textdomain(PACKAGE);
1025 conf_parse_path(kconfig);
1027 conf_read(baseconf);
1030 read_varfile(varfile);
1032 set_all_symbols_variable();
1034 mark_variable_expressions();
1035 check_dep_consistency();
1037 SatData sat_data(dot);
1043 sat_data.write_dimacs_cnf(cnf);
1045 return EXIT_SUCCESS;