All of lore.kernel.org
 help / color / mirror / Atom feed
From: Yang Zhong <yang.zhong@intel.com>
To: qemu-devel@nongnu.org
Cc: pbonzini@redhat.com, thuth@redhat.com, peter.maydell@linaro.org,
	sameo@linux.intel.com, ehabkost@redhat.com, yang.zhong@intel.com
Subject: [Qemu-devel] [RFC PATCH v2 17/37] minikconfig: add semantic analysis
Date: Tue, 15 Jan 2019 22:10:48 +0800	[thread overview]
Message-ID: <20190115141108.934-18-yang.zhong@intel.com> (raw)
In-Reply-To: <20190115141108.934-1-yang.zhong@intel.com>

From: Paolo Bonzini <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>
---
 scripts/minikconf.py | 129 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 124 insertions(+), 5 deletions(-)

diff --git a/scripts/minikconf.py b/scripts/minikconf.py
index a6a28c9c47..48800591e2 100644
--- a/scripts/minikconf.py
+++ b/scripts/minikconf.py
@@ -15,6 +15,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
@@ -34,6 +38,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
@@ -41,6 +51,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
@@ -48,22 +64,62 @@ 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()
         def __str__(self):
             return self.name
 
+        def has_value(self):
+            return not (self.value is None)
+        def set_value(self, val):
+            if self.has_value() and self.value != val:
+                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):
@@ -72,11 +128,16 @@ class KconfigData:
         def __str__(self):
             return "%s=%s" % (self.dest, 'y' if self.value else 'n')
 
+        def process(self):
+            self.dest.set_value(self.value)
+
     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:
@@ -84,20 +145,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)
+
     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)
+
     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)
+
     def __init__(self):
         self.previously_included = []
         self.incl_info = None
@@ -115,6 +194,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):
@@ -188,9 +311,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):
@@ -499,5 +619,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()
-- 
2.17.1

  parent reply	other threads:[~2019-01-15 14:19 UTC|newest]

Thread overview: 86+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-01-15 14:10 [Qemu-devel] [RFC PATCH v2 00/37] Support Kconfig in QEMU Yang Zhong
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 01/37] build: actually use CONFIG_PAM Yang Zhong
2019-01-15 18:02   ` Thomas Huth
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 02/37] hw/i386/Makefile.objs: Build pc_piix* and pc_q35 boards Yang Zhong
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 03/37] hw/arm/Makefile.objs: CONFIG_VIRT created for virt board Yang Zhong
2019-01-16  7:07   ` Thomas Huth
     [not found]   ` <bb109ff0-8475-73f6-c33d-52044de758ac@redhat.com>
2019-01-17 11:17     ` Yang Zhong
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 04/37] hw/m68k/Makefile.objs: Conditionally build boards Yang Zhong
2019-01-16  7:15   ` Thomas Huth
2019-01-17 11:33     ` Yang Zhong
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 05/37] hw/microblaze/Makefile.objs: Create configs for petalogix and xilinx boards Yang Zhong
2019-01-16  8:28   ` Thomas Huth
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 06/37] hw/mips/Makefile.objs: Create CONFIG_* for r4k, malta, mipssim boards Yang Zhong
2019-01-16  8:34   ` Thomas Huth
2019-01-17 11:44     ` Yang Zhong
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 07/37] hw/ppc/Makefile.objs: Build all boards conditinally with CONFIG_* Yang Zhong
2019-01-15 23:10   ` Paolo Bonzini
2019-01-16  8:41   ` Thomas Huth
2019-01-17 11:58     ` Yang Zhong
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 08/37] hw/sh4/Makefile.objs: New CONFIG_* varibales created for sh4 boards and device Yang Zhong
2019-01-16  8:48   ` Thomas Huth
2019-01-17 12:10     ` Yang Zhong
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 09/37] hw/sparc/Makefile.objs: CONFIG_* for sun4m and leon3 created Yang Zhong
2019-01-16  9:04   ` Thomas Huth
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 10/37] hw/lm32/Makefile.objs: Conditionally build lm32 and milkmyst Yang Zhong
2019-01-16  9:10   ` Thomas Huth
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 11/37] hw/xtensa/Makefile.objs: Build xtensa_sim and xtensa_fpga conditionally Yang Zhong
2019-01-16  9:31   ` Thomas Huth
2019-01-16 18:43   ` Max Filippov
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 12/37] hw/nios2/Makefile.objs: Conditionally build nios2 Yang Zhong
2019-01-16  9:37   ` Thomas Huth
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 13/37] hw/riscv/Makefile.objs: Create CONFIG_* for riscv boards Yang Zhong
2019-01-16  9:46   ` Thomas Huth
2019-01-16  9:46     ` [Qemu-riscv] " Thomas Huth
2019-01-17 12:36     ` Yang Zhong
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 14/37] hw/sparc64/Makefile.objs: Create CONFIG_* for sparc64 Yang Zhong
2019-01-16  9:56   ` Thomas Huth
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 15/37] minikconfig: add parser skeleton Yang Zhong
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 16/37] minikconfig: add AST Yang Zhong
2019-01-15 14:10 ` Yang Zhong [this message]
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 18/37] hw/display: make edid configurable Yang Zhong
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 19/37] kconfig: introduce kconfig files Yang Zhong
2019-01-16 10:44   ` Thomas Huth
2019-01-16 14:06     ` Thomas Huth
2019-01-18  6:41       ` Yang Zhong
2019-01-18  6:34     ` Yang Zhong
2019-01-17  9:17   ` Thomas Huth
2019-01-18  6:42     ` Yang Zhong
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 20/37] build: switch to Kconfig Yang Zhong
2019-01-16 11:05   ` Thomas Huth
2019-01-16 16:28   ` Thomas Huth
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 21/37] ide: express dependencies with Kconfig Yang Zhong
2019-01-16 11:21   ` Thomas Huth
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 22/37] hw/pci/Makefile.objs: make pcie configurable Yang Zhong
2019-01-16 11:23   ` Thomas Huth
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 23/37] build: convert pci.mak to Kconfig Yang Zhong
2019-01-16 11:41   ` Thomas Huth
2019-01-18  7:08     ` Yang Zhong
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 24/37] build: convert sound.mak " Yang Zhong
2019-01-16 13:48   ` Thomas Huth
2019-01-16 13:51     ` Thomas Huth
2019-01-18  7:24       ` Yang Zhong
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 25/37] build: convert usb.mak " Yang Zhong
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 26/37] scsi: express dependencies with Kconfig Yang Zhong
2019-01-16 11:50   ` Thomas Huth
2019-01-16 13:57     ` Paolo Bonzini
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 27/37] bluetooth: " Yang Zhong
2019-01-17 10:19   ` Thomas Huth
2019-01-15 14:10 ` [Qemu-devel] [RFC PATCH v2 28/37] isa: express dependencies with kconfig Yang Zhong
2019-01-17 10:25   ` Thomas Huth
2019-01-15 14:11 ` [Qemu-devel] [RFC PATCH v2 29/37] i386: express dependencies with Kconfig Yang Zhong
2019-01-15 14:11 ` [Qemu-devel] [RFC PATCH v2 30/37] i2c: " Yang Zhong
2019-01-17 10:30   ` Thomas Huth
2019-01-15 14:11 ` [Qemu-devel] [RFC PATCH v2 31/37] ptimer: " Yang Zhong
2019-01-17 10:32   ` Thomas Huth
2019-01-15 14:11 ` [Qemu-devel] [RFC PATCH v2 32/37] edid: express dependencies with kconfig Yang Zhong
2019-01-15 14:11 ` [Qemu-devel] [RFC PATCH v2 33/37] hyperv: " Yang Zhong
2019-01-15 14:11 ` [Qemu-devel] [RFC PATCH v2 34/37] virtio: make virtio dependencies with Kconfig Yang Zhong
2019-01-17 10:37   ` Thomas Huth
2019-01-15 14:11 ` [Qemu-devel] [RFC PATCH v2 35/37] i386-softmmu.mak: remove all CONFIG_* except boards definitions Yang Zhong
2019-01-17 11:03   ` Thomas Huth
2019-01-18  9:00     ` Yang Zhong
2019-01-15 14:11 ` [Qemu-devel] [RFC PATCH v2 36/37] minikconf: implement allyesconfig, allnoconfig, randconfig, defconfig Yang Zhong
2019-01-15 14:11 ` [Qemu-devel] [RFC PATCH v2 37/37] Makefile: only support defconfig Yang Zhong
2019-01-15 23:20 ` [Qemu-devel] [RFC PATCH v2 00/37] Support Kconfig in QEMU Paolo Bonzini
2019-01-16 12:52 ` Thomas Huth

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=20190115141108.934-18-yang.zhong@intel.com \
    --to=yang.zhong@intel.com \
    --cc=ehabkost@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-devel@nongnu.org \
    --cc=sameo@linux.intel.com \
    --cc=thuth@redhat.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.