All of lore.kernel.org
 help / color / mirror / Atom feed
From: Paolo Bonzini <pbonzini@redhat.com>
To: qemu-devel@nongnu.org
Cc: yang.zhong@intel.com, thuth@redhat.com
Subject: [Qemu-devel] [PATCH 30/52] minikconfig: add semantic analysis
Date: Fri, 25 Jan 2019 11:06:49 +0100	[thread overview]
Message-ID: <1548410831-19553-31-git-send-email-pbonzini@redhat.com> (raw)
In-Reply-To: <1548410831-19553-1-git-send-email-pbonzini@redhat.com>

There are three parts in the semantic analysis:

1) evaluating expressions.  This is done as a simple visit
of the Expr nodes.

2) ordering clauses.  This is done by constructing a graph of variables.
There is an edge from X to Y if Y depends on X, if X selects Y, or if
X appears in a conditional selection of Y; in other words, if the value
of X can affect the value of Y.  Each clause has a "destination" variable
whose value can be affected by the clause, and clauses will be processed
according to a topological sorting of their destination variables.
Defaults are processed after all other clauses with the same destination.

3) deriving the value of the variables.  This is done by processing
the clauses in the topological order provided by the previous step.
A "depends on" clause will force a variable to False, a "select" clause
will force a variable to True, an assignment will force a variable
to its RHS.  A default will set a variable to its RHS if it has not
been set before.  Because all variables have a default, after visiting
all clauses all variables will have been set.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <20190123065618.3520-25-yang.zhong@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 scripts/minikconf.py | 136 ++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 130 insertions(+), 6 deletions(-)

diff --git a/scripts/minikconf.py b/scripts/minikconf.py
index eeecac1..7fd1438 100644
--- a/scripts/minikconf.py
+++ b/scripts/minikconf.py
@@ -16,6 +16,10 @@ import sys
 
 __all__ = [ 'KconfigParserError', 'KconfigData', 'KconfigParser' ]
 
+def debug_print(*args):
+    #print ' '.join(str(x) for x in args)
+    pass
+
 # -------------------------------------------
 # KconfigData implements the Kconfig semantics.  For now it can only
 # detect undefined symbols, i.e. symbols that were referenced in
@@ -35,6 +39,12 @@ class KconfigData:
         def __invert__(self):
             return KconfigData.NOT(self)
 
+        # Abstract methods
+        def add_edges_to(self, var):
+            pass
+        def evaluate(self):
+            assert False
+
     class AND(Expr):
         def __init__(self, lhs, rhs):
             self.lhs = lhs
@@ -42,6 +52,12 @@ class KconfigData:
         def __str__(self):
             return "(%s && %s)" % (self.lhs, self.rhs)
 
+        def add_edges_to(self, var):
+            self.lhs.add_edges_to(var)
+            self.rhs.add_edges_to(var)
+        def evaluate(self):
+            return self.lhs.evaluate() and self.rhs.evaluate()
+
     class OR(Expr):
         def __init__(self, lhs, rhs):
             self.lhs = lhs
@@ -49,35 +65,85 @@ class KconfigData:
         def __str__(self):
             return "(%s || %s)" % (self.lhs, self.rhs)
 
+        def add_edges_to(self, var):
+            self.lhs.add_edges_to(var)
+            self.rhs.add_edges_to(var)
+        def evaluate(self):
+            return self.lhs.evaluate() or self.rhs.evaluate()
+
     class NOT(Expr):
         def __init__(self, lhs):
             self.lhs = lhs
         def __str__(self):
             return "!%s" % (self.lhs)
 
+        def add_edges_to(self, var):
+            self.lhs.add_edges_to(var)
+        def evaluate(self):
+            return not self.lhs.evaluate()
+
     class Var(Expr):
         def __init__(self, name):
             self.name = name
             self.value = None
+            self.outgoing = set()
+            self.clauses_for_var = list()
         def __str__(self):
             return self.name
 
+        def has_value(self):
+            return not (self.value is None)
+        def set_value(self, val, clause):
+            self.clauses_for_var.append(clause)
+            if self.has_value() and self.value != val:
+                print("The following clauses were found for " + self.name)
+                for i in self.clauses_for_var:
+                    print("    " + str(i), file=sys.stderr)
+                raise Exception('contradiction between clauses when setting %s' % self)
+            debug_print("=> %s is now %s" % (self.name, val))
+            self.value = val
+
+        # depth first search of the dependency graph
+        def dfs(self, visited, f):
+            if self in visited:
+                return
+            visited.add(self)
+            for v in self.outgoing:
+                v.dfs(visited, f)
+            f(self)
+
+        def add_edges_to(self, var):
+            self.outgoing.add(var)
+        def evaluate(self):
+            if not self.has_value():
+                raise Exception('cycle found including %s' % self)
+            return self.value
+
     class Clause:
         def __init__(self, dest):
             self.dest = dest
+        def priority(self):
+            return 0
+        def process(self):
+            pass
 
     class AssignmentClause(Clause):
         def __init__(self, dest, value):
             KconfigData.Clause.__init__(self, dest)
             self.value = value
         def __str__(self):
-            return "%s=%s" % (self.dest, 'y' if self.value else 'n')
+            return "CONFIG_%s=%s" % (self.dest, 'y' if self.value else 'n')
+
+        def process(self):
+            self.dest.set_value(self.value, self)
 
     class DefaultClause(Clause):
         def __init__(self, dest, value, cond=None):
             KconfigData.Clause.__init__(self, dest)
             self.value = value
             self.cond = cond
+            if not (self.cond is None):
+                self.cond.add_edges_to(self.dest)
         def __str__(self):
             value = 'y' if self.value else 'n'
             if self.cond is None:
@@ -85,20 +151,38 @@ class KconfigData:
             else:
                 return "config %s default %s if %s" % (self.dest, value, self.cond)
 
+        def priority(self):
+            # Defaults are processed just before leaving the variable
+            return -1
+        def process(self):
+            if not self.dest.has_value() and \
+                    (self.cond is None or self.cond.evaluate()):
+                self.dest.set_value(self.value, self)
+
     class DependsOnClause(Clause):
         def __init__(self, dest, expr):
             KconfigData.Clause.__init__(self, dest)
             self.expr = expr
+            self.expr.add_edges_to(self.dest)
         def __str__(self):
             return "config %s depends on %s" % (self.dest, self.expr)
 
+        def process(self):
+            if not self.expr.evaluate():
+                self.dest.set_value(False, self)
+
     class SelectClause(Clause):
         def __init__(self, dest, cond):
             KconfigData.Clause.__init__(self, dest)
             self.cond = cond
+            self.cond.add_edges_to(self.dest)
         def __str__(self):
             return "select %s if %s" % (self.dest, self.cond)
 
+        def process(self):
+            if self.cond.evaluate():
+                self.dest.set_value(True, self)
+
     def __init__(self):
         self.previously_included = []
         self.incl_info = None
@@ -116,6 +200,50 @@ class KconfigData:
                 undef = True
         return undef
 
+    def compute_config(self):
+        if self.check_undefined():
+            raise Exception(parser, "there were undefined symbols")
+            return None
+
+        debug_print("Input:")
+        for clause in self.clauses:
+            debug_print(clause)
+
+        debug_print("\nDependency graph:")
+        for i in self.referenced_vars:
+            debug_print(i, "->", [str(x) for x in self.referenced_vars[i].outgoing])
+
+        # The reverse of the depth-first order is the topological sort
+        dfo = dict()
+        visited = set()
+        debug_print("\n")
+        def visit_fn(var):
+            debug_print(var, "has DFS number", len(dfo))
+            dfo[var] = len(dfo)
+
+        for name in self.referenced_vars:
+            v = self.referenced_vars[name]
+            v.dfs(visited, visit_fn)
+
+        # Put higher DFS numbers and higher priorities first.  This
+        # places the clauses in topological order and places defaults
+        # after assignments and dependencies.
+        self.clauses.sort(key=lambda x: (-dfo[x.dest], -x.priority()))
+
+        debug_print("\nSorted clauses:")
+        for clause in self.clauses:
+            debug_print(clause)
+            clause.process()
+
+        debug_print("")
+        values = dict()
+        for name in self.referenced_vars:
+            debug_print("Evaluating", name)
+            v = self.referenced_vars[name]
+            values[name] = v.evaluate()
+
+        return values
+
     # semantic actions -------------
 
     def do_declaration(self, var):
@@ -190,9 +318,6 @@ class KconfigParser:
         data = KconfigData()
         parser = KconfigParser(data)
         parser.parse_file(fp)
-        if data.check_undefined():
-            raise KconfigParserError(parser, "there were undefined symbols")
-
         return data
 
     def __init__(self, data):
@@ -501,5 +626,4 @@ class KconfigParser:
 if __name__ == '__main__':
     fname = len(sys.argv) > 1 and sys.argv[1] or 'Kconfig.test'
     data = KconfigParser.parse(open(fname, 'r'))
-    for i in data.clauses:
-        print i
+    print data.compute_config()
-- 
1.8.3.1

  parent reply	other threads:[~2019-01-25 10:07 UTC|newest]

Thread overview: 119+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-01-25 10:06 [Qemu-devel] [RFC PATCH v5 00/52] Support Kconfig in QEMU Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 01/52] arm: disable CONFIG_SERIAL_ISA Paolo Bonzini
2019-01-25 14:49   ` Thomas Huth
2019-01-25 15:21     ` Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 02/52] ide: split ioport registration to a separate file Paolo Bonzini
2019-01-25 14:53   ` Thomas Huth
2019-01-25 15:22     ` Paolo Bonzini
2019-01-30 12:07   ` Thomas Huth
2019-01-30 12:20     ` Paolo Bonzini
2019-01-30 12:55       ` Yang Zhong
2019-01-30 15:55         ` BALATON Zoltan
2019-01-25 10:06 ` [Qemu-devel] [PATCH 03/52] vfio: move conditional up to hw/Makefile.objs Paolo Bonzini
2019-01-25 15:04   ` Thomas Huth
2019-01-25 10:06 ` [Qemu-devel] [PATCH 04/52] hw/pci-host/Makefile.objs: make CONFIGS clear for PCI EXPRESS Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 05/52] build: actually use CONFIG_PAM Paolo Bonzini
2019-01-31 21:50   ` Philippe Mathieu-Daudé
2019-01-25 10:06 ` [Qemu-devel] [PATCH 06/52] hw/i386/Makefile.objs: Build pc_piix* and pc_q35 boards Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 07/52] hw/arm/Makefile.objs: CONFIG_VIRT created for virt board Paolo Bonzini
2019-01-25 15:06   ` Thomas Huth
2019-01-25 15:23     ` Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 08/52] hw/m68k/Makefile.objs: Conditionally build boards Paolo Bonzini
2019-01-25 15:08   ` Thomas Huth
2019-01-25 10:06 ` [Qemu-devel] [PATCH 09/52] hw/microblaze/Makefile.objs: Create configs for petalogix and xilinx boards Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 10/52] hw/mips/Makefile.objs: Create CONFIG_* for r4k, malta, mipssim boards Paolo Bonzini
2019-01-31 21:50   ` Philippe Mathieu-Daudé
2019-01-25 10:06 ` [Qemu-devel] [PATCH 11/52] hw/ppc/Makefile.objs: Build all boards conditinally with CONFIG_* Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 12/52] hw/sh4/Makefile.objs: New CONFIG_* varibales created for sh4 boards and device Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 13/52] hw/s390/Makefile.objs: Create new CONFIG_* variables for s390x boards and devices Paolo Bonzini
2019-01-25 15:17   ` Thomas Huth
2019-01-25 15:23     ` Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 14/52] hw/sparc/Makefile.objs: CONFIG_* for sun4m and leon3 created Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 15/52] hw/lm32/Makefile.objs: Conditionally build lm32 and milkmyst Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 16/52] hw/xtensa/Makefile.objs: Build xtensa_sim and xtensa_fpga conditionally Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 17/52] hw/nios2/Makefile.objs: Conditionally build nios2 Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 18/52] hw/riscv/Makefile.objs: Create CONFIG_* for riscv boards Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 19/52] hw/sparc64/Makefile.objs: Create CONFIG_* for sparc64 Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 20/52] hw/alpha/Makefile.objs: Create CONFIG_* for alpha Paolo Bonzini
2019-01-25 15:29   ` Thomas Huth
2019-01-25 20:04   ` Richard Henderson
2019-01-25 10:06 ` [Qemu-devel] [PATCH 21/52] hw/cris/Makefile.objs: Create CONFIG_* for cris Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 22/52] hw/hppa/Makefile.objs: Create CONFIG_* for hppa Paolo Bonzini
2019-01-25 20:05   ` Richard Henderson
2019-01-25 10:06 ` [Qemu-devel] [PATCH 23/52] hw/moxie/Makefile.objs: Conditionally build moxie Paolo Bonzini
2019-01-25 15:33   ` Thomas Huth
2019-01-25 10:06 ` [Qemu-devel] [PATCH 24/52] hw/openrisc/Makefile.objs: Create CONFIG_* for openrisc Paolo Bonzini
2019-01-25 15:35   ` Thomas Huth
2019-01-25 17:33     ` Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 25/52] hw/tricore/Makefile.objs: Create CONFIG_* for tricore Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 26/52] hw/i2c/Makefile.objs: Create new CONFIG_* variables for EEPROM and ACPI controller Paolo Bonzini
2019-01-25 15:42   ` Thomas Huth
2019-01-25 10:06 ` [Qemu-devel] [PATCH 27/52] hw/vfio/Makefile.objs: Create new CONFIG_* variables for VFIO core and PCI Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 28/52] minikconfig: add parser skeleton Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 29/52] minikconfig: add AST Paolo Bonzini
2019-01-25 10:06 ` Paolo Bonzini [this message]
2019-01-25 10:06 ` [Qemu-devel] [PATCH 31/52] hw/display: make edid configurable Paolo Bonzini
2019-01-31 21:53   ` Philippe Mathieu-Daudé
2019-01-25 10:06 ` [Qemu-devel] [PATCH 32/52] kconfig: introduce kconfig files Paolo Bonzini
2019-01-31 13:21   ` Thomas Huth
2019-01-31 13:37     ` Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 33/52] build: switch to Kconfig Paolo Bonzini
2019-01-31 21:48   ` Philippe Mathieu-Daudé
2019-01-31 22:15     ` Paolo Bonzini
2019-02-01 14:56       ` Philippe Mathieu-Daudé
2019-02-01 21:24         ` Paolo Bonzini
2019-02-04 12:58         ` Paolo Bonzini
2019-02-04 15:45     ` Anthony PERARD
2019-02-04 19:04       ` Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 34/52] minikconf: implement allnoconfig and defconfig Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 35/52] ide: express dependencies with Kconfig Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 36/52] hw/pci/Makefile.objs: make pcie configurable Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 37/52] build: convert pci.mak to Kconfig Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 38/52] build: convert sound.mak " Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 39/52] build: convert usb.mak " Paolo Bonzini
2019-01-25 10:06 ` [Qemu-devel] [PATCH 40/52] scsi: express dependencies with Kconfig Paolo Bonzini
2019-01-31 21:23   ` Philippe Mathieu-Daudé
2019-01-31 22:11     ` Paolo Bonzini
2019-01-25 10:07 ` [Qemu-devel] [PATCH 41/52] isa: express dependencies with kconfig Paolo Bonzini
2019-01-30 10:53   ` Thomas Huth
2019-01-30 11:13     ` Paolo Bonzini
2019-01-30 11:32       ` Thomas Huth
2019-01-30 11:43         ` Paolo Bonzini
2019-01-30 11:58   ` Thomas Huth
2019-01-30 12:00     ` Yang Zhong
2019-01-31 21:22   ` Philippe Mathieu-Daudé
2019-01-31 22:14     ` Paolo Bonzini
2019-01-31 22:24       ` Philippe Mathieu-Daudé
2019-02-14 16:46         ` Paolo Bonzini
2019-01-25 10:07 ` [Qemu-devel] [PATCH 42/52] i386: express dependencies with Kconfig Paolo Bonzini
2019-01-28 14:21   ` Thomas Huth
2019-02-01 15:05   ` Philippe Mathieu-Daudé
2019-02-01 20:58     ` Paolo Bonzini
2019-02-14 16:47     ` Paolo Bonzini
2019-02-14 16:54       ` Michael S. Tsirkin
2019-02-14 17:02         ` Paolo Bonzini
2019-01-25 10:07 ` [Qemu-devel] [PATCH 43/52] i2c: " Paolo Bonzini
2019-01-31 22:10   ` Philippe Mathieu-Daudé
2019-01-31 22:21     ` Paolo Bonzini
2019-01-25 10:07 ` [Qemu-devel] [PATCH 44/52] ptimer: " Paolo Bonzini
2019-01-25 10:07 ` [Qemu-devel] [PATCH 45/52] display: express dependencies with kconfig Paolo Bonzini
2019-01-25 10:07 ` [Qemu-devel] [PATCH 46/52] hyperv: " Paolo Bonzini
2019-01-25 10:07 ` [Qemu-devel] [PATCH 47/52] vfio: express vfio dependencies with Kconfig Paolo Bonzini
2019-01-25 20:00   ` Alex Williamson
2019-01-28 10:54     ` Paolo Bonzini
2019-01-25 10:07 ` [Qemu-devel] [PATCH 48/52] virtio: express virtio " Paolo Bonzini
2019-01-25 10:07 ` [Qemu-devel] [PATCH 49/52] tpm: express " Paolo Bonzini
2019-01-25 10:07 ` [Qemu-devel] [PATCH 50/52] isa: express SuperIO " Paolo Bonzini
2019-01-31 21:26   ` Philippe Mathieu-Daudé
2019-01-25 10:07 ` [Qemu-devel] [PATCH 51/52] i386-softmmu.mak: remove all CONFIG_* except boards definitions Paolo Bonzini
2019-01-25 10:07 ` [Qemu-devel] [PATCH 52/52] kconfig: introduce CONFIG_TEST_DEVICES Paolo Bonzini
2019-01-25 11:07 ` [Qemu-devel] [RFC PATCH v5 00/52] Support Kconfig in QEMU Yang Zhong
2019-01-31 17:56 ` no-reply
2019-01-31 21:57 ` no-reply
2019-01-31 21:58 ` no-reply
2019-01-31 22:01 ` no-reply
2019-01-31 22:22 ` no-reply
2019-01-31 22:22 ` no-reply
2019-01-31 22:26 ` no-reply
2019-02-01 10:41 ` Philippe Mathieu-Daudé
2019-02-03 12:01 ` no-reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1548410831-19553-31-git-send-email-pbonzini@redhat.com \
    --to=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=thuth@redhat.com \
    --cc=yang.zhong@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.