From patchwork Mon Oct 28 03:49:39 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13852970 Received: from mail-ed1-f53.google.com (mail-ed1-f53.google.com [209.85.208.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CF9BA189F20; Mon, 28 Oct 2024 03:50:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730087410; cv=none; b=n1uXJBS7pFPXp8aUQf5PLNzSJlxwm6CERVXAg8U1vs2/KejDEJJlEjpJ2y3su4IwtHRmFF4qZSS7Ev5ZwjvmeZVmClMAvnqy5YojaSRPBrhexvxtIZSgq4QL0DZc3g5B4jBz8lfSg5Du9G+J0reyZB+tODkanZtHDvED0A1oySY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730087410; c=relaxed/simple; bh=P9zb82ZF3TOsV01pX4uqRUgkoUgICbZMKDHOOuaHYGc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=F9fGh3r22NviamxkyrPyL6nY09S1T316mB21zle1DYYeFB9S3KOTXAyvQ3NJNZx3NF70N0J13ib3/gcPLxPIyyMG+sty94mh1lAEjZKN9/yqv2u4qag7COhJwtixiQoeYD6XFkkJL+IiaQQfmBSokG3CTAVUDbpp+R4eP22cwqY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=S+Y+X284; arc=none smtp.client-ip=209.85.208.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="S+Y+X284" Received: by mail-ed1-f53.google.com with SMTP id 4fb4d7f45d1cf-5cb72918bddso5042769a12.3; Sun, 27 Oct 2024 20:50:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1730087406; x=1730692206; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=U4a2Y9QWZw7xiUTbneztFSO9nfoS4jbwm9CQiNKA4PU=; b=S+Y+X284ykdzk54aQ6XIM2CovTCbZtRs22f2YU2QdW0AtIUpyj4U8Zjsou6kPALb+5 KNsCTigr6wYpP3KAU46CD5DHUSyKLPKU6b0x3BoqZNYs4Ctl0KW9CNOa6IB7rc3VLnHy wVN4nq6p6SdNKEOjRXkpx0rUzT1k/krmhfHl6D/3g0c36yIrinqUDm2sih281wYFRy9n uSYUlUv0EEz7WMgWfq7AYFFUVdrkw4jrdSlrlEZqUnibaHTRwMjEizNiGBDkjJRP6GBI g+KKTnChhpnWN+5Deo0q9yo1GvGrM43q+zj5+WpouZMTCpzIjMRTB6Ssnz/O5pzMbVc+ 2PoA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1730087406; x=1730692206; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=U4a2Y9QWZw7xiUTbneztFSO9nfoS4jbwm9CQiNKA4PU=; b=bgfveUNtjv8kryzI0lRNHScsAmzqKHG8ovqM99Wh4A5X8ln7rVfy2RcvcGkaytCja6 xrHd7yqhBIHdrmxrsYnCop8vFMQTUr9wES8EZQClrRUMg+tzwf7M2cnNbrkdn4z57wO/ RS2zBK3WGkPem5qJM+aZDtkTme1T9+57FtyxEkKYzYA0cEGy0qBCc9l6XaHML1j5uzsr i/vTnR8ZNMj3q/LN48IkpCOLgvAfw7aii59rrVy9oFILvBscm0wUu+ZLtfwiH7MxfzWH NYl83cZAmLceII5OCQzslu5PSE1PAwiVNPiOnpKlxqv/VtumQAoQ/jlTG2B50WRiBAoL Hv+A== X-Forwarded-Encrypted: i=1; AJvYcCWVDn1quRL7LzY7UHEGvxb1PbexA+R8TFM8stweCemEqOZSsJRJjAz3GvDTMlwVaRXdEMv3OV5C5/FM9i4=@vger.kernel.org X-Gm-Message-State: AOJu0YzXTSJ6w22CqtaCVL1hMXBC3iF1PGOGZqpRcWH42QVGqej7DWgy stMtE08K1lDH7CVhCweY/lxe9WUDdleAZ7T6nM3vBTLPtCm2qLTWHJsgEA== X-Google-Smtp-Source: AGHT+IGqVGnFy/jbmnTmkEau4V4aa/W20CG3f2rVO0eR6Qr+61F/dwJpDGqtY3JjDklo+mj2kWebiw== X-Received: by 2002:a17:906:d550:b0:a9a:13c2:2b07 with SMTP id a640c23a62f3a-a9de5f27029mr688676966b.31.1730087405927; Sun, 27 Oct 2024 20:50:05 -0700 (PDT) Received: from localhost.localdomain ([2a02:908:e842:bf20:78b:e59b:2b0:d2e9]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9b30f5932fsm334599366b.168.2024.10.27.20.50.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 27 Oct 2024 20:50:05 -0700 (PDT) From: Ole Schuerks To: linux-kbuild@vger.kernel.org Cc: ole0811sch@gmail.com, jude.gyimah@rub.de, thorsten.berger@rub.de, deltaone@debian.org, jan.sollmann@rub.de, mcgrof@kernel.org, masahiroy@kernel.org, linux-kernel@vger.kernel.org, nathan@kernel.org, nicolas@fjasle.eu Subject: [PATCH v6 01/11] kconfig: Add PicoSAT interface Date: Mon, 28 Oct 2024 04:49:39 +0100 Message-Id: <20241028034949.95322-2-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20241028034949.95322-1-ole0811sch@gmail.com> References: <20241028034949.95322-1-ole0811sch@gmail.com> Precedence: bulk X-Mailing-List: linux-kbuild@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 PicoSAT (https://fmv.jku.at/picosat/) is the SAT solver used in this project. It is used as a dynamically loaded library. This commit contains a script that installs PicoSAT as a library on the host system, a source file that provides a function for loading a subset of functions from the library, and a header file that declares these functions. Signed-off-by: Patrick Franz Signed-off-by: Ibrahim Fayaz Signed-off-by: Thorsten Berger Signed-off-by: Ole Schuerks --- scripts/kconfig/picosat_functions.c | 74 +++++++++++++++++++++++++++++ scripts/kconfig/picosat_functions.h | 35 ++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 scripts/kconfig/picosat_functions.c create mode 100644 scripts/kconfig/picosat_functions.h diff --git a/scripts/kconfig/picosat_functions.c b/scripts/kconfig/picosat_functions.c new file mode 100644 index 000000000000..ada42abbc22b --- /dev/null +++ b/scripts/kconfig/picosat_functions.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +#include "array_size.h" + +#include "cf_defs.h" +#include "picosat_functions.h" + +const char *picosat_lib_names[] = { "libpicosat-trace.so", + "libpicosat-trace.so.0", + "libpicosat-trace.so.1" }; + +PicoSAT *(*picosat_init)(void); +int (*picosat_add)(PicoSAT *pico, int lit); +int (*picosat_deref)(PicoSAT *pico, int lit); +void (*picosat_assume)(PicoSAT *pico, int lit); +int (*picosat_sat)(PicoSAT *pico, int decision_limit); +const int *(*picosat_failed_assumptions)(PicoSAT *pico); +int (*picosat_added_original_clauses)(PicoSAT *pico); +int (*picosat_enable_trace_generation)(PicoSAT *pico); +void (*picosat_print)(PicoSAT *pico, FILE *file); + +#define PICOSAT_FUNCTION_LIST \ + X(picosat_init) \ + X(picosat_add) \ + X(picosat_deref) \ + X(picosat_assume) \ + X(picosat_sat) \ + X(picosat_failed_assumptions) \ + X(picosat_added_original_clauses) \ + X(picosat_enable_trace_generation)\ + X(picosat_print) + +static void load_function(const char *name, void **ptr, void *handle, bool *failed) +{ + if (*failed) + return; + + *ptr = dlsym(handle, name); + if (!*ptr) { + printd("While loading %s: %s\n", name, dlerror()); + *failed = true; + } +} + +bool load_picosat(void) +{ + void *handle = NULL; + bool failed = false; + + /* + * Try different names for the .so library. This is necessary since + * all packages don't use the same versioning. + */ + for (int i = 0; i < ARRAY_SIZE(picosat_lib_names) && !handle; ++i) + handle = dlopen(picosat_lib_names[i], RTLD_LAZY); + if (!handle) { + printd("%s\n", dlerror()); + return false; + } + +#define X(name) load_function(#name, (void **) &name, handle, &failed); + + PICOSAT_FUNCTION_LIST +#undef X + + if (failed) { + dlclose(handle); + return false; + } else + return true; +} diff --git a/scripts/kconfig/picosat_functions.h b/scripts/kconfig/picosat_functions.h new file mode 100644 index 000000000000..5d8524afa844 --- /dev/null +++ b/scripts/kconfig/picosat_functions.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef PICOSAT_FUNCTIONS_H +#define PICOSAT_FUNCTIONS_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define PICOSAT_UNKNOWN 0 +#define PICOSAT_SATISFIABLE 10 +#define PICOSAT_UNSATISFIABLE 20 + +typedef struct PicoSAT PicoSAT; + +extern PicoSAT *(*picosat_init)(void); +extern int (*picosat_add)(PicoSAT *pico, int lit); +extern int (*picosat_deref)(PicoSAT *pico, int lit); +extern void (*picosat_assume)(PicoSAT *pico, int lit); +extern int (*picosat_sat)(PicoSAT *pico, int decision_limit); +extern const int *(*picosat_failed_assumptions)(PicoSAT *pico); +extern int (*picosat_added_original_clauses)(PicoSAT *pico); +extern int (*picosat_enable_trace_generation)(PicoSAT *pico); +extern void (*picosat_print)(PicoSAT *pico, FILE *file); + +bool load_picosat(void); + +#ifdef __cplusplus +} +#endif + +#endif // PICOSAT_FUNCTIONS_H From patchwork Mon Oct 28 03:49:40 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13852971 Received: from mail-ed1-f50.google.com (mail-ed1-f50.google.com [209.85.208.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C875618A959; Mon, 28 Oct 2024 03:50:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730087411; cv=none; b=DnE0Jh5OMzu3YqomC75WLPNuvySK/HReroW122aJo627IykcL29khuqS6xMi7qCCn5XmnCb0t+usxlWmQvcrIpyLUZ9KsydhYCnMI2VmHhikZy7Vf3iWkLR455n/9SxiR0QymGAqcSvj6GBhZmTyspAbLQ5GXhiZrMScDV7CrFY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730087411; c=relaxed/simple; bh=ifCKAl8xVCdFFQlr8xJlI3ddk6mjeoWe5hPaL6oEUPI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=NxVCd+fmuDNh3pDqRfr3jbN2lQiX7eNi6QCxFmCRonkmQflLv86TOTWjY8CbUhMwjeQnHyTS3D7fHvSmKCULAPOKmJgVNJYZkVdUNY+lHPG6DYZyr99tNGVdGjGlrqdbpizY53sDjUb+eptvKkJqxvsT9I+s8F0YOEDxEVUX/mU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=K1ucgzVN; arc=none smtp.client-ip=209.85.208.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="K1ucgzVN" Received: by mail-ed1-f50.google.com with SMTP id 4fb4d7f45d1cf-5c935d99dc5so4319041a12.1; Sun, 27 Oct 2024 20:50:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1730087408; x=1730692208; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=VQ7mAumYZVzVpV3VI37dduP6ySzc3FV45gfVD05zAaY=; b=K1ucgzVNacubTGZ2ry2dQQCBFSpQlU73aneM5AzDlgUN6H0rWhy4plaUwUbYLzAkbR 9dnSbfKjqjFB5zxzxdTH58nAU42cPuq4smK5MKY1m/ZS9sxbhZt4kX2+rmDX1mJB/4oJ xA+V402rjUZaQSOa33hupVu5ljwcWRdxlZPBxTwFHqdW+UhnJLPqlcYwwx2ydvSrQx04 ojJw9/b5j4n5dDW0pKWzYssJZmlDM8Cq9XPAQ7t4h/3M+XeKo33KpoaSDqUeKbA32Uh6 et9sXAU6TttIhJkq71uAKd/o1wl8aoBaRty77KfsD+2fefwLgQ/HefnBTXYXvwn0ltaC PcQA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1730087408; x=1730692208; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=VQ7mAumYZVzVpV3VI37dduP6ySzc3FV45gfVD05zAaY=; b=LogmZwMxrceoLMQyjTVnHxZx7UPHJKunsaTHeJQkz+1eciqyyFZtvoAUrXjVmfHSQ2 FYpvfUWiMAbFNG5kInPLYNKCTc2jZ+v6FDUQBMhq0109bOZfolBPltBNpNNC6Y4NvBlL K1eWntkZeWmwgPVW+eKKjfL9JKvBO9wHebnzjmn9IccYYaqzuUNqDmF3zrV7wqPWTOAN 8aGoDxA0TKy1uJ8qern8TnMcXdkC4z1MYPnXR4LBuchez5iK0KCiDUJ09mw7f13RMw65 rDm/2c326KqULBGROO02coUodrCCN+UvDzZ2O3pgwD0YdPv3bpucog1EytSYy6yWQqnl 5hTA== X-Forwarded-Encrypted: i=1; AJvYcCXmzDG/fGvdqC9dXqAVzBn5XoAK2vArsyRrAlDDx7feVswGdCr+1cAcRU03xpjIJlNLBMVZjpw8DQqXHvg=@vger.kernel.org X-Gm-Message-State: AOJu0Yyf+wCUM+3zde8wBraI6Sby22kfv+RZySKM7GbUWcmJ6OE6GMiE wAgpS/SqB6wxpB4Twfb+lIGHDsnauniSblEa9CQSsiO37dkTFYa6YQMFQg== X-Google-Smtp-Source: AGHT+IGPoetqkVQatYQdf1xY6hWJjL2RJpJBhg/calw+5WbdnT80kmLvxL3nvh0UoXepLgJMbrXuuA== X-Received: by 2002:a17:907:8686:b0:a99:4615:f58c with SMTP id a640c23a62f3a-a9de5d6eb9emr683602066b.2.1730087407876; Sun, 27 Oct 2024 20:50:07 -0700 (PDT) Received: from localhost.localdomain ([2a02:908:e842:bf20:78b:e59b:2b0:d2e9]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9b30f5932fsm334599366b.168.2024.10.27.20.50.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 27 Oct 2024 20:50:07 -0700 (PDT) From: Ole Schuerks To: linux-kbuild@vger.kernel.org Cc: ole0811sch@gmail.com, jude.gyimah@rub.de, thorsten.berger@rub.de, deltaone@debian.org, jan.sollmann@rub.de, mcgrof@kernel.org, masahiroy@kernel.org, linux-kernel@vger.kernel.org, nathan@kernel.org, nicolas@fjasle.eu Subject: [PATCH v6 02/11] kbuild: Add list_size, list_at_index, list_for_each_from Date: Mon, 28 Oct 2024 04:49:40 +0100 Message-Id: <20241028034949.95322-3-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20241028034949.95322-1-ole0811sch@gmail.com> References: <20241028034949.95322-1-ole0811sch@gmail.com> Precedence: bulk X-Mailing-List: linux-kbuild@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 list_size counts the number of entries. list_at_index retrieves the entry at an index. list_for_each_from iterates over a list from some entry to the end. Signed-off-by: Ole Schuerks --- scripts/include/list.h | 49 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/scripts/include/list.h b/scripts/include/list.h index 8bdcaadca709..0c50774606c2 100644 --- a/scripts/include/list.h +++ b/scripts/include/list.h @@ -219,6 +219,21 @@ static inline int list_empty(const struct list_head *head) return head->next == head; } +/** + * list_size - counts the number of entries in a list + * @head: the list whose entries are counted + */ +static inline size_t list_size(const struct list_head *head) +{ + size_t ret = 0; + + for (struct list_head *curr = head->next; curr != head; + curr = curr->next) + ++ret; + + return ret; +} + /** * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. @@ -310,6 +325,40 @@ static inline int list_empty(const struct list_head *head) !list_entry_is_head(pos, head, member); \ pos = n, n = list_next_entry(n, member)) +/** + * list_for_each_entry_from - iterate over list of given type starting at a given node + * @pos: the type * to use as a loop cursor. + * @start: the node to start iterating at + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry_from(pos, start, head, member) \ + for (pos = list_entry(start, typeof(*pos), member); \ + !list_entry_is_head(pos, head, member); \ + pos = list_next_entry(pos, member)) + +/** + * list_at_index - retrieve the entry at index i in O(n) + * @i: index of entry to retrieve. + * @head: the head for your list. + * @type: the type of the struct the entries are embedded in. + * @member: the name of the list_head within the struct. + */ +#define list_at_index(i, head, type, member) \ + ({ \ + type *__pos; \ + size_t __counter = 0; \ + list_for_each_entry(__pos, head, member) { \ + if (__counter++ == i) \ + break; \ + if (__pos->member.next == head) { \ + __pos = NULL; \ + break; \ + } \ + } \ + __pos; \ + }) + /* * Double linked lists with a single pointer list head. * Mostly useful for hash tables where the two pointer list head is From patchwork Mon Oct 28 03:49:41 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13852972 Received: from mail-ej1-f48.google.com (mail-ej1-f48.google.com [209.85.218.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9F42E18BC19; Mon, 28 Oct 2024 03:50:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730087414; cv=none; b=cBb0axiADRoYu65ut3RcCctg3k48qhADQRWdHQd+SESbKA8VQJFXNY7E7yDGZzPk6KU1l+CseM7GgyCkMxZIU1LY3vLkdBMYD0LozD/17h/mEbSKYfdNIqMdVsM7SNe6eqWw+aKYUD8N+CtZwSpVPlZrUFNKjKL7zZL8mvjP0aU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730087414; c=relaxed/simple; bh=k9Pg0g29CllsjxHSePoTf3woFh0Lh50RyLppmsi4mbs=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=db1iG/Ez/MrVl8ThMUYPtlB5AxkxkOoVopBkgqGZiD/pQdSbT7Ru4V2eYTkxOsZjPhTEmekmXWtNRO7Mq0w7CjSk1Uejde2uTZ0S4GUIMYYfo9wiwxE/+BZPotLxlxr3lK+ts3pNp+ZUOMel/5Dg4GcLvWdcFX4emZsYIK+vGB8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=ii4Xk4EA; arc=none smtp.client-ip=209.85.218.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ii4Xk4EA" Received: by mail-ej1-f48.google.com with SMTP id a640c23a62f3a-a9aa8895facso623222666b.2; Sun, 27 Oct 2024 20:50:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1730087410; x=1730692210; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=cq3p/vnBdzkwGfECoynwncxNFavjyfaYTvdBRHdEFOY=; b=ii4Xk4EAntOmuxCNicsIBDV7rTXykuiDKivDOzIBYC4YbMNhOY+77LiFuuX/vMUpGP L61YGwecFRYjYbGxh5vcUmNyKpZPxUMycSGIUkcrB5FORnJafeTpfdiseagZKt+6Bp3Z gh8+ZKbfJjTujUmcRHPatlYjtUHjd85mAVgsHZ2Ynn8uMPKznCRdYi936Gco53dmOdsh fo409WEKfaBkdK92QThJ8vYzi0Fbi4gAT8hSyQ5OWZArAxq/kSDrghak+Pij5Jg1lm0k mFKdi2IY1GrrQ8bumsTu0SPdlXSzfKjda9P49zthd76gOveZtamxSxPhtsvzPsUqeo3k hxzw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1730087410; x=1730692210; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=cq3p/vnBdzkwGfECoynwncxNFavjyfaYTvdBRHdEFOY=; b=QyFOxOblrRgQZQU5Mh4gEtBsplsp6lgTeb6qhHnRvDip6Bz2e6zHbQvsP7xeU+Ab39 U/HXXwgCWx4mr30T6RWrg/XshOjWwQZ/aZ5oaUB4lzp5AYSir+788E9/yszCDR5ZHKP7 MVRKIF8LYrlfd2Eqo46kb/As3HWDqQhJhgU5gOxAIfWDKrNuurDpnKPtEfPiHQMCTH8g 5gblQxHA2n52ILdUdIEKnaeOyTW4DCN5Ewy7BOQJY22p2t7Lz2MtSBbzAg35JmJFQUaT TC1GwB0AskjEtKfm6lR+rFOtgQ8+KISiXUyi990KP99nNqWjp+4MBnVaedyVeHHduVJ7 qGKw== X-Forwarded-Encrypted: i=1; AJvYcCVmN20R0LAEovC+ntKG2lyZDOYMHuRxFK9MZ6FtcCKdFF6stZ/L+WDdfsifGbR4DvaTs2VUgze+N0aKEN4=@vger.kernel.org X-Gm-Message-State: AOJu0YxcsQ36m5qCjW+cAZR8HyMWiohe0lIF5zZqJ59rBNNKJF1iFIPF MilBeYgihk8ifuq3s+shDMfkFzLOw0zUxj3sE/7mhQfnOtk175eKdDN22g== X-Google-Smtp-Source: AGHT+IGk7wV+gq7Q1uJhb1qerd7i63C9dpb8ao1rJ/tcQKpPzluXl65z7oyHgwadmUCd55wfT3QiDg== X-Received: by 2002:a17:906:c112:b0:a9a:26c9:ac14 with SMTP id a640c23a62f3a-a9de5d6ed92mr648469466b.1.1730087409736; Sun, 27 Oct 2024 20:50:09 -0700 (PDT) Received: from localhost.localdomain ([2a02:908:e842:bf20:78b:e59b:2b0:d2e9]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9b30f5932fsm334599366b.168.2024.10.27.20.50.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 27 Oct 2024 20:50:09 -0700 (PDT) From: Ole Schuerks To: linux-kbuild@vger.kernel.org Cc: ole0811sch@gmail.com, jude.gyimah@rub.de, thorsten.berger@rub.de, deltaone@debian.org, jan.sollmann@rub.de, mcgrof@kernel.org, masahiroy@kernel.org, linux-kernel@vger.kernel.org, nathan@kernel.org, nicolas@fjasle.eu Subject: [PATCH v6 03/11] kconfig: Add definitions Date: Mon, 28 Oct 2024 04:49:41 +0100 Message-Id: <20241028034949.95322-4-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20241028034949.95322-1-ole0811sch@gmail.com> References: <20241028034949.95322-1-ole0811sch@gmail.com> Precedence: bulk X-Mailing-List: linux-kbuild@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 We need to be able to store constraints for each symbol. We therefore add several expressions for each such struct which we define in a header-file. Finally, we prepare the Makefile. Co-developed-by: Patrick Franz Signed-off-by: Patrick Franz Co-developed-by: Ibrahim Fayaz Signed-off-by: Ibrahim Fayaz Reviewed-by: Luis Chamberlain Tested-by: Evgeny Groshev Suggested-by: Sarah Nadi Suggested-by: Thorsten Berger Signed-off-by: Thorsten Berger Signed-off-by: Ole Schuerks --- scripts/kconfig/Makefile | 11 +- scripts/kconfig/cf_defs.h | 391 ++++++++++++++++++++++++++++++++++++++ scripts/kconfig/expr.h | 17 ++ 3 files changed, 417 insertions(+), 2 deletions(-) create mode 100644 scripts/kconfig/cf_defs.h diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile index a0a0be38cbdc..e907713a4a76 100644 --- a/scripts/kconfig/Makefile +++ b/scripts/kconfig/Makefile @@ -43,6 +43,7 @@ menuconfig-prog := mconf nconfig-prog := nconf gconfig-prog := gconf xconfig-prog := qconf +cfoutconfig-prog := cfoutconfig define config_rule PHONY += $(1) @@ -53,7 +54,7 @@ PHONY += build_$(1) build_$(1): $(obj)/$($(1)-prog) endef -$(foreach c, config menuconfig nconfig gconfig xconfig, $(eval $(call config_rule,$(c)))) +$(foreach c, config menuconfig nconfig gconfig xconfig cfoutconfig, $(eval $(call config_rule,$(c)))) PHONY += localmodconfig localyesconfig localyesconfig localmodconfig: $(obj)/conf @@ -152,6 +153,7 @@ help: @echo ' default value without prompting' @echo ' tinyconfig - Configure the tiniest possible kernel' @echo ' testconfig - Run Kconfig unit tests (requires python3 and pytest)' + @echo ' cfoutconfig - Print constraints and DIMACS-output into files (requires PicoSAT)' @echo '' @echo 'Configuration topic targets:' @$(foreach f, $(all-config-fragments), \ @@ -196,10 +198,15 @@ $(foreach f, mconf.o $(lxdialog), \ $(obj)/mconf: | $(obj)/mconf-libs $(addprefix $(obj)/, mconf.o $(lxdialog)): | $(obj)/mconf-cflags +# configfix: Used for the xconfig target as well as for its debugging tools +hostprogs += cfoutconfig +cfconf-objs := configfix.o cf_constraints.o cf_expr.o cf_rangefix.o cf_utils.o picosat_functions.o +cfoutconfig-objs := cfoutconfig.o $(common-objs) $(cfconf-objs) + # qconf: Used for the xconfig target based on Qt hostprogs += qconf qconf-cxxobjs := qconf.o qconf-moc.o -qconf-objs := images.o $(common-objs) +qconf-objs := images.o $(common-objs) $(cfconf-objs) HOSTLDLIBS_qconf = $(call read-file, $(obj)/qconf-libs) HOSTCXXFLAGS_qconf.o = -std=c++11 -fPIC $(call read-file, $(obj)/qconf-cflags) diff --git a/scripts/kconfig/cf_defs.h b/scripts/kconfig/cf_defs.h new file mode 100644 index 000000000000..4d7c2cac2b24 --- /dev/null +++ b/scripts/kconfig/cf_defs.h @@ -0,0 +1,391 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Patrick Franz + */ + +#ifndef DEFS_H +#define DEFS_H + +/* global variables */ +#include +#include +#include + +#include + +#include "lkc.h" +#include "expr.h" +#include "list_types.h" + +extern bool CFDEBUG; +extern bool stop_rangefix; + +#define printd(fmt...) do { \ + if (CFDEBUG) \ + printf(fmt); \ +} while (0) + +/* + * Helper macros for use of list.h with type safety. + * Lists of type X can then be defined as + * `struct X_list { + * struct list_head list; + * }`, + * which contains the head of the list, and the nodes with the actual elements + * are contained in `struct X_node { + * struct X *elem; + * struct list_head node; + * }` + */ + +/* macros for internal usage */ +#define __NODE_T(prefix) struct prefix##_node +#define __LIST_T(prefix) struct prefix##_list +#define __CF_DEFS_TO_STR2(x) #x +#define __CF_DEFS_TO_STR(x) __CF_DEFS_TO_STR2(x) +#define __ASSERT_LIST_PREF(list, prefix) \ + _Static_assert(__builtin_types_compatible_p(typeof(*list), \ + __LIST_T(prefix)), \ + "Incorrect type of list, should be `" __CF_DEFS_TO_STR( \ + __LIST_T(prefix)) " *`") +#define __ASSERT_NODE_PREF(node, prefix) \ + _Static_assert(__builtin_types_compatible_p(typeof(*node), \ + __NODE_T(prefix)), \ + "Incorrect type of node, should be `" __CF_DEFS_TO_STR( \ + __LIST_T(prefix)) " *`") + +/* + * CF_ALLOC_NODE - Utility macro for allocating, initializing and returning an + * object of a type like struct fexpr_node + * + * @node_type: type of the object to create a pointer to (e.g. struct fexpr_node) + * @el: the value to set field .element to + */ +#define CF_ALLOC_NODE(el, prefix) \ + ({ \ + __NODE_T(prefix) *__node_cf_alloc = \ + xmalloc(sizeof(*__node_cf_alloc)); \ + __node_cf_alloc->elem = el; \ + INIT_LIST_HEAD(&__node_cf_alloc->node); \ + __node_cf_alloc; \ + }) + +/* + * constructs an object using CF_ALLOC_NODE(node_type, el) and then adds to the + * end of list->list + */ +#define CF_PUSH_BACK(list_, el, prefix) \ + do { \ + __ASSERT_LIST_PREF(list_, prefix); \ + __NODE_T(prefix) *__cf_emplace_back_node = \ + CF_ALLOC_NODE(el, prefix); \ + list_add_tail(&__cf_emplace_back_node->node, &(list_)->list); \ + } while (0) + +/* + * frees all nodes and then list_ + */ +#define CF_LIST_FREE(list_, prefix) \ + do { \ + __NODE_T(prefix) * __node, *__next; \ + __ASSERT_LIST_PREF(list_, prefix); \ + list_for_each_entry_safe(__node, __next, &(list_)->list, \ + node) { \ + list_del(&__node->node); \ + free(__node); \ + } \ + free(list_); \ + } while (0) + +#define __CF_LIST_INIT(full_list_type) \ + ({ \ + full_list_type *__cf_list = xmalloc(sizeof(*__cf_list)); \ + INIT_LIST_HEAD(&__cf_list->list); \ + __cf_list; \ + }) + +#define __CF_DEF_LIST(name, full_list_type) \ + full_list_type *name = __CF_LIST_INIT(full_list_type) + +/* + * declares and initializes a list + */ +#define CF_DEF_LIST(name, prefix) __CF_DEF_LIST(name, __LIST_T(prefix)) + +/* + * returns initialized a list + */ +#define CF_LIST_INIT(prefix) __CF_LIST_INIT(__LIST_T(prefix)) + +#define CF_LIST_FOR_EACH(node_, list_, prefix) \ + list_for_each_entry(node_, ({ \ + __ASSERT_LIST_PREF(list_, prefix); \ + __ASSERT_NODE_PREF(node_, prefix); \ + &(list_)->list; \ + }), \ + node) + +#define CF_LIST_COPY(orig, prefix) \ + ({ \ + __CF_DEF_LIST(__ret, typeof(*orig)); \ + __NODE_T(prefix) * __node; \ + CF_LIST_FOR_EACH(__node, orig, prefix) \ + CF_PUSH_BACK(__ret, __node->elem, prefix); \ + __ret; \ + }) + +/* + * For functions that construct nested pexpr expressions. + */ +enum pexpr_move { + PEXPR_ARG1, /* put reference to first pexpr */ + PEXPR_ARG2, /* put reference to second pexpr */ + PEXPR_ARGX /* put all references to pexpr's */ +}; + + +/* different types for f_expr */ +enum fexpr_type { + FE_SYMBOL, + FE_NPC, /* no prompt condition */ + FE_TRUE, /* constant of value True */ + FE_FALSE, /* constant of value False */ + FE_NONBOOL, /* for all non-(boolean/tristate) known values */ + FE_CHOICE, /* symbols of type choice */ + FE_SELECT, /* auxiliary variable for selected symbols */ + FE_TMPSATVAR /* temporary sat-variable (Tseytin) */ +}; + +/* struct for a propositional logic formula */ +struct fexpr { + /* name of the feature expr */ + struct gstr name; + + /* associated symbol */ + struct symbol *sym; + + /* integer value for the SAT solver */ + int satval; + + /* assumption in the last call to PicoSAT */ + bool assumption; + + /* type of the fexpr */ + enum fexpr_type type; + + union { + /* symbol */ + struct { + tristate tri; + }; + /* EQUALS */ + struct { + struct symbol *eqsym; + struct symbol *eqvalue; + }; + /* HEX, INTEGER, STRING */ + struct { + struct gstr nb_val; + }; + }; +}; + +/* struct definitions for lists */ +struct fexpr_node { + struct fexpr *elem; + struct list_head node; +}; + +struct fexpr_list { + struct list_head list; +}; + +struct fexl_list { + struct list_head list; +}; + +struct fexl_node { + struct fexpr_list *elem; + struct list_head node; +}; + +struct pexpr_list { + struct list_head list; +}; + +struct pexpr_node { + struct pexpr *elem; + struct list_head node; +}; + +/** + * struct defm_list - Map from values of default properties of a symbol to their + * (accumulated) conditions + */ +struct defm_list { + struct list_head list; +}; + +struct defm_node { + struct default_map *elem; + struct list_head node; +}; + +struct sfix_list { + struct list_head list; +}; + +struct sfix_node { + struct symbol_fix *elem; + struct list_head node; +}; + +struct sfl_list { + struct list_head list; +}; + +struct sfl_node { + struct sfix_list *elem; + struct list_head node; +}; + +struct sym_list { + struct list_head list; +}; + +struct sym_node { + struct symbol *elem; + struct list_head node; +}; + +struct prop_list { + struct list_head list; +}; + +struct prop_node { + struct property *elem; + struct list_head node; +}; + +struct sdv_list { + struct list_head list; +}; + +struct sdv_node { + struct symbol_dvalue *elem; + struct list_head node; +}; + + +enum pexpr_type { + PE_SYMBOL, + PE_AND, + PE_OR, + PE_NOT +}; + +union pexpr_data { + struct pexpr *pexpr; + struct fexpr *fexpr; +}; + +/** + * struct pexpr - a node in a tree representing a propositional formula + * @type: Type of the node + * @left: left-hand-side for AND and OR, the unique operand for NOT, and for + * SYMBOL it contains the fpexpr. + * @right: right-hand-side for AND and OR + * @ref_count: Number of calls to pexpr_put() that need to effectuated with this + * pexpr for it to get free'd. + * + * Functions that return new struct pexpr instances (like pexpr_or(), + * pexpr_or_share(), pexf(), ...) set @ref_count in a way that accounts for the + * new reference that they return (e.g. pexf() will always set it to 1). + * Functions with arguments of type ``struct pexpr *`` will generally keep the + * reference intact, so that for example + * ``e = pexf(sym); not_e = pexpr_not_share(e)`` would require + * ``pexpr_put(not_e)`` before not_e can be free'd and additionally + * ``pexpr_put(e)`` for e to get free'd. Some functions take an argument of type + * ``enum pexpr_move`` which function as a wrapper of sorts that first executes + * a function and then pexpr_put's the argument(s) specified by the + * ``enum pexpr_move`` argument (e.g. the normal function for OR is + * pexpr_or_share() and the wrapper is pexpr_or()). + */ +struct pexpr { + enum pexpr_type type; + union pexpr_data left, right; + unsigned int ref_count; +}; + +enum symboldv_type { + SDV_BOOLEAN, /* boolean/tristate */ + SDV_NONBOOLEAN /* string/int/hex */ +}; + +/** + * struct default_map - Map entry from default values to their condition + * @val: value of the default property. Not 'owned' by this struct and + * therefore shouldn't be free'd. + * @e: condition that implies that the symbol assumes the @val. Needs to be + * pexpr_put when free'ing. + */ +struct default_map { + struct fexpr *val; + struct pexpr *e; +}; + +struct symbol_dvalue { + struct symbol *sym; + + enum symboldv_type type; + + union { + /* boolean/tristate */ + tristate tri; + + /* string/int/hex */ + struct gstr nb_val; + }; +}; + +enum symbolfix_type { + SF_BOOLEAN, /* boolean/tristate */ + SF_NONBOOLEAN, /* string/int/hex */ + SF_DISALLOWED /* disallowed non-boolean values */ +}; + +struct symbol_fix { + struct symbol *sym; + + enum symbolfix_type type; + + union { + /* boolean/tristate */ + tristate tri; + + /* string/int/hex */ + struct gstr nb_val; + + /* disallowed non-boolean values */ + struct gstr disallowed; + }; +}; + +struct constants { + struct fexpr *const_false; + struct fexpr *const_true; + struct fexpr *symbol_yes_fexpr; + struct fexpr *symbol_mod_fexpr; + struct fexpr *symbol_no_fexpr; +}; + +struct cfdata { + unsigned int sat_variable_nr; + unsigned int tmp_variable_nr; + struct fexpr **satmap; // map SAT variables to fexpr + size_t satmap_size; + struct constants *constants; + struct sdv_list *sdv_symbols; // array with conflict-symbols +}; + +#endif diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h index 21578dcd4292..1b5427fe90e5 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -140,6 +140,19 @@ struct symbol { * "Weak" reverse dependencies through being implied by other symbols */ struct expr_value implied; + + /* + * ConfigFix + */ + struct fexpr *fexpr_y; + struct fexpr *fexpr_m; + struct fexpr *fexpr_sel_y; + struct fexpr *fexpr_sel_m; + struct pexpr *list_sel_y; + struct pexpr *list_sel_m; + struct fexpr *noPromptCond; + struct fexpr_list *nb_vals; /* list of struct fexpr_node's; used for non-booleans */ + struct pexpr_list *constraints; /* list of constraints for symbol */ }; #define SYMBOL_CONST 0x0001 /* symbol is const */ @@ -201,6 +214,10 @@ struct property { for (st = sym->prop; st; st = st->next) \ if (st->type == (tok)) #define for_all_defaults(sym, st) for_all_properties(sym, st, P_DEFAULT) +#define for_all_choices(symbol, child, menu_ptr) \ + list_for_each_entry(menu_ptr, &(symbol)->menus, link) \ + for (child = (menu_ptr)->list; child; child = (child)->next) \ + if ((child)->sym && sym_is_choice_value((child)->sym)) #define for_all_prompts(sym, st) \ for (st = sym->prop; st; st = st->next) \ if (st->text) From patchwork Mon Oct 28 03:49:42 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13852973 Received: from mail-ed1-f42.google.com (mail-ed1-f42.google.com [209.85.208.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C9C6E189BA2; Mon, 28 Oct 2024 03:50:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730087419; cv=none; b=cacfLMnAHc4Xuq6zQrjRZH9qZFbmTU5VDWMtmT9TWyLlw8iu/eLsNXG87xH1LDoLZY+eYqhCMxAive1gI6iFuFc+CPZ1cgipGDxpNUPfrbP60dJ3UekVT4TSE4LXcjwZDpj/2GFu1XqQ673E2MYfoemTHd80A76QMAuulL37P+s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730087419; c=relaxed/simple; bh=/aVKGeA777U5eG1uhyMAsfd819ksCDLc6jSxEaYw3N8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=UArN7QwsMZt25rPxSKDGwte3mno4l9HPE5+xHBpxmuYP8JodI7FsxYfo13MFODt/O7cTCuTRYbM5J1UX5sWjxtx90TP0SNFRxW7VCgH2iGH6Tqtdy45gDvmyulli0y/AyrWrrPQEfGChkGHJ1pv+5WSO7gmFU5c59wD/TesEFLs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=ncjZS6Ef; arc=none smtp.client-ip=209.85.208.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ncjZS6Ef" Received: by mail-ed1-f42.google.com with SMTP id 4fb4d7f45d1cf-5cb15b84544so4864672a12.2; Sun, 27 Oct 2024 20:50:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1730087412; x=1730692212; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=dNl8lHJI8Gm3a3dc3tXMXZ/OalTBY3QOpHdBJvTKdnI=; b=ncjZS6EfY1um30Zt7UIr8zyPQZT1ECJDZkWBZu4NNm6AO7qsUW3pdVqAO0f2FiRdEt noBMSfL5YHZZ0t81kI/HinRASS4kBZK6m4mZo+cFlyti2X/et4x41Pb3zW7T8QRpiXbV Gf+xvfQHCBVHrH5tV2HJo7zylYxencoNyCMHpd1CqiZTAg/lnqZ8pFB9pM18iEbI9Nx/ 71TenPSHMlP5tdDtRS0urI9mDJBgfviV6Gxq69LQiaQPK3BwkH4IDTpiIvjw4X4p/NNo qabq0okQNudb+WfmL6laGKrd6ZLf7qWSCi5ySs2wbsmkViRe+kV7jhXON4KNuwBmi90X ManA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1730087412; x=1730692212; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=dNl8lHJI8Gm3a3dc3tXMXZ/OalTBY3QOpHdBJvTKdnI=; b=SDPJkCx8OgeM/3vPZUv4syJX6dJRplIR7NUR0xfwaMsFoCXN8oUIvJ2jApkSqftv/9 Bv6K7n7G9+cwGYSgP0U54eTHzkwtXxnJskj9jKivYdUAJpWU+4JrECPUbroJx3/yerwm EJOV9JVX8e4KOHDw73hKpACZAUwN22TJn2ex2Sz0Wf/PrzCSawI1OZXpAulcL9xP5LkY lV4e2PHbNdX+TFQ4YYfQZf9lNuTX2wbCozWHhz+MWXvRFkD7tw66GLEg+9gGgzZnQ9BY MaUfIKAef8CJTWyfuLNn+ZYA50TOFJm8IAr/gUGm5FBwCqmeu47M9Czi7anVBxuriAQx VdMg== X-Forwarded-Encrypted: i=1; AJvYcCWeBk/TPF6cP38J6373O8xA0CgkC9w6mpLwQrO1H5SdHg9bN/FeA7hDhjFfX33TPrfJHPhuTDbGwW96DDM=@vger.kernel.org X-Gm-Message-State: AOJu0YzuusYlEXRzJUl/djWTjYIJngEVGlDeJ1XVV+IAsRTTXVB44I8I ++/BH7QnCGeQ1E4MlOtOSl0gc8EFbmt0+3/FoAwi1PJsOp3Tgnkv6BQNfQ== X-Google-Smtp-Source: AGHT+IEm7zbrxYSepM72ETZ/Qsk0sdV3SvOJtGvzPqe9tvj1JYtqbaPgl2JgGs7WZyPhV5dXRSGVuA== X-Received: by 2002:a17:907:944c:b0:a8d:2b7a:ff44 with SMTP id a640c23a62f3a-a9de5f256c2mr547373066b.32.1730087411645; Sun, 27 Oct 2024 20:50:11 -0700 (PDT) Received: from localhost.localdomain ([2a02:908:e842:bf20:78b:e59b:2b0:d2e9]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9b30f5932fsm334599366b.168.2024.10.27.20.50.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 27 Oct 2024 20:50:11 -0700 (PDT) From: Ole Schuerks To: linux-kbuild@vger.kernel.org Cc: ole0811sch@gmail.com, jude.gyimah@rub.de, thorsten.berger@rub.de, deltaone@debian.org, jan.sollmann@rub.de, mcgrof@kernel.org, masahiroy@kernel.org, linux-kernel@vger.kernel.org, nathan@kernel.org, nicolas@fjasle.eu Subject: [PATCH v6 04/11] kconfig: Add files for building constraints Date: Mon, 28 Oct 2024 04:49:42 +0100 Message-Id: <20241028034949.95322-5-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20241028034949.95322-1-ole0811sch@gmail.com> References: <20241028034949.95322-1-ole0811sch@gmail.com> Precedence: bulk X-Mailing-List: linux-kbuild@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 These files translate the Kconfig-model into propositional logic and store the constraints for each symbol in the corresponding struct. Co-developed-by: Patrick Franz Signed-off-by: Patrick Franz Co-developed-by: Ibrahim Fayaz Signed-off-by: Ibrahim Fayaz Reviewed-by: Luis Chamberlain Tested-by: Evgeny Groshev Suggested-by: Sarah Nadi Suggested-by: Thorsten Berger Signed-off-by: Thorsten Berger Signed-off-by: Ole Schuerks --- scripts/kconfig/cf_constraints.c | 1779 ++++++++++++++++++++++++++++++ scripts/kconfig/cf_constraints.h | 24 + 2 files changed, 1803 insertions(+) create mode 100644 scripts/kconfig/cf_constraints.c create mode 100644 scripts/kconfig/cf_constraints.h diff --git a/scripts/kconfig/cf_constraints.c b/scripts/kconfig/cf_constraints.c new file mode 100644 index 000000000000..2181b823c73f --- /dev/null +++ b/scripts/kconfig/cf_constraints.c @@ -0,0 +1,1779 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Patrick Franz + */ + +#include "cf_defs.h" +#include "expr.h" +#include "list.h" +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cf_utils.h" +#include "internal.h" +#include "cf_expr.h" +#include "cf_constraints.h" + +#define KCR_CMP false +#define NPC_OPTIMISATION true + +static void find_nonboolean_known_vals(struct cfdata *data); +static void build_constraints_bool(struct cfdata *data); +static void build_constraints_select(struct cfdata *data); +static void build_constraints_nonbool(struct cfdata *data); + +static void build_tristate_constraint_clause(struct symbol *sym, + struct cfdata *data); + +static void add_selects_kcr(struct symbol *sym, struct cfdata *data); +static void add_selects(struct symbol *sym, struct cfdata *data); + +static void add_dependencies_bool(struct symbol *sym, struct cfdata *data); +static void add_dependencies_bool_kcr(struct symbol *sym, struct cfdata *data); +static void add_dependencies_nonbool(struct symbol *sym, struct cfdata *data); + +static void add_choice_prompt_cond(struct symbol *sym, struct cfdata *data); +static void add_choice_dependencies(struct symbol *sym, struct cfdata *data); +static void add_choice_constraints(struct symbol *sym, struct cfdata *data); +static void add_invisible_constraints(struct symbol *sym, struct cfdata *data); +static void sym_nonbool_at_least_1(struct symbol *sym, struct cfdata *data); +static void sym_nonbool_at_most_1(struct symbol *sym, struct cfdata *data); +static void sym_add_nonbool_values_from_default_range(struct symbol *sym, + struct cfdata *data); +static void sym_add_range_constraints(struct symbol *sym, struct cfdata *data); +static void sym_add_nonbool_prompt_constraint(struct symbol *sym, + struct cfdata *data); + +static struct default_map *create_default_map_entry(struct fexpr *val, + struct pexpr *e); +static struct defm_list *calc_default_conditions(struct symbol *sym, struct cfdata *data); +static struct pexpr *get_default_y(struct defm_list *list, struct cfdata *data); +static struct pexpr *get_default_m(struct defm_list *list, struct cfdata *data); +static struct pexpr *get_default_any(struct symbol *sym, struct cfdata *data); +static long sym_get_range_val(struct symbol *sym, int base); + +/* -------------------------------------- */ + +/* + * build the constraints for each symbol + */ +void build_constraints(struct cfdata *data) +{ + printd("Building constraints..."); + + find_nonboolean_known_vals(data); + build_constraints_bool(data); + build_constraints_select(data); + build_constraints_nonbool(data); +} + +/* + * need to go through the constraints once to find all "known values" + * for the non-Boolean symbols (and add them to sym->nb_vals for the given + * symbols). + * expr_calculate_pexpr_both and get_defaults have the side effect of creating + * known values. + */ +static void find_nonboolean_known_vals(struct cfdata *data) +{ + struct symbol *sym; + struct property *p; + + for_all_symbols(sym) { + struct property *prompt; + + if (sym->type == S_UNKNOWN) + continue; + + if (sym_is_boolean(sym)) { + for_all_properties(sym, p, P_SELECT) + pexpr_put(expr_calculate_pexpr_both( + p->visible.expr, data)); + + for_all_properties(sym, p, P_IMPLY) + pexpr_put(expr_calculate_pexpr_both( + p->visible.expr, data)); + } + + if (sym->dir_dep.expr) + pexpr_put(expr_calculate_pexpr_both(sym->dir_dep.expr, + data)); + + prompt = sym_get_prompt(sym); + if (prompt != NULL && prompt->visible.expr) { + pexpr_put(expr_calculate_pexpr_both( + prompt->visible.expr, data)); + defm_list_destruct(calc_default_conditions(sym, data)); + } + + if (sym_is_nonboolean(sym)) { + const char *curr; + + for_all_defaults(sym, p) { + if (p == NULL) + continue; + + sym_create_nonbool_fexpr( + sym, p->expr->left.sym->name, data); + } + for_all_properties(sym, p, P_RANGE) { + if (p == NULL) + continue; + + sym_create_nonbool_fexpr( + sym, p->expr->left.sym->name, data); + sym_create_nonbool_fexpr( + sym, p->expr->right.sym->name, data); + } + curr = sym_get_string_value(sym); + if (strcmp(curr, "") != 0) + sym_create_nonbool_fexpr(sym, (char *)curr, + data); + } + + if (sym->type == S_HEX || sym->type == S_INT) + sym_add_nonbool_values_from_default_range(sym, data); + } +} + +/* + * build constraints for boolean symbols + */ +static void build_constraints_bool(struct cfdata *data) +{ + struct symbol *sym; + + for_all_symbols(sym) { + if (!sym_is_boolean(sym)) + continue; + + /* build tristate constraints */ + if (sym->type == S_TRISTATE) + build_tristate_constraint_clause(sym, data); + + /* build constraints for select statements + * need to treat choice symbols separately + */ + if (!KCR_CMP) { + add_selects(sym, data); + } else { + if (sym->rev_dep.expr && !sym_is_choice(sym) && + !sym_is_choice_value(sym)) + add_selects_kcr(sym, data); + } + + /* build constraints for dependencies for booleans */ + if (sym->dir_dep.expr && !sym_is_choice(sym) && + !sym_is_choice_value(sym)) { + if (!KCR_CMP) + add_dependencies_bool(sym, data); + else + add_dependencies_bool_kcr(sym, data); + } + + /* build constraints for choice prompts */ + if (sym_is_choice(sym)) + add_choice_prompt_cond(sym, data); + + /* + * build constraints for dependencies (choice symbols and + * options) + */ + if (sym_is_choice(sym) || sym_is_choice_value(sym)) + add_choice_dependencies(sym, data); + + /* build constraints for the choice groups */ + if (sym_is_choice(sym)) + add_choice_constraints(sym, data); + + /* build invisible constraints */ + add_invisible_constraints(sym, data); + } +} + +/* + * build the constraints for select-variables + * skip non-Booleans, choice symbols/options och symbols without rev_dir + */ +static void build_constraints_select(struct cfdata *data) +{ + struct symbol *sym; + + for_all_symbols(sym) { + struct pexpr *sel_y, *sel_m; + struct pexpr *c1, *c2; + + if (KCR_CMP) + continue; + + if (!sym_is_boolean(sym)) + continue; + + if (sym_is_choice(sym) || sym_is_choice_value(sym)) + continue; + + if (!sym->rev_dep.expr) + continue; + + if (sym->list_sel_y == NULL) + continue; + + sel_y = pexpr_implies(pexpr_alloc_symbol(sym->fexpr_sel_y), + pexpr_alloc_symbol(sym->fexpr_y), data, + PEXPR_ARGX); + sym_add_constraint(sym, sel_y, data); + + c1 = pexpr_implies(pexpr_alloc_symbol(sym->fexpr_sel_y), + sym->list_sel_y, data, PEXPR_ARG1); + sym_add_constraint(sym, c1, data); + + /* only continue for tristates */ + if (sym->type == S_BOOLEAN) + continue; + + sel_m = pexpr_implies(pexpr_alloc_symbol(sym->fexpr_sel_m), + sym_get_fexpr_both(sym, data), data, + PEXPR_ARGX); + sym_add_constraint(sym, sel_m, data); + + c2 = pexpr_implies(pexpr_alloc_symbol(sym->fexpr_sel_m), + sym->list_sel_m, data, PEXPR_ARG1); + sym_add_constraint(sym, c2, data); + PEXPR_PUT(sel_y, sel_m, c1, c2); + } +} + +/* + * build constraints for non-booleans + */ +static void build_constraints_nonbool(struct cfdata *data) +{ + struct symbol *sym; + + for_all_symbols(sym) { + if (!sym_is_nonboolean(sym)) + continue; + + /* the symbol must have a value, if there is a prompt */ + if (sym_has_prompt(sym)) + sym_add_nonbool_prompt_constraint(sym, data); + + /* build the range constraints for int/hex */ + if (sym->type == S_HEX || sym->type == S_INT) + sym_add_range_constraints(sym, data); + + /* build constraints for dependencies for non-booleans */ + if (sym->dir_dep.expr) + add_dependencies_nonbool(sym, data); + + /* build invisible constraints */ + add_invisible_constraints(sym, data); + + /* exactly one of the symbols must be true */ + sym_nonbool_at_least_1(sym, data); + sym_nonbool_at_most_1(sym, data); + } +} + +/* + * enforce tristate constraints + */ +static void build_tristate_constraint_clause(struct symbol *sym, + struct cfdata *data) +{ + struct pexpr *X, *X_m, *modules, *c; + + if (sym->type != S_TRISTATE) + return; + + X = pexpr_alloc_symbol(sym->fexpr_y); + X_m = pexpr_alloc_symbol(sym->fexpr_m); + modules = pexpr_alloc_symbol(modules_sym->fexpr_y); + + /* -X v -X_m */ + c = pexpr_or(pexpr_not_share(X, data), pexpr_not_share(X_m, data), + data, PEXPR_ARGX); + sym_add_constraint(sym, c, data); + + /* X_m -> MODULES */ + if (modules_sym->fexpr_y != NULL) { + struct pexpr *c2 = pexpr_implies_share(X_m, modules, data); + + sym_add_constraint(sym, c2, data); + PEXPR_PUT(c2); + } + PEXPR_PUT(X, X_m, modules, c); +} + +/* + * build the select constraints + * - RDep(X) implies X + */ +static void add_selects_kcr(struct symbol *sym, struct cfdata *data) +{ + struct pexpr *rdep_y = expr_calculate_pexpr_y(sym->rev_dep.expr, data); + struct pexpr *c1 = pexpr_implies( + rdep_y, pexpr_alloc_symbol(sym->fexpr_y), data, PEXPR_ARG2); + + struct pexpr *rdep_both = + expr_calculate_pexpr_both(sym->rev_dep.expr, data); + struct pexpr *c2 = pexpr_implies( + rdep_both, sym_get_fexpr_both(sym, data), data, PEXPR_ARG2); + + sym_add_constraint(sym, c1, data); + sym_add_constraint(sym, c2, data); + PEXPR_PUT(rdep_y, c1, rdep_both, c2); +} + +/* + * build the select constraints simplified + * - RDep(X) implies X + */ +static void add_selects(struct symbol *sym, struct cfdata *data) +{ + struct property *p; + + if (!sym_is_boolean(sym)) + return; + + for_all_properties(sym, p, P_SELECT) { + struct symbol *selected = p->expr->left.sym; + struct pexpr *cond_y, *cond_both; + + if (selected->type == S_UNKNOWN) + continue; + + if (!selected->rev_dep.expr) + continue; + + if (p->visible.expr) { + cond_y = expr_calculate_pexpr_y(p->visible.expr, data); + cond_both = expr_calculate_pexpr_both(p->visible.expr, + data); + } else { + cond_y = + pexpr_alloc_symbol(data->constants->const_true); + cond_both = + pexpr_alloc_symbol(data->constants->const_true); + } + + if (selected->type == S_BOOLEAN) { + /* imply that symbol is selected to y */ + struct pexpr *e1 = pexpr_and( + cond_both, sym_get_fexpr_both(sym, data), data, + PEXPR_ARG2); + struct pexpr *c1 = pexpr_implies( + e1, pexpr_alloc_symbol(selected->fexpr_sel_y), + data, PEXPR_ARG2); + + sym_add_constraint(selected, c1, data); + + if (selected->list_sel_y == NULL) + selected->list_sel_y = pexpr_get(e1); + else + selected->list_sel_y = + pexpr_or(selected->list_sel_y, e1, data, + PEXPR_ARG1); + PEXPR_PUT(e1, c1); + } + + if (selected->type == S_TRISTATE) { + struct pexpr *e2, *e3, *c2, *c3; + + /* imply that symbol is selected to y */ + e2 = pexpr_and(cond_y, pexpr_alloc_symbol(sym->fexpr_y), + data, PEXPR_ARG2); + c2 = pexpr_implies( + e2, pexpr_alloc_symbol(selected->fexpr_sel_y), + data, PEXPR_ARG2); + sym_add_constraint(selected, c2, data); + + if (selected->list_sel_y == NULL) + selected->list_sel_y = pexpr_get(e2); + else + selected->list_sel_y = + pexpr_or(selected->list_sel_y, e2, + data, PEXPR_ARG1); + + /* imply that symbol is selected to m */ + e3 = pexpr_and(cond_both, sym_get_fexpr_both(sym, data), + data, PEXPR_ARG2); + c3 = pexpr_implies( + e3, pexpr_alloc_symbol(selected->fexpr_sel_m), + data, PEXPR_ARG2); + sym_add_constraint(selected, c3, data); + + if (selected->list_sel_m == NULL) + selected->list_sel_m = pexpr_get(e3); + else + selected->list_sel_m = + pexpr_or(selected->list_sel_m, e3, + data, PEXPR_ARG1); + PEXPR_PUT(e2, c2, e3, c3); + } + PEXPR_PUT(cond_y, cond_both); + } +} + +/* + * build the dependency constraints for booleans + * - X implies Dep(X) or RDep(X) + */ +static void add_dependencies_bool(struct symbol *sym, struct cfdata *data) +{ + struct pexpr *dep_both; + struct pexpr *visible_m; + struct pexpr *visible_y; + struct pexpr *visible_both; + struct property *prompt; + struct pexpr *has_prompt; + struct pexpr *sel_y; + + if (!sym_is_boolean(sym) || !sym->dir_dep.expr) + return; + + prompt = sym_get_prompt(sym); + if (!prompt) { + visible_m = pexpr_alloc_symbol(data->constants->const_false); + visible_y = pexpr_get(visible_m); + visible_both = pexpr_get(visible_m); + } else if (prompt->expr == NULL) { + visible_m = pexpr_alloc_symbol(data->constants->const_true); + visible_y = pexpr_get(visible_m); + visible_both = pexpr_get(visible_m); + } else { + visible_m = expr_calculate_pexpr_m(prompt->expr, data); + visible_y = expr_calculate_pexpr_y(prompt->expr, data); + visible_both = pexpr_or_share(visible_y, visible_m, data); + } + + dep_both = expr_calculate_pexpr_both(sym->dir_dep.expr, data); + + sel_y = sym->rev_dep.expr ? + pexpr_alloc_symbol(sym->fexpr_sel_y) : + pexpr_alloc_symbol(data->constants->const_false); + has_prompt = pexpr_get(visible_both); + has_prompt = pexpr_and( + has_prompt, + pexpr_not(pexpr_and_share(sel_y, visible_m, data), + data), + data, PEXPR_ARGX); + + if (sym->type == S_TRISTATE) { + struct pexpr *c1; + struct pexpr *c2; + struct pexpr *dep_y = + expr_calculate_pexpr_y(sym->dir_dep.expr, data); + struct pexpr *sel_both = sym_get_fexpr_sel_both(sym, data); + struct pexpr *cond_y1; + struct pexpr *cond_y2; + struct pexpr *cond_y; + struct pexpr *cond_m1; + struct pexpr *cond_m2; + struct pexpr *cond_m; + + cond_y1 = pexpr_implies(pexpr_not_share(has_prompt, data), + pexpr_or_share(dep_y, sel_y, data), + data, PEXPR_ARGX); + cond_y2 = pexpr_implies_share(has_prompt, visible_y, data); + cond_y = pexpr_and_share(cond_y1, cond_y2, data); + cond_m1 = + pexpr_implies(pexpr_not_share(has_prompt, data), + pexpr_or_share(dep_both, sel_both, data), + data, PEXPR_ARGX); + cond_m2 = pexpr_implies(has_prompt, + pexpr_not_share(sel_y, data), data, + PEXPR_ARG2); + cond_m = pexpr_and_share(cond_m1, cond_m2, data); + c1 = pexpr_implies(pexpr_alloc_symbol(sym->fexpr_y), cond_y, + data, PEXPR_ARG1); + c2 = pexpr_implies(pexpr_alloc_symbol(sym->fexpr_m), cond_m, + data, PEXPR_ARG1); + + sym_add_constraint(sym, c1, data); + sym_add_constraint(sym, c2, data); + PEXPR_PUT(c1, c2, dep_y, sel_both, cond_y1, + cond_y2, cond_y, cond_m1, cond_m2, cond_m); + } else if (sym->type == S_BOOLEAN) { + struct pexpr *cond1; + struct pexpr *cond2; + struct pexpr *c; + + cond1 = pexpr_implies(pexpr_not_share(has_prompt, data), + pexpr_or(dep_both, + pexpr_alloc_symbol(sym->fexpr_m), + data, PEXPR_ARG2), + data, PEXPR_ARGX); + cond2 = pexpr_implies_share(has_prompt, visible_y, data); + c = pexpr_implies(pexpr_alloc_symbol(sym->fexpr_y), + pexpr_and_share(cond1, cond2, data), data, + PEXPR_ARGX); + + sym_add_constraint(sym, c, data); + PEXPR_PUT(c, cond1, cond2); + } + PEXPR_PUT(dep_both, has_prompt, sel_y, visible_y, visible_m, + visible_both); +} + +/* + * build the dependency constraints for booleans (KCR) + * - X implies Dep(X) or RDep(X) + */ +static void add_dependencies_bool_kcr(struct symbol *sym, struct cfdata *data) +{ + struct pexpr *dep_both, *sel_both; + + if (!sym_is_boolean(sym) || !sym->dir_dep.expr) + return; + + dep_both = expr_calculate_pexpr_both(sym->dir_dep.expr, data); + + sel_both = sym->rev_dep.expr ? + expr_calculate_pexpr_both(sym->rev_dep.expr, data) : + pexpr_alloc_symbol(data->constants->const_false); + + if (sym->type == S_TRISTATE) { + struct pexpr *c1; + struct pexpr *c2; + { + struct pexpr *dep_y = + expr_calculate_pexpr_y(sym->dir_dep.expr, data); + struct pexpr *sel_y = + sym->rev_dep.expr ? + expr_calculate_pexpr_y( + sym->rev_dep.expr, data) : + pexpr_alloc_symbol( + data->constants->const_false); + c1 = pexpr_implies(pexpr_alloc_symbol(sym->fexpr_y), + pexpr_or(dep_y, sel_y, + data, PEXPR_ARGX), + data, PEXPR_ARGX); + } + c2 = pexpr_implies(pexpr_alloc_symbol(sym->fexpr_m), + pexpr_or_share(dep_both, sel_both, + data), + data, PEXPR_ARGX); + + sym_add_constraint(sym, c1, data); + sym_add_constraint(sym, c2, data); + PEXPR_PUT(c1, c2); + } else if (sym->type == S_BOOLEAN) { + struct pexpr *c = pexpr_implies( + pexpr_alloc_symbol(sym->fexpr_y), + pexpr_or_share(dep_both, sel_both, data), data, + PEXPR_ARGX); + + sym_add_constraint(sym, c, data); + PEXPR_PUT(c); + } + + PEXPR_PUT(dep_both, sel_both); +} + +/* + * build the dependency constraints for non-booleans + * + * sym is not 'n' implies `sym->dir_dep` + */ +static void add_dependencies_nonbool(struct symbol *sym, struct cfdata *data) +{ + struct pexpr *dep_both; + struct pexpr *nb_vals; // "sym is set to some value" / "sym is not 'n'" + struct fexpr_node *node; + struct pexpr *c; + bool first = true; + + if (!sym_is_nonboolean(sym) || !sym->dir_dep.expr || sym->rev_dep.expr) + return; + + dep_both = expr_calculate_pexpr_both(sym->dir_dep.expr, data); + + nb_vals = pexpr_alloc_symbol(data->constants->const_false); + /* can skip the first non-boolean value, since this is 'n' */ + CF_LIST_FOR_EACH(node, sym->nb_vals, fexpr) { + if (first) { + first = false; + continue; + } + + nb_vals = pexpr_or(nb_vals, pexpr_alloc_symbol(node->elem), + data, PEXPR_ARGX); + } + + c = pexpr_implies(nb_vals, dep_both, data, PEXPR_ARGX); + sym_add_constraint(sym, c, data); + pexpr_put(c); +} + +/* + * build the constraints for the choice prompt + */ +static void add_choice_prompt_cond(struct symbol *sym, struct cfdata *data) +{ + struct property *prompt; + struct pexpr *promptCondition; + struct pexpr *fe_both; + struct pexpr *pr_cond; + struct pexpr *req_cond; + + if (!sym_is_boolean(sym)) + return; + + prompt = sym_get_prompt(sym); + if (prompt == NULL) + return; + + promptCondition = + prompt->visible.expr ? + expr_calculate_pexpr_both(prompt->visible.expr, data) : + pexpr_alloc_symbol(data->constants->const_true); + fe_both = sym_get_fexpr_both(sym, data); + req_cond = pexpr_implies_share(promptCondition, fe_both, data); + sym_add_constraint(sym, req_cond, data); + pr_cond = pexpr_implies_share(fe_both, promptCondition, data); + sym_add_constraint(sym, pr_cond, data); + PEXPR_PUT(promptCondition, fe_both, req_cond, pr_cond); +} + +/* + * build constraints for dependencies (choice symbols and options) + */ +static void add_choice_dependencies(struct symbol *sym, struct cfdata *data) +{ + struct property *prompt; + struct expr *to_parse; + struct pexpr *dep_both; + + if (!sym_is_choice(sym) || !sym_is_choice_value(sym)) + return; + + prompt = sym_get_prompt(sym); + if (prompt == NULL) + return; + + if (sym_is_choice(sym)) { + if (!prompt->visible.expr) + return; + to_parse = prompt->visible.expr; + } else { + if (!sym->dir_dep.expr) + return; + to_parse = sym->dir_dep.expr; + } + + dep_both = expr_calculate_pexpr_both(to_parse, data); + + if (sym->type == S_TRISTATE) { + struct pexpr *dep_y = expr_calculate_pexpr_y(to_parse, data); + struct pexpr *c1 = + pexpr_implies(pexpr_alloc_symbol(sym->fexpr_y), dep_y, + data, PEXPR_ARG1); + struct pexpr *c2 = + pexpr_implies(pexpr_alloc_symbol(sym->fexpr_m), + dep_both, data, PEXPR_ARG1); + + sym_add_constraint_unique(sym, c1, data); + sym_add_constraint_unique(sym, c2, data); + PEXPR_PUT(dep_y, c1, c2); + } else if (sym->type == S_BOOLEAN) { + struct pexpr *c = + pexpr_implies(pexpr_alloc_symbol(sym->fexpr_y), + dep_both, data, PEXPR_ARG1); + + sym_add_constraint_unique(sym, c, data); + pexpr_put(c); + } + pexpr_put(dep_both); +} + +/* + * build constraints for the choice groups + */ +static void add_choice_constraints(struct symbol *sym, struct cfdata *data) +{ + struct property *prompt; + struct symbol *choice, *choice2; + struct sym_node *node; + struct sym_list *items, *promptItems; + struct pexpr *c1; + struct menu *menu_ptr, *choiceval_menu; + + if (!sym_is_boolean(sym)) + return; + + prompt = sym_get_prompt(sym); + if (prompt == NULL) + return; + + /* create list of all choice options */ + items = CF_LIST_INIT(sym); + /* create list of choice options with a prompt */ + promptItems = CF_LIST_INIT(sym); + + for_all_choices(sym, choiceval_menu, menu_ptr) { + choice = choiceval_menu->sym; + + CF_PUSH_BACK(items, choice, sym); + if (sym_get_prompt(choice) != NULL) + CF_PUSH_BACK(promptItems, choice, sym); + } + + /* if the choice is set to yes, at least one child must be set to yes */ + c1 = NULL; + CF_LIST_FOR_EACH(node, promptItems, sym) { + choice = node->elem; + c1 = list_is_head(node->node.prev, &promptItems->list) ? + pexpr_alloc_symbol(choice->fexpr_y) : + pexpr_or(c1, pexpr_alloc_symbol(choice->fexpr_y), + data, PEXPR_ARGX); + } + if (c1 != NULL) { + struct pexpr *c2 = pexpr_implies( + pexpr_alloc_symbol(sym->fexpr_y), c1, data, PEXPR_ARG1); + + sym_add_constraint(sym, c2, data); + PEXPR_PUT(c1, c2); + } + + /* + * every choice option (even those without a prompt) implies the choice + */ + CF_LIST_FOR_EACH(node, items, sym) { + choice = node->elem; + c1 = pexpr_implies(sym_get_fexpr_both(choice, data), + sym_get_fexpr_both(sym, data), data, + PEXPR_ARGX); + sym_add_constraint(sym, c1, data); + pexpr_put(c1); + } + + /* choice options can only select mod, if the entire choice is mod */ + if (sym->type == S_TRISTATE) { + CF_LIST_FOR_EACH(node, items, sym) { + choice = node->elem; + if (choice->type == S_TRISTATE) { + c1 = pexpr_implies( + pexpr_alloc_symbol(choice->fexpr_m), + pexpr_alloc_symbol(sym->fexpr_m), data, + PEXPR_ARGX); + sym_add_constraint(sym, c1, data); + pexpr_put(c1); + } + } + } + + /* tristate options cannot be m, if the choice symbol is boolean */ + if (sym->type == S_BOOLEAN) { + CF_LIST_FOR_EACH(node, items, sym) { + choice = node->elem; + if (choice->type == S_TRISTATE) { + struct pexpr *e = pexpr_not( + pexpr_alloc_symbol(choice->fexpr_m), + data); + sym_add_constraint(sym, e, data); + pexpr_put(e); + } + } + } + + /* all choice options are mutually exclusive for yes */ + CF_LIST_FOR_EACH(node, promptItems, sym) { + struct sym_node *node2; + + choice = node->elem; + list_for_each_entry_from(node2, + &list_next_entry(node, node)->node, + &promptItems->list, node) { + choice2 = node2->elem; + c1 = pexpr_or( + pexpr_not(pexpr_alloc_symbol(choice->fexpr_y), + data), + pexpr_not(pexpr_alloc_symbol(choice2->fexpr_y), + data), + data, PEXPR_ARGX); + sym_add_constraint(sym, c1, data); + pexpr_put(c1); + } + } + + /* if one choice option with a prompt is set to yes, + * then no other option may be set to mod + */ + if (sym->type == S_TRISTATE) { + CF_LIST_FOR_EACH(node, promptItems, sym) { + struct sym_list *tmp; + struct sym_node *node2; + + choice = node->elem; + + tmp = CF_LIST_INIT(sym); + list_for_each_entry_from( + node2, &list_next_entry(node, node)->node, + &promptItems->list, node) { + choice2 = node2->elem; + if (choice2->type == S_TRISTATE) + CF_PUSH_BACK(tmp, choice2, sym); + } + if (list_empty(&tmp->list)) + continue; + + CF_LIST_FOR_EACH(node2, tmp, sym) { + struct pexpr *choice2_mod = + pexpr_alloc_symbol(choice2->fexpr_m); + + choice2 = node2->elem; + if (list_is_first(&node2->node, &tmp->list)) + c1 = pexpr_not_share(choice2_mod, data); + else + c1 = pexpr_and( + c1, + pexpr_not_share(choice2_mod, + data), + data, PEXPR_ARGX); + + PEXPR_PUT(choice2_mod); + } + c1 = pexpr_implies(pexpr_alloc_symbol(choice->fexpr_y), + c1, data, PEXPR_ARGX); + sym_add_constraint(sym, c1, data); + pexpr_put(c1); + } + } + CF_LIST_FREE(promptItems, sym); + CF_LIST_FREE(items, sym); +} + +/* + * build the constraints for invisible options such as defaults + */ +static void add_invisible_constraints(struct symbol *sym, struct cfdata *data) +{ + struct property *prompt = sym_get_prompt(sym); + struct pexpr *promptCondition_both, *promptCondition_yes, *noPromptCond; + struct pexpr *npc; + struct defm_list *defaults; + struct pexpr *default_y, *default_m, *default_both; + + /* no constraints for the prompt, nothing to do here */ + if (prompt != NULL && !prompt->visible.expr) + return; + + if (prompt == NULL) { + promptCondition_both = + pexpr_alloc_symbol(data->constants->const_false); + promptCondition_yes = + pexpr_alloc_symbol(data->constants->const_false); + noPromptCond = pexpr_alloc_symbol(data->constants->const_true); + } else { + struct property *p; + + promptCondition_both = + pexpr_alloc_symbol(data->constants->const_false); + promptCondition_yes = + pexpr_alloc_symbol(data->constants->const_false); + + /* some symbols have multiple prompts */ + for_all_prompts(sym, p) { + promptCondition_both = + pexpr_or(promptCondition_both, + expr_calculate_pexpr_both( + p->visible.expr, data), + data, PEXPR_ARGX); + promptCondition_yes = pexpr_or( + promptCondition_yes, + expr_calculate_pexpr_y(p->visible.expr, data), + data, PEXPR_ARGX); + } + noPromptCond = pexpr_not_share(promptCondition_both, data); + } + + if (NPC_OPTIMISATION) { + struct fexpr *npc_fe = + fexpr_create(data->sat_variable_nr++, FE_NPC, ""); + + if (sym_is_choice(sym)) + str_append(&npc_fe->name, "Choice_"); + + str_append(&npc_fe->name, sym_get_name(sym)); + str_append(&npc_fe->name, "_NPC"); + sym->noPromptCond = npc_fe; + fexpr_add_to_satmap(npc_fe, data); + + npc = pexpr_alloc_symbol(npc_fe); + + if (!sym_is_choice_value(sym) && !sym_is_choice(sym)) { + struct pexpr *c = + pexpr_implies_share(noPromptCond, npc, data); + sym_add_constraint(sym, c, data); + pexpr_put(c); + } + } else { + npc = pexpr_get(noPromptCond); + } + + defaults = calc_default_conditions(sym, data); + default_y = get_default_y(defaults, data); + default_m = get_default_m(defaults, data); + default_both = pexpr_or_share(default_y, default_m, data); + + /* + * tristate elements are only selectable as yes, if they are visible as + * yes + */ + if (sym->type == S_TRISTATE) { + struct pexpr *e1 = pexpr_implies( + promptCondition_both, + pexpr_implies(pexpr_alloc_symbol(sym->fexpr_y), + promptCondition_yes, data, + PEXPR_ARG1), + data, PEXPR_ARG2); + + sym_add_constraint(sym, e1, data); + pexpr_put(e1); + } + + /* if invisible and off by default, then a symbol can only be + * deactivated by its reverse dependencies + */ + if (sym->type == S_TRISTATE) { + struct pexpr *sel_y, *sel_m, *sel_both; + struct pexpr *c1, *c2, *c3; + struct pexpr *d1, *d2, *d3; + struct pexpr *e1, *e2, *e3; + + if (sym->fexpr_sel_y != NULL) { + sel_y = pexpr_implies( + pexpr_alloc_symbol(sym->fexpr_y), + pexpr_alloc_symbol(sym->fexpr_sel_y), data, + PEXPR_ARGX); + sel_m = pexpr_implies( + pexpr_alloc_symbol(sym->fexpr_m), + pexpr_alloc_symbol(sym->fexpr_sel_m), data, + PEXPR_ARGX); + sel_both = pexpr_implies( + pexpr_alloc_symbol(sym->fexpr_y), + pexpr_or(pexpr_alloc_symbol(sym->fexpr_sel_m), + pexpr_alloc_symbol(sym->fexpr_sel_y), + data, PEXPR_ARGX), + data, PEXPR_ARGX); + } else { + sel_y = pexpr_not(pexpr_alloc_symbol(sym->fexpr_y), + data); + sel_m = pexpr_not(pexpr_alloc_symbol(sym->fexpr_m), + data); + sel_both = pexpr_get(sel_y); + } + + c1 = pexpr_implies(pexpr_not_share(default_y, data), sel_y, + data, PEXPR_ARG1); + c2 = pexpr_implies(pexpr_alloc_symbol(modules_sym->fexpr_y), c1, + data, PEXPR_ARG1); + c3 = pexpr_implies_share(npc, c2, data); + sym_add_constraint(sym, c3, data); + + d1 = pexpr_implies(pexpr_not_share(default_m, data), sel_m, + data, PEXPR_ARG1); + d2 = pexpr_implies(pexpr_alloc_symbol(modules_sym->fexpr_y), d1, + data, PEXPR_ARG1); + d3 = pexpr_implies_share(npc, d2, data); + sym_add_constraint(sym, d3, data); + + e1 = pexpr_implies(pexpr_not_share(default_both, data), + sel_both, data, PEXPR_ARG1); + e2 = pexpr_implies( + pexpr_not(pexpr_alloc_symbol(modules_sym->fexpr_y), + data), + e1, data, PEXPR_ARG1); + e3 = pexpr_implies_share(npc, e2, data); + sym_add_constraint(sym, e3, data); + PEXPR_PUT(sel_y, sel_m, sel_both, c1, c2, c3, d1, d2, d3, e1, + e2, e3); + } else if (sym->type == S_BOOLEAN) { + struct pexpr *sel_y; + struct pexpr *e1, *e2; + + if (sym->fexpr_sel_y != NULL) + sel_y = pexpr_implies( + pexpr_alloc_symbol(sym->fexpr_y), + pexpr_alloc_symbol(sym->fexpr_sel_y), data, + PEXPR_ARGX); + else + sel_y = pexpr_not(pexpr_alloc_symbol(sym->fexpr_y), + data); + + e1 = pexpr_implies(pexpr_not_share(default_both, data), + sel_y, data, PEXPR_ARG1); + e2 = pexpr_implies_share(npc, e1, data); + + sym_add_constraint_unique(sym, e2, data); + PEXPR_PUT(sel_y, e1, e2); + } else { + /* if non-boolean is invisible and no default's condition is + * fulfilled, then the symbol is not set + */ + struct pexpr *default_any = get_default_any(sym, data); + struct pexpr *e1 = + pexpr_alloc_symbol(data->constants->const_true); + struct pexpr *e2, *e3; + struct fexpr_node *node; + bool first = true; + + /* e1 = "sym is not set" */ + CF_LIST_FOR_EACH(node, sym->nb_vals, fexpr) { + if (first) { + first = false; + continue; + } + e1 = pexpr_and(e1, + pexpr_not(pexpr_alloc_symbol(node->elem), + data), + data, PEXPR_ARGX); + } + + e2 = pexpr_implies(pexpr_not_share(default_any, data), e1, + data, PEXPR_ARG1); + e3 = pexpr_implies_share(npc, e2, data); + + sym_add_constraint(sym, e3, data); + PEXPR_PUT(default_any, e1, e2, e3); + } + + /* if invisible and on by default, then a symbol can only be deactivated + * by its dependencies + */ + if (list_empty(&defaults->list)) { + // nothing to do + } else if (sym->type == S_TRISTATE) { + struct pexpr *e1; + struct pexpr *e2; + + e1 = pexpr_implies( + npc, + pexpr_implies(default_y, + pexpr_alloc_symbol(sym->fexpr_y), data, + PEXPR_ARG2), + data, PEXPR_ARG2); + sym_add_constraint(sym, e1, data); + + e2 = pexpr_implies( + npc, + pexpr_implies(default_m, + sym_get_fexpr_both(sym, data), + data, PEXPR_ARG2), + data, PEXPR_ARG2); + sym_add_constraint(sym, e2, data); + PEXPR_PUT(e1, e2); + } else if (sym->type == S_BOOLEAN) { + struct pexpr *c; + struct pexpr *c2; + + c = pexpr_implies(default_both, + pexpr_alloc_symbol(sym->fexpr_y), data, + PEXPR_ARG2); + + // TODO tristate choice hack + + c2 = pexpr_implies_share(npc, c, data); + sym_add_constraint(sym, c2, data); + PEXPR_PUT(c, c2); + } else { + /* if non-boolean invisible, then it assumes the correct + * default (if any). + */ + struct defm_node *node; + struct pexpr *cond, *c; + struct fexpr *f; + + CF_LIST_FOR_EACH(node, defaults, defm) { + f = node->elem->val; + cond = node->elem->e; + c = pexpr_implies(npc, + pexpr_implies(cond, + pexpr_alloc_symbol(f), + data, PEXPR_ARG2), + data, PEXPR_ARG2); + sym_add_constraint(sym, c, data); + pexpr_put(c); + } + } + + PEXPR_PUT(promptCondition_yes, promptCondition_both, noPromptCond, npc, + default_y, default_m, default_both); + defm_list_destruct(defaults); +} + +/* + * add the known values from the default and range properties + */ +static void sym_add_nonbool_values_from_default_range(struct symbol *sym, + struct cfdata *data) +{ + struct property *p; + + for_all_defaults(sym, p) { + if (p == NULL) + continue; + + /* add the value to known values, if it doesn't exist yet */ + sym_create_nonbool_fexpr(sym, p->expr->left.sym->name, data); + } + + for_all_properties(sym, p, P_RANGE) { + if (p == NULL) + continue; + + /* add the values to known values, if they don't exist yet */ + sym_create_nonbool_fexpr(sym, p->expr->left.sym->name, data); + sym_create_nonbool_fexpr(sym, p->expr->right.sym->name, data); + } +} + +/* + * build the range constraints for int/hex: + * For each range and each value in `sym->nb_vals` that's not in the range: + * If the range's condition is fulfilled, then sym can't have this value. + */ +static void sym_add_range_constraints(struct symbol *sym, struct cfdata *data) +{ + struct property *prop; + struct pexpr *prevs; + struct pexpr *propCond; + struct pexpr_list *prevCond; // list of all conditions of the ranges + // from the previous iterations + + prevCond = CF_LIST_INIT(pexpr); + + for_all_properties(sym, prop, P_RANGE) { + int base; + long long range_min, range_max, tmp; + struct fexpr_node *node; + bool first; + + if (prop == NULL) + continue; + + prevs = pexpr_alloc_symbol(data->constants->const_true); + propCond = prop_get_condition(prop, data); + + // construct prevs as "none of the previous ranges' conditions + // were fulfilled but this range's condition is" + if (list_empty(&prevCond->list)) { + pexpr_put(prevs); + prevs = pexpr_get(propCond); +; + } else { + struct pexpr_node *node; + + CF_LIST_FOR_EACH(node, prevCond, pexpr) + prevs = pexpr_and(pexpr_not_share(node->elem, + data), + prevs, data, PEXPR_ARGX); + + prevs = pexpr_and(propCond, prevs, data, + PEXPR_ARG2); + } + CF_PUSH_BACK(prevCond, pexpr_get(propCond), pexpr); + + switch (sym->type) { + case S_INT: + base = 10; + break; + case S_HEX: + base = 16; + break; + default: + return; + } + + range_min = sym_get_range_val(prop->expr->left.sym, base); + range_max = sym_get_range_val(prop->expr->right.sym, base); + + first = true; + /* can skip the first non-boolean value, since this is 'n' */ + CF_LIST_FOR_EACH(node, sym->nb_vals, fexpr) { + struct pexpr *not_nb_val; + struct pexpr *c; + + if (first) { + first = false; + continue; + } + + tmp = strtoll(str_get(&node->elem->nb_val), NULL, base); + + /* known value is in range, nothing to do here */ + if (tmp >= range_min && tmp <= range_max) + continue; + + not_nb_val = + pexpr_not(pexpr_alloc_symbol(node->elem), data); + c = pexpr_implies_share(prevs, not_nb_val, data); + sym_add_constraint(sym, c, data); + PEXPR_PUT(not_nb_val, c); + } + PEXPR_PUT(prevs, propCond); + } + + pexpr_list_free_put(prevCond); + +} + +/* + * at least 1 of the known values for a non-boolean symbol must be true + */ +static void sym_nonbool_at_least_1(struct symbol *sym, struct cfdata *data) +{ + struct pexpr *e; + struct fexpr_node *node; + + if (!sym_is_nonboolean(sym)) + return; + + e = pexpr_alloc_symbol(data->constants->const_false); + CF_LIST_FOR_EACH(node, sym->nb_vals, fexpr) + e = pexpr_or(e, pexpr_alloc_symbol(node->elem), data, + PEXPR_ARGX); + + sym_add_constraint(sym, e, data); + pexpr_put(e); +} + +/* + * at most 1 of the known values for a non-boolean symbol can be true + */ +static void sym_nonbool_at_most_1(struct symbol *sym, struct cfdata *data) +{ + struct fexpr_node *node1; + + if (!sym_is_nonboolean(sym)) + return; + + /* iterate over all subsets of sym->nb_vals of size 2 */ + CF_LIST_FOR_EACH(node1, sym->nb_vals, fexpr) { + struct pexpr *e1 = pexpr_alloc_symbol(node1->elem); + struct fexpr_node *node2; + + list_for_each_entry_reverse(node2, &sym->nb_vals->list, node) { + struct pexpr *e2, *e; + + if (node2 == node1) + break; + e2 = pexpr_alloc_symbol(node2->elem); + e = pexpr_or(pexpr_not_share(e1, data), + pexpr_not_share(e2, data), + data, PEXPR_ARGX); + + sym_add_constraint(sym, e, data); + PEXPR_PUT(e, e2); + } + pexpr_put(e1); + } +} + +/* + * a visible prompt for a non-boolean implies a value for the symbol + */ +static void sym_add_nonbool_prompt_constraint(struct symbol *sym, + struct cfdata *data) +{ + struct property *prompt; + struct pexpr *promptCondition; + struct pexpr *n; + struct pexpr *c = NULL; + + prompt = sym_get_prompt(sym); + if (prompt == NULL) + return; + + promptCondition = prop_get_condition(prompt, data); + n = pexpr_alloc_symbol(sym_get_nonbool_fexpr(sym, "n")); + + if (n->type != PE_SYMBOL || n->left.fexpr == NULL) + goto cleanup; + + c = pexpr_implies(promptCondition, pexpr_not_share(n, data), data, + PEXPR_ARG2); + + sym_add_constraint(sym, c, data); + +cleanup: + PEXPR_PUT(n, promptCondition, c); +} + +static struct default_map *create_default_map_entry(struct fexpr *val, + struct pexpr *e) +{ + struct default_map *map = malloc(sizeof(struct default_map)); + + pexpr_get(e); + map->val = val; + map->e = e; + + return map; +} + +/** + * findDefaultEntry() + * @val: Value that the entry must have + * @defaults: List of defaults to search in + * @constants: To get ``constants->const_false`` from + * + * Finds an entry in @defaults whose &default_map.val attribute is the same + * pointer as the @val argument. + * + * Return: The condition &default_map.e of the found entry, or + * ``pexf(constants->const_false)`` if none was found. To be pexpr_put() by the + * caller. + */ +static struct pexpr *findDefaultEntry(struct fexpr *val, + struct defm_list *defaults, + struct constants *constants) +{ + struct defm_node *node; + + CF_LIST_FOR_EACH(node, defaults, defm) { + if (val == node->elem->val) { + pexpr_get(node->elem->e); + return node->elem->e; + } + } + + return pexpr_alloc_symbol(constants->const_false); +} + +/* + * accumulated during execution of add_defaults(), a disjunction of the + * conditions for all default props of a symbol + */ +static struct pexpr *covered; + +static bool is_tri_as_num(struct symbol *sym) +{ + if (!sym->name) + return false; + + return !strcmp(sym->name, "0") + || !strcmp(sym->name, "1") + || !strcmp(sym->name, "2"); +} + +/** + * add_to_default_map() - Add to or update an entry in a default list + * @entry: Will be consumed by this function, i.e. the caller should and need + * only access @entry via @defaults. + */ +static void add_to_default_map(struct defm_list *defaults, + struct default_map *entry, struct symbol *sym) +{ + /* as this is a map, the entry must be replaced if it already exists */ + if (sym_is_boolean(sym)) { + struct default_map *map; + struct defm_node *node; + + CF_LIST_FOR_EACH(node, defaults, defm) { + map = node->elem; + if (map->val->sym == entry->val->sym) { + pexpr_put(map->e); + map->e = entry->e; + free(entry); + return; + } + } + CF_PUSH_BACK(defaults, entry, defm); + } else { + struct default_map *map; + struct defm_node *node; + + CF_LIST_FOR_EACH(node, defaults, defm) { + map = node->elem; + if (map->val->satval == entry->val->satval) { + pexpr_put(map->e); + map->e = entry->e; + free(entry); + return; + } + } + CF_PUSH_BACK(defaults, entry, defm); + } +} + +/** + * updateDefaultList() - Update a default list with a new value-condition pair + * @val: The value whose condition will be updated + * @newCond: The condition of the default prop. Does not include the condition + * that the earlier default's conditions are not fulfilled. + * @result: the default list + * @sym: the symbol that the defaults belong to + * + * Update the condition that @val will be used for @sym by considering the next + * default property, whose condition is given by @newCond. + */ +static void updateDefaultList(struct fexpr *val, struct pexpr *newCond, + struct defm_list *result, struct symbol *sym, + struct cfdata *data) +{ + // The current condition of @val deduced from the previous default props + struct pexpr *prevCond = findDefaultEntry(val, result, data->constants); + // New combined condition for @val + struct pexpr *condUseVal = + pexpr_or(prevCond, + pexpr_and(newCond, pexpr_not_share(covered, data), + data, PEXPR_ARG2), + data, PEXPR_ARG2); + add_to_default_map(result, create_default_map_entry(val, condUseVal), + sym); + covered = pexpr_or(covered, newCond, data, PEXPR_ARG1); + PEXPR_PUT(prevCond, condUseVal); +} + +/** + * add_defaults() - Generate list of default values and their conditions + * @defaults: List of the default properties + * @ctx: Additional condition that needs to be fulfilled for any default. May be + * NULL. + * @result: List that will be filled + * @sym: Symbol that the defaults belong to + * + * Creates a map from values that @sym can assume to the conditions under which + * they will be assumed. Without @ctx, this will only consider the conditions + * directly associated with the defaults, e.g. sym->dir_dep would not be + * considered. + * + * As a side effect, the &symbol->nb_vals of @sym will be added for + * all default values (as well as the @symbol->nb_vals of other symbols @sym has + * as default (recursively)). + */ +static void add_defaults(struct prop_list *defaults, struct expr *ctx, + struct defm_list *result, struct symbol *sym, + struct cfdata *data) +{ + struct prop_node *node; + struct property *p; + struct expr *expr; + + CF_LIST_FOR_EACH(node, defaults, prop) { + p = node->elem; + /* calculate expr as whether the default's condition (and the + * one inherited from ctx) is fulfilled + */ + if (p->visible.expr) { + if (ctx == NULL) + expr = p->visible.expr; + else + expr = expr_alloc_and(p->visible.expr, ctx); + } else { + if (ctx == NULL) + expr = expr_alloc_symbol(&symbol_yes); + else + expr = expr_alloc_and( + expr_alloc_symbol(&symbol_yes), ctx); + } + + /* if tristate and def.value = y */ + if (p->expr->type == E_SYMBOL && sym->type == S_TRISTATE && + p->expr->left.sym == &symbol_yes) { + struct pexpr *expr_y = + expr_calculate_pexpr_y(expr, data); + struct pexpr *expr_m = + expr_calculate_pexpr_m(expr, data); + + updateDefaultList(data->constants->symbol_yes_fexpr, + expr_y, result, sym, data); + updateDefaultList(data->constants->symbol_mod_fexpr, + expr_m, result, sym, data); + PEXPR_PUT(expr_y, expr_m); + } + /* if def.value = n/m/y */ + else if (p->expr->type == E_SYMBOL && + sym_is_tristate_constant(p->expr->left.sym) && + sym_is_boolean(sym)) { + struct fexpr *s; + struct pexpr *expr_both = + expr_calculate_pexpr_both(expr, data); + + if (p->expr->left.sym == &symbol_yes) + s = data->constants->symbol_yes_fexpr; + else if (p->expr->left.sym == &symbol_mod) + s = data->constants->symbol_mod_fexpr; + else + s = data->constants->symbol_no_fexpr; + + updateDefaultList(s, expr_both, result, sym, data); + pexpr_put(expr_both); + } + /* if def.value = n/m/y, but written as 0/1/2 for a boolean */ + else if (sym_is_boolean(sym) && p->expr->type == E_SYMBOL && + p->expr->left.sym->type == S_UNKNOWN && + is_tri_as_num(p->expr->left.sym)) { + struct fexpr *s; + struct pexpr *expr_both = + expr_calculate_pexpr_both(expr, data); + + if (!strcmp(p->expr->left.sym->name, "0")) + s = data->constants->symbol_no_fexpr; + else if (!strcmp(p->expr->left.sym->name, "1")) + s = data->constants->symbol_mod_fexpr; + else + s = data->constants->symbol_yes_fexpr; + + updateDefaultList(s, expr_both, result, sym, data); + pexpr_put(expr_both); + } + /* if def.value = non-boolean constant */ + else if (expr_is_nonbool_constant(p->expr)) { + struct fexpr *s = sym_get_or_create_nonbool_fexpr( + sym, p->expr->left.sym->name, data); + struct pexpr *expr_both = + expr_calculate_pexpr_both(expr, data); + + updateDefaultList(s, expr_both, result, sym, data); + pexpr_put(expr_both); + } + /* any expression which evaluates to n/m/y for a tristate */ + else if (sym->type == S_TRISTATE) { + struct expr *e_tmp = expr_alloc_and(p->expr, expr); + struct pexpr *expr_y = + expr_calculate_pexpr_y(e_tmp, data); + struct pexpr *expr_m = + expr_calculate_pexpr_m(e_tmp, data); + + updateDefaultList(data->constants->symbol_yes_fexpr, + expr_y, result, sym, data); + updateDefaultList(data->constants->symbol_mod_fexpr, + expr_m, result, sym, data); + PEXPR_PUT(expr_y, expr_m); + } + /* if non-boolean && def.value = non-boolean symbol */ + else if (p->expr->type == E_SYMBOL && sym_is_nonboolean(sym) && + sym_is_nonboolean(p->expr->left.sym)) { + CF_DEF_LIST(nb_sym_defaults, prop); + struct property *p_tmp; + + /* Add defaults of other symbol as possible defaults for + * this symbol + */ + for_all_defaults(p->expr->left.sym, p_tmp) + CF_PUSH_BACK(nb_sym_defaults, p_tmp, prop); + + add_defaults(nb_sym_defaults, expr, result, sym, data); + CF_LIST_FREE(nb_sym_defaults, prop); + } + /* any expression which evaluates to n/m/y */ + else { + struct expr *e_tmp = expr_alloc_and(p->expr, expr); + struct pexpr *expr_both = + expr_calculate_pexpr_both(e_tmp, data); + + updateDefaultList(data->constants->symbol_yes_fexpr, + expr_both, result, sym, data); + + pexpr_put(expr_both); + } + } +} + +/** + * get_defaults() - Generate list of default values and their conditions + * @sym: Symbol whose defaults we want to look at + * + * Creates a map from values that @sym can assume to the conditions under which + * they will be assumed. This will only consider the conditions + * directly associated with the defaults, e.g. sym->dir_dep would not be + * considered. + * + * As a side effect, the &symbol->nb_vals of @sym will be added for + * all default values (as well as the @symbol->nb_vals of other symbols @sym has + * as default (recursively)). + */ +static struct defm_list *calc_default_conditions(struct symbol *sym, + struct cfdata *data) +{ + CF_DEF_LIST(result, defm); + struct prop_list *defaults; /* list of default props of sym */ + struct property *p; + + covered = pexpr_alloc_symbol(data->constants->const_false); + + defaults = CF_LIST_INIT(prop); + for_all_defaults(sym, p) + CF_PUSH_BACK(defaults, p, prop); + + add_defaults(defaults, NULL, result, sym, data); + CF_LIST_FREE(defaults, prop); + pexpr_put(covered); + + return result; +} + +/* + * return the condition for "y", False if it doesn't exist + */ +static struct pexpr *get_default_y(struct defm_list *list, struct cfdata *data) +{ + struct default_map *entry; + struct defm_node *node; + + CF_LIST_FOR_EACH(node, list, defm) { + entry = node->elem; + if (entry->val->type == FE_SYMBOL && + entry->val->sym == &symbol_yes) { + pexpr_get(entry->e); + return entry->e; + } + } + + return pexpr_alloc_symbol(data->constants->const_false); +} + +/* + * return the condition for "m", False if it doesn't exist + */ +static struct pexpr *get_default_m(struct defm_list *list, struct cfdata *data) +{ + struct default_map *entry; + struct defm_node *node; + + CF_LIST_FOR_EACH(node, list, defm) { + entry = node->elem; + if (entry->val->type == FE_SYMBOL && + entry->val->sym == &symbol_mod) { + pexpr_get(entry->e); + return entry->e; + } + } + + return pexpr_alloc_symbol(data->constants->const_false); +} + +/* + * return the constraint when _some_ default value will be applied + */ +static struct pexpr *get_default_any(struct symbol *sym, struct cfdata *data) +{ + struct property *prop; + struct expr *e; + struct pexpr *p; + + if (!sym_is_nonboolean(sym)) + return NULL; + + p = pexpr_alloc_symbol(data->constants->const_false); + for_all_defaults(sym, prop) { + if (prop->visible.expr) + e = prop->visible.expr; + else + e = expr_alloc_symbol(&symbol_yes); + + if (expr_can_evaluate_to_mod(e)) + p = pexpr_or(p, expr_calculate_pexpr_both(e, data), + data, PEXPR_ARGX); + + p = pexpr_or(p, expr_calculate_pexpr_y(e, data), data, + PEXPR_ARGX); + } + + return p; +} + +/* + * get the value for the range + */ +static long sym_get_range_val(struct symbol *sym, int base) +{ + sym_calc_value(sym); + switch (sym->type) { + case S_INT: + base = 10; + break; + case S_HEX: + base = 16; + break; + default: + break; + } + return strtol(sym->curr.val, NULL, base); +} + +/* + * count the number of all constraints + */ +unsigned int count_constraints(void) +{ + unsigned int c = 0; + struct symbol *sym; + + for_all_symbols(sym) { + if (sym->type == S_UNKNOWN) + continue; + + c += list_size(&sym->constraints->list); + } + + return c; +} + +/* + * add a constraint for a symbol + */ +void sym_add_constraint(struct symbol *sym, struct pexpr *constraint, + struct cfdata *data) +{ + if (!constraint) + return; + + /* no need to add that */ + if (constraint->type == PE_SYMBOL && + constraint->left.fexpr == data->constants->const_true) + return; + + /* this should never happen */ + if (constraint->type == PE_SYMBOL && + constraint->left.fexpr == data->constants->const_false) + perror("Adding const_false."); + + CF_PUSH_BACK(sym->constraints, pexpr_get(constraint), pexpr); + + if (!pexpr_is_nnf(constraint)) + pexpr_print("Not NNF:", constraint, -1); +} + +/* + * add a constraint for a symbol, but check for duplicate constraints + */ +void sym_add_constraint_unique(struct symbol *sym, struct pexpr *constraint, + struct cfdata *data) +{ + struct pexpr_node *node; + + if (!constraint) + return; + + /* no need to add that */ + if (constraint->type == PE_SYMBOL && + constraint->left.fexpr == data->constants->const_true) + return; + + /* this should never happen */ + if (constraint->type == PE_SYMBOL && + constraint->left.fexpr == data->constants->const_false) + perror("Adding const_false."); + + /* check the constraints for the same symbol */ + CF_LIST_FOR_EACH(node, sym->constraints, pexpr) + if (pexpr_test_eq(constraint, node->elem, data)) + return; + + CF_PUSH_BACK(sym->constraints, pexpr_get(constraint), pexpr); + + if (!pexpr_is_nnf(constraint)) + pexpr_print("Not NNF:", constraint, -1); +} diff --git a/scripts/kconfig/cf_constraints.h b/scripts/kconfig/cf_constraints.h new file mode 100644 index 000000000000..a68dc9ba4f03 --- /dev/null +++ b/scripts/kconfig/cf_constraints.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Patrick Franz + */ + +#ifndef CF_CONSTRAINTS_H +#define CF_CONSTRAINTS_H + +#include "cf_defs.h" +#include "expr.h" + +/* build the constraints for each symbol */ +void build_constraints(struct cfdata *data); + +/* count the number of all constraints */ +unsigned int count_constraints(void); + +/* add a constraint for a symbol */ +void sym_add_constraint(struct symbol *sym, struct pexpr *constraint, struct cfdata *data); + +/* add a constraint for a symbol, but check for duplicate constraints */ +void sym_add_constraint_unique(struct symbol *sym, struct pexpr *constraint, struct cfdata *data); + +#endif From patchwork Mon Oct 28 03:49:43 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13852975 Received: from mail-ej1-f53.google.com (mail-ej1-f53.google.com [209.85.218.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A14CE18C922; Mon, 28 Oct 2024 03:50:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730087422; cv=none; b=YQ5NYcSkc8Q9ZlcHzBFDT0e7e6UG2xZzeKVB95F+nunCj7j0jnFQ5QbmONK3377UYOaUYhod0PfvMJBsFo64bTwU1agXOUX9DAt7I1iRpmRRS3C7n7qq71P+iQIv765rDEDfFstvd9T01iD1YapqJGPo8vIRELkMaLb9EKdMyxY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730087422; c=relaxed/simple; bh=k/bUKUUAJHe1+NA5BNujHWaV/Z5YTpYwTcwM+s085eE=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=lNGa25aS0r6sD+FwXMtT6uPI3lmrz+MkkhMglEtOuBOwywT0h3tXpb34hFJKtZgmyjJu4ACRQQQbAZqtS1L2gglNc8jxrQzf0AQQEYrgJ7a9j9noEraboNTvR7scjAjB33NcVz1Y2CgNdimUdARu2yWVOMDURsKqNvXqjHA+rbA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=gO1u6saD; arc=none smtp.client-ip=209.85.218.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="gO1u6saD" Received: by mail-ej1-f53.google.com with SMTP id a640c23a62f3a-a9a2209bd7fso570039466b.2; Sun, 27 Oct 2024 20:50:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1730087414; x=1730692214; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=N+SM40gzbEVrxNR9L7vvwwMIRtadsuZvw315apK21qU=; b=gO1u6saDXUILL9b8ZUdaY22i6m1MKH3d15gPODpWw2bVUgokVXp830wrmf4HMN5pOZ SOgzY34d/aRQXfqKUebtjQ4Or7x5mN/4eYsdD7aWRMek+GaBukaEKUgWzfy8nu3X6dAq 8ctJGkFFpE5YH1T5uDcQ97XYkxjvl9Uh8v6RsUKUQA4ggvhnFkyIyz0ywu8+OJQ5YL+v i/6bxVh7RqFzGVWUhy38jKjFWrVW68XORLm1aH7lZ6/dt9vCaDgnMD8gaOeJ9lmNEnvC fs+jU6WLjm6KsmQhX8ZwOjZF5zm6Z8ZIBzrEQV+gKGCPPnQbG2IIoAJHzPwz58YTwnMk vLsA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1730087414; x=1730692214; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=N+SM40gzbEVrxNR9L7vvwwMIRtadsuZvw315apK21qU=; b=ZRjhyCSVOzoBYcPS0WgWcG9i5HhVV5SqdWArtYM/aRHbXCkhv4myxU6Vz4Jy67pGWe stmI7uwRtgKT7ksCZrTgKys4mlOE4h2O00YwGKasImzzWLI4aEGSjL8vOV7BhziLuEQ9 He2ikv+Sp7fnT14Sf2cPtJPirKsWL+FnH4Zhle4Qp6ZJErjH2vjtWXq4lqKi0Thk3iFG +6IqNhGwj3wuSJx7TxnAqGweu9Xlzpdd4tLpwKHVJkm3fCCB3nymgW7ZAJNOjo9A1iHS cUvGFikWqZP6VC7nXWuAEdV4Qmaf2aSvPOT3nx4sxZOS0tkjn80JcV1WCVueP7zWCME+ Bszg== X-Forwarded-Encrypted: i=1; AJvYcCWDJkEOiagl+z7xDLJSVxB3yJls+LK9uW/VBhhGPzj6k0mJeYXrmqTCFBqXEHTvjhUE9NIwvE4S/pWuI7A=@vger.kernel.org X-Gm-Message-State: AOJu0YwGn2grCv7LpifKiigM19bWHe8V+dtqN7nGRy0lYuPAXyIJb136 /XVGje51K3mTMTLDpB1EXX4VUbPAX/TGhGEdYNfpsErMrom4GpV6pbniyw== X-Google-Smtp-Source: AGHT+IFAukOurpze4MCD6vSdfmT1eWM9JqZLzE6yOard99kD9SlZ7YKbytrpv8aQQUV2OfKzCk4RSQ== X-Received: by 2002:a17:907:7f03:b0:a99:f209:ceaa with SMTP id a640c23a62f3a-a9de5c919acmr617488766b.10.1730087413569; Sun, 27 Oct 2024 20:50:13 -0700 (PDT) Received: from localhost.localdomain ([2a02:908:e842:bf20:78b:e59b:2b0:d2e9]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9b30f5932fsm334599366b.168.2024.10.27.20.50.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 27 Oct 2024 20:50:12 -0700 (PDT) From: Ole Schuerks To: linux-kbuild@vger.kernel.org Cc: ole0811sch@gmail.com, jude.gyimah@rub.de, thorsten.berger@rub.de, deltaone@debian.org, jan.sollmann@rub.de, mcgrof@kernel.org, masahiroy@kernel.org, linux-kernel@vger.kernel.org, nathan@kernel.org, nicolas@fjasle.eu Subject: [PATCH v6 05/11] kconfig: Add files for handling expressions Date: Mon, 28 Oct 2024 04:49:43 +0100 Message-Id: <20241028034949.95322-6-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20241028034949.95322-1-ole0811sch@gmail.com> References: <20241028034949.95322-1-ole0811sch@gmail.com> Precedence: bulk X-Mailing-List: linux-kbuild@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To translate the Kconfig-model into propositional logic and resolve conflicts, we need to handle propostional formulas. These files contain many functions and macros to deal with propositional formulas. Co-developed-by: Patrick Franz Signed-off-by: Patrick Franz Co-developed-by: Ibrahim Fayaz Signed-off-by: Ibrahim Fayaz Reviewed-by: Luis Chamberlain Tested-by: Evgeny Groshev Suggested-by: Sarah Nadi Suggested-by: Thorsten Berger Signed-off-by: Thorsten Berger Signed-off-by: Ole Schuerks --- scripts/kconfig/cf_expr.c | 2003 +++++++++++++++++++++++++++++++++++++ scripts/kconfig/cf_expr.h | 181 ++++ 2 files changed, 2184 insertions(+) create mode 100644 scripts/kconfig/cf_expr.c create mode 100644 scripts/kconfig/cf_expr.h diff --git a/scripts/kconfig/cf_expr.c b/scripts/kconfig/cf_expr.c new file mode 100644 index 000000000000..e4c97439ffdf --- /dev/null +++ b/scripts/kconfig/cf_expr.c @@ -0,0 +1,2003 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Patrick Franz + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "lkc.h" +#include "list.h" +#include "cf_expr.h" +#include "cf_defs.h" +#include "cf_utils.h" + +static void create_fexpr_bool(struct symbol *sym, struct cfdata *data); +static void create_fexpr_nonbool(struct symbol *sym, struct cfdata *data); +static void create_fexpr_unknown(struct symbol *sym, struct cfdata *data); +static void create_fexpr_choice(struct symbol *sym, struct cfdata *data); + +static void pexpr_print_util(struct pexpr *e, int prevtoken); +static void pexpr_shallow_copy(struct pexpr *dest, struct pexpr *org, + unsigned int ref_count); + +static struct pexpr *pexpr_move_wrapper( + struct pexpr *a, struct pexpr *b, struct cfdata *data, + enum pexpr_move move, + struct pexpr *(*func)(struct pexpr *, struct pexpr *, struct cfdata *)); + +static int trans_count; + + +/* + * create a fexpr + */ +struct fexpr *fexpr_create(int satval, enum fexpr_type type, char *name) +{ + struct fexpr *e = xcalloc(1, sizeof(*e)); + + e->satval = satval; + e->type = type; + e->name = str_new(); + e->assumption = false; + str_append(&e->name, name); + + return e; +} + +/* + * create the fexpr for a symbol + */ +void sym_create_fexpr(struct symbol *sym, struct cfdata *data) +{ + if (sym_is_choice(sym)) + create_fexpr_choice(sym, data); + else if (sym_is_boolean(sym)) + create_fexpr_bool(sym, data); + else if (sym_is_nonboolean(sym)) + create_fexpr_nonbool(sym, data); + else + create_fexpr_unknown(sym, data); +} + +/* + * create the fexpr for symbols with reverse dependencies + */ +static void create_fexpr_selected(struct symbol *sym, struct cfdata *data) +{ + struct fexpr *fexpr_sel_y; + struct fexpr *fexpr_sel_m; + + /* fexpr_sel_y */ + fexpr_sel_y = + fexpr_create(data->sat_variable_nr++, FE_SELECT, sym->name); + str_append(&fexpr_sel_y->name, "_sel_y"); + fexpr_sel_y->sym = sym; + fexpr_add_to_satmap(fexpr_sel_y, data); + + sym->fexpr_sel_y = fexpr_sel_y; + + /* fexpr_sel_m */ + if (sym->type == S_BOOLEAN) + return; + + fexpr_sel_m = + fexpr_create(data->sat_variable_nr++, FE_SELECT, sym->name); + str_append(&fexpr_sel_m->name, "_sel_m"); + fexpr_sel_m->sym = sym; + fexpr_add_to_satmap(fexpr_sel_m, data); + + sym->fexpr_sel_m = fexpr_sel_m; +} + +/* + * create the fexpr for a boolean/tristate symbol + */ +static void create_fexpr_bool(struct symbol *sym, struct cfdata *data) +{ + struct fexpr *fexpr_y; + struct fexpr *fexpr_m; + + fexpr_y = fexpr_create(data->sat_variable_nr++, FE_SYMBOL, sym->name); + fexpr_y->sym = sym; + fexpr_y->tri = yes; + fexpr_add_to_satmap(fexpr_y, data); + + sym->fexpr_y = fexpr_y; + + + if (sym->type == S_TRISTATE) { + fexpr_m = fexpr_create(data->sat_variable_nr++, FE_SYMBOL, + sym->name); + str_append(&fexpr_m->name, "_MODULE"); + fexpr_m->sym = sym; + fexpr_m->tri = mod; + fexpr_add_to_satmap(fexpr_m, data); + } else { + fexpr_m = data->constants->const_false; + } + + sym->fexpr_m = fexpr_m; + + if (sym->rev_dep.expr) + create_fexpr_selected(sym, data); +} + +/* + * create the fexpr for a non-boolean symbol + */ +static void create_fexpr_nonbool(struct symbol *sym, struct cfdata *data) +{ + /* default values */ + char int_values[][2] = {"n", "0", "1"}; + char hex_values[][4] = {"n", "0x0", "0x1"}; + char string_values[][9] = {"n", "", "nonempty"}; + + sym->fexpr_y = data->constants->const_false; + sym->fexpr_m = data->constants->const_false; + sym->nb_vals = xmalloc(sizeof(*sym->nb_vals)); + INIT_LIST_HEAD(&sym->nb_vals->list); + + for (int i = 0; i < 3; i++) { + struct fexpr *e = fexpr_create(data->sat_variable_nr++, + FE_NONBOOL, sym->name); + + e->sym = sym; + str_append(&e->name, "="); + e->nb_val = str_new(); + + switch (sym->type) { + case S_INT: + str_append(&e->name, int_values[i]); + str_append(&e->nb_val, int_values[i]); + break; + case S_HEX: + str_append(&e->name, hex_values[i]); + str_append(&e->nb_val, hex_values[i]); + break; + case S_STRING: + str_append(&e->name, string_values[i]); + str_append(&e->nb_val, string_values[i]); + break; + default: + break; + } + + CF_PUSH_BACK(sym->nb_vals, e, fexpr); + fexpr_add_to_satmap(e, data); + } +} + +/* + * set fexpr_y and fexpr_m simply to False + */ +static void create_fexpr_unknown(struct symbol *sym, struct cfdata *data) +{ + sym->fexpr_y = data->constants->const_false; + sym->fexpr_m = data->constants->const_false; +} + +/* + * create the fexpr for a choice symbol + */ +static void create_fexpr_choice(struct symbol *sym, struct cfdata *data) +{ + struct property *prompt; + char *name, *write, *read; + struct fexpr *fexpr_y; + struct fexpr *fexpr_m; + + if (!sym_is_boolean(sym)) + return; + + prompt = sym_get_prompt(sym); + if (prompt == NULL) { + perror("Choice symbol should have a prompt."); + return; + } + + name = strdup(prompt->text); + + /* remove spaces */ + write = name; + read = name; + do { + if (*read != ' ') + *write++ = *read; + } while (*read++); + + fexpr_y = fexpr_create(data->sat_variable_nr++, FE_CHOICE, "Choice_"); + str_append(&fexpr_y->name, name); + fexpr_y->sym = sym; + fexpr_y->tri = yes; + fexpr_add_to_satmap(fexpr_y, data); + + sym->fexpr_y = fexpr_y; + + if (sym->type == S_TRISTATE) { + fexpr_m = fexpr_create(data->sat_variable_nr++, FE_CHOICE, + "Choice_"); + str_append(&fexpr_m->name, name); + str_append(&fexpr_m->name, "_MODULE"); + fexpr_m->sym = sym; + fexpr_m->tri = mod; + fexpr_add_to_satmap(fexpr_m, data); + } else { + fexpr_m = data->constants->const_false; + } + sym->fexpr_m = fexpr_m; + free(name); +} + +/* + * evaluate an unequality between a non-Boolean symbol and a constant + */ +static struct pexpr *expr_eval_unequal_nonbool_const(struct symbol *sym, + struct symbol *compval, + enum expr_type type, + struct cfdata *data) +{ + int base; + struct pexpr *c; + long val; + struct fexpr_node *node; + struct fexpr *fe; + bool first; + + if (!sym || !compval) + return pexpr_alloc_symbol(data->constants->const_false); + + base = 0; + switch (sym->type) { + case S_INT: + base = 10; + break; + case S_HEX: + base = 16; + break; + default: + break; + } + + c = pexpr_alloc_symbol(data->constants->const_false); + val = strtol(compval->name, NULL, base); + first = true; + CF_LIST_FOR_EACH(node, sym->nb_vals, fexpr) { + long symval; + + if (first) { + first = false; + continue; + } + fe = node->elem; + symval = strtol(str_get(&fe->nb_val), NULL, base); + + switch (type) { + case E_LTH: + if (symval < val) + c = pexpr_or(c, pexpr_alloc_symbol(fe), data, + PEXPR_ARGX); + break; + case E_LEQ: + if (symval <= val) + c = pexpr_or(c, pexpr_alloc_symbol(fe), data, + PEXPR_ARGX); + break; + case E_GTH: + if (symval > val) + c = pexpr_or(c, pexpr_alloc_symbol(fe), data, + PEXPR_ARGX); + break; + case E_GEQ: + if (symval >= val) + c = pexpr_or(c, pexpr_alloc_symbol(fe), data, + PEXPR_ARGX); + break; + default: + perror("Illegal unequal."); + } + } + + return c; +} + +/* + * evaluate an unequality between 2 Boolean symbols + */ +static struct pexpr *expr_eval_unequal_bool(struct symbol *left, + struct symbol *right, + enum expr_type type, + struct cfdata *data) +{ + struct pexpr *c; + + if (!left || !right) + return pexpr_alloc_symbol(data->constants->const_false); + + if (!sym_is_boolean(left) || !sym_is_boolean(right)) { + perror("Comparing 2 symbols that should be boolean."); + return pexpr_alloc_symbol(data->constants->const_false); + } + + switch (type) { + case E_LTH: + c = pexpr_and(pexpr_not(sym_get_fexpr_both(left, data), data), + sym_get_fexpr_both(right, data), data, + PEXPR_ARGX); + if (left->type == S_TRISTATE) + c = pexpr_or( + c, + pexpr_and(pexpr_alloc_symbol(left->fexpr_m), + pexpr_alloc_symbol(right->fexpr_y), + data, PEXPR_ARGX), + data, PEXPR_ARGX); + break; + case E_LEQ: + c = pexpr_and(pexpr_alloc_symbol(left->fexpr_y), + pexpr_alloc_symbol(right->fexpr_y), data, + PEXPR_ARGX); + if (left->type == S_TRISTATE) + c = pexpr_or( + c, + pexpr_and(pexpr_alloc_symbol(left->fexpr_m), + sym_get_fexpr_both(right, data), data, + PEXPR_ARGX), + data, PEXPR_ARGX); + c = pexpr_or(c, pexpr_not(sym_get_fexpr_both(left, data), data), + data, PEXPR_ARGX); + break; + case E_GTH: + c = pexpr_and(sym_get_fexpr_both(left, data), + pexpr_not(sym_get_fexpr_both(right, data), data), + data, PEXPR_ARGX); + if (right->type == S_TRISTATE) + c = pexpr_or( + c, + pexpr_and(pexpr_alloc_symbol(left->fexpr_y), + pexpr_alloc_symbol(right->fexpr_m), + data, PEXPR_ARGX), + data, PEXPR_ARGX); + break; + case E_GEQ: + c = pexpr_and(pexpr_alloc_symbol(left->fexpr_y), + pexpr_alloc_symbol(right->fexpr_y), data, + PEXPR_ARGX); + if (right->type == S_TRISTATE) + c = pexpr_or( + c, + pexpr_and(sym_get_fexpr_both(left, data), + pexpr_alloc_symbol(right->fexpr_m), + data, PEXPR_ARGX), + data, PEXPR_ARGX); + c = pexpr_or(c, + pexpr_not(sym_get_fexpr_both(right, data), data), + data, PEXPR_ARGX); + break; + default: + fprintf(stderr, "Wrong type - %s", __func__); + c = pexpr_alloc_symbol(data->constants->const_false); + } + + return c; +} +/* + * calculate, when expr will evaluate to yes or mod + */ +struct pexpr *expr_calculate_pexpr_both(struct expr *e, struct cfdata *data) +{ + if (!e) + return pexpr_alloc_symbol(data->constants->const_false); + + if (!expr_can_evaluate_to_mod(e)) + return expr_calculate_pexpr_y(e, data); + + switch (e->type) { + case E_SYMBOL: + return pexpr_or(expr_calculate_pexpr_m(e, data), + expr_calculate_pexpr_y(e, data), data, + PEXPR_ARGX); + case E_AND: + return expr_calculate_pexpr_both_and(e->left.expr, + e->right.expr, data); + case E_OR: + return expr_calculate_pexpr_both_or(e->left.expr, e->right.expr, + data); + case E_NOT: + return pexpr_or(expr_calculate_pexpr_m(e, data), + expr_calculate_pexpr_y(e, data), data, + PEXPR_ARGX); + case E_EQUAL: + return expr_calculate_pexpr_y_equals(e, data); + case E_UNEQUAL: + return expr_calculate_pexpr_y_unequals(e, data); + case E_LTH: + case E_LEQ: + case E_GTH: + case E_GEQ: + return expr_calculate_pexpr_y_comp(e, data); + default: + // TODO + fprintf(stderr, "Unhandled type - %s", __func__); + return NULL; + } +} + +/* + * calculate, when expr will evaluate to yes + */ +struct pexpr *expr_calculate_pexpr_y(struct expr *e, struct cfdata *data) +{ + if (!e) + return NULL; + + switch (e->type) { + case E_SYMBOL: + return pexpr_alloc_symbol(e->left.sym->fexpr_y); + case E_AND: + return expr_calculate_pexpr_y_and(e->left.expr, e->right.expr, + data); + case E_OR: + return expr_calculate_pexpr_y_or(e->left.expr, e->right.expr, + data); + case E_NOT: + return expr_calculate_pexpr_y_not(e->left.expr, data); + case E_EQUAL: + return expr_calculate_pexpr_y_equals(e, data); + case E_UNEQUAL: + return expr_calculate_pexpr_y_unequals(e, data); + case E_LTH: + case E_LEQ: + case E_GTH: + case E_GEQ: + return expr_calculate_pexpr_y_comp(e, data); + default: + fprintf(stderr, "Unhandled type - %s", __func__); + return NULL; + } +} + +/* + * calculate, when expr will evaluate to mod + */ +struct pexpr *expr_calculate_pexpr_m(struct expr *e, struct cfdata *data) +{ + if (!e) + return NULL; + + if (!expr_can_evaluate_to_mod(e)) + return pexpr_alloc_symbol(data->constants->const_false); + + switch (e->type) { + case E_SYMBOL: + return pexpr_alloc_symbol(e->left.sym->fexpr_m); + case E_AND: + return expr_calculate_pexpr_m_and(e->left.expr, e->right.expr, + data); + case E_OR: + return expr_calculate_pexpr_m_or(e->left.expr, e->right.expr, + data); + case E_NOT: + return expr_calculate_pexpr_m_not(e->left.expr, data); + default: + perror("Trying to evaluate to mod."); + return NULL; + } +} + +/* + * calculate, when expr of type AND will evaluate to yes + * A && B + */ +struct pexpr *expr_calculate_pexpr_y_and(struct expr *a, struct expr *b, + struct cfdata *data) +{ + return pexpr_and(expr_calculate_pexpr_y(a, data), + expr_calculate_pexpr_y(b, data), data, + PEXPR_ARGX); +} + +/* + * calculate, when expr of type AND will evaluate to mod + * (A || A_m) && (B || B_m) && !(A && B) + */ +struct pexpr *expr_calculate_pexpr_m_and(struct expr *a, struct expr *b, + struct cfdata *data) +{ + struct pexpr *topright = + pexpr_not(pexpr_and(expr_calculate_pexpr_y(a, data), + expr_calculate_pexpr_y(b, data), + data, PEXPR_ARGX), + data); + struct pexpr *ll_left = pexpr_or(expr_calculate_pexpr_y(a, data), + expr_calculate_pexpr_m(a, data), data, + PEXPR_ARGX); + struct pexpr *ll_right = pexpr_or(expr_calculate_pexpr_y(b, data), + expr_calculate_pexpr_m(b, data), data, + PEXPR_ARGX); + struct pexpr *topleft = pexpr_and(ll_left, ll_right, data, PEXPR_ARGX); + + return pexpr_and(topleft, topright, data, PEXPR_ARGX); +} + +/* + * calculate, when expr of type AND will evaluate to mod or yes + * (A || A_m) && (B || B_m) + */ +struct pexpr *expr_calculate_pexpr_both_and(struct expr *a, struct expr *b, + struct cfdata *data) +{ + struct pexpr *left = pexpr_or(expr_calculate_pexpr_y(a, data), + expr_calculate_pexpr_m(a, data), data, + PEXPR_ARGX); + struct pexpr *right = pexpr_or(expr_calculate_pexpr_y(b, data), + expr_calculate_pexpr_m(b, data), data, + PEXPR_ARGX); + + return pexpr_and(left, right, data, PEXPR_ARGX); +} + +/* + * calculate, when expr of type OR will evaluate to yes + * A || B + */ +struct pexpr *expr_calculate_pexpr_y_or(struct expr *a, struct expr *b, + struct cfdata *data) +{ + return pexpr_or(expr_calculate_pexpr_y(a, data), + expr_calculate_pexpr_y(b, data), data, PEXPR_ARGX); +} + +/* + * calculate, when expr of type OR will evaluate to mod + * (A_m || B_m) && !A && !B + */ +struct pexpr *expr_calculate_pexpr_m_or(struct expr *a, struct expr *b, + struct cfdata *data) +{ + struct pexpr *topright = + pexpr_not(expr_calculate_pexpr_y(b, data), data); + struct pexpr *lowerleft = pexpr_or(expr_calculate_pexpr_m(a, data), + expr_calculate_pexpr_m(b, data), + data, PEXPR_ARGX); + struct pexpr *topleft = pexpr_and( + lowerleft, + pexpr_not(expr_calculate_pexpr_y(a, data), data), data, + PEXPR_ARGX); + + return pexpr_and(topleft, topright, data, PEXPR_ARGX); +} + +/* + * calculate, when expr of type OR will evaluate to mod or yes + * (A_m || A || B_m || B) + */ +struct pexpr *expr_calculate_pexpr_both_or(struct expr *a, struct expr *b, + struct cfdata *data) +{ + struct pexpr *left = pexpr_or(expr_calculate_pexpr_y(a, data), + expr_calculate_pexpr_m(a, data), data, + PEXPR_ARGX); + struct pexpr *right = pexpr_or(expr_calculate_pexpr_y(b, data), + expr_calculate_pexpr_m(b, data), data, + PEXPR_ARGX); + + return pexpr_or(left, right, data, PEXPR_ARGX); +} + +/* + * calculate, when expr of type NOT will evaluate to yes + * !(A || A_m) + */ +struct pexpr *expr_calculate_pexpr_y_not(struct expr *e, struct cfdata *data) +{ + return pexpr_not(pexpr_or(expr_calculate_pexpr_y(e, data), + expr_calculate_pexpr_m(e, data), + data, PEXPR_ARGX), + data); +} + +/* + * calculate, when expr of type NOT will evaluate to mod + * A_m + */ +struct pexpr *expr_calculate_pexpr_m_not(struct expr *e, struct cfdata *data) +{ + return expr_calculate_pexpr_m(e, data); +} + +static struct pexpr *equiv_pexpr_share(struct pexpr *a, struct pexpr *b, + struct cfdata *data) +{ + struct pexpr *yes = pexpr_and_share(a, b, data); + struct pexpr *not = pexpr_and(pexpr_not_share(a, data), + pexpr_not_share(b, data), data, + PEXPR_ARGX); + + return pexpr_or(yes, not, data, PEXPR_ARGX); +} + +static struct pexpr *equiv_pexpr_move(struct pexpr *a, struct pexpr *b, + struct cfdata *data, + enum pexpr_move move) +{ + return pexpr_move_wrapper(a, b, data, move, equiv_pexpr_share); +} + +/* + * create the fexpr of a non-boolean symbol for a specific value + */ +struct fexpr *sym_create_nonbool_fexpr(struct symbol *sym, char *value, + struct cfdata *data) +{ + struct fexpr *e; + char *s; + struct fexpr_node *first = + list_first_entry(&sym->nb_vals->list, struct fexpr_node, node); + + if (!strcmp(value, "")) { + if (sym->type == S_STRING) + return list_next_entry(first, node)->elem; + else + return first->elem; + } + + e = sym_get_nonbool_fexpr(sym, value); + + /* fexpr already exists */ + if (e != NULL) + return e; + + s = value; + if (sym->type == S_INT && !string_is_number(value)) { + struct symbol *tmp = sym_find(value); + + if (tmp != NULL) + s = (char *) tmp->curr.val; + } else if (sym->type == S_HEX && !string_is_hex(value)) { + struct symbol *tmp = sym_find(value); + + if (tmp != NULL) + s = (char *) tmp->curr.val; + } else if (sym->type == S_STRING) { + struct symbol *tmp = sym_find(value); + + if (tmp != NULL) + s = (char *) tmp->curr.val; + } + + if (!strcmp(s, "")) { + if (sym->type == S_STRING) + return list_next_entry(first, node)->elem; + else + return first->elem; + } + + e = sym_get_nonbool_fexpr(sym, s); + if (e != NULL) + return e; + + e = fexpr_create(data->sat_variable_nr++, FE_NONBOOL, sym->name); + e->sym = sym; + str_append(&e->name, "="); + str_append(&e->name, s); + e->nb_val = str_new(); + str_append(&e->nb_val, s); + + CF_PUSH_BACK(sym->nb_vals, e, fexpr); + fexpr_add_to_satmap(e, data); + + return e; +} + +/* + * return the fexpr of a non-boolean symbol for a specific value, NULL if + * non-existent + */ +struct fexpr *sym_get_nonbool_fexpr(struct symbol *sym, char *value) +{ + struct fexpr_node *e; + + CF_LIST_FOR_EACH(e, sym->nb_vals, fexpr) { + if (strcmp(str_get(&e->elem->nb_val), value) == 0) + return e->elem; + } + + return NULL; +} + +/* + * return the fexpr of a non-boolean symbol for a specific value, if it exists + * otherwise create it + */ +struct fexpr *sym_get_or_create_nonbool_fexpr(struct symbol *sym, char *value, + struct cfdata *data) +{ + struct fexpr *e = sym_get_nonbool_fexpr(sym, value); + + if (e != NULL) + return e; + else + return sym_create_nonbool_fexpr(sym, value, data); +} + +/* + * calculate, when expr of type EQUAL will evaluate to yes + * Side effect: May create certain values in e->{left,right}.sym->nb_vals + */ +struct pexpr *expr_calculate_pexpr_y_equals(struct expr *e, struct cfdata *data) +{ + /* comparing 2 tristate constants */ + if (sym_is_tristate_constant(e->left.sym) && + sym_is_tristate_constant(e->right.sym)) + return e->left.sym == e->right.sym ? + pexpr_alloc_symbol(data->constants->const_true) : + pexpr_alloc_symbol(data->constants->const_false); + + /* comparing 2 nonboolean constants */ + if (sym_is_nonbool_constant(e->left.sym) && + sym_is_nonbool_constant(e->right.sym)) + return strcmp(e->left.sym->name, e->right.sym->name) == 0 ? + pexpr_alloc_symbol(data->constants->const_true) : + pexpr_alloc_symbol(data->constants->const_false); + + /* comparing 2 boolean/tristate incl. yes/mod/no constants */ + if (sym_is_bool_or_triconst(e->left.sym) && + sym_is_bool_or_triconst(e->right.sym)) { + struct pexpr *yes = equiv_pexpr_move( + pexpr_alloc_symbol(e->left.sym->fexpr_y), + pexpr_alloc_symbol(e->right.sym->fexpr_y), data, + PEXPR_ARGX); + struct pexpr *mod = equiv_pexpr_move( + pexpr_alloc_symbol(e->left.sym->fexpr_m), + pexpr_alloc_symbol(e->right.sym->fexpr_m), data, + PEXPR_ARGX); + + return pexpr_and(yes, mod, data, PEXPR_ARGX); + } + + /* comparing nonboolean with a constant */ + if (sym_is_nonboolean(e->left.sym) && + sym_is_nonbool_constant(e->right.sym)) + return pexpr_alloc_symbol(sym_get_or_create_nonbool_fexpr( + e->left.sym, e->right.sym->name, data)); + + if (sym_is_nonbool_constant(e->left.sym) && + sym_is_nonboolean(e->right.sym)) + return pexpr_alloc_symbol(sym_get_or_create_nonbool_fexpr( + e->right.sym, e->left.sym->name, data)); + + /* comparing nonboolean with tristate constant, will never be true */ + if (sym_is_nonboolean(e->left.sym) && + sym_is_tristate_constant(e->right.sym)) + return pexpr_alloc_symbol(data->constants->const_false); + if (sym_is_tristate_constant(e->left.sym) && + sym_is_nonboolean(e->right.sym)) + return pexpr_alloc_symbol(data->constants->const_false); + + /* comparing 2 nonboolean symbols */ + if (sym_is_nonboolean(e->left.sym) && sym_is_nonboolean(e->right.sym)) { + struct pexpr *c = + pexpr_alloc_symbol(data->constants->const_false); + struct fexpr *e1, *e2; + struct fexpr_node *node1, *node2; + bool first1 = true; + + CF_LIST_FOR_EACH(node1, e->left.sym->nb_vals, fexpr) { + bool first2 = true; + + if (first1) { + first1 = false; + continue; + } + e1 = node1->elem; + CF_LIST_FOR_EACH(node2, e->right.sym->nb_vals, fexpr) + { + if (first2) { + first2 = false; + continue; + } + + e2 = node2->elem; + if (!strcmp(str_get(&e1->nb_val), + str_get(&e2->nb_val))) { + c = pexpr_or( + c, + pexpr_and( + pexpr_alloc_symbol(e1), + pexpr_alloc_symbol(e2), + data, PEXPR_ARGX), + data, PEXPR_ARGX); + break; + } + } + } + return c; + } + + /* + * comparing boolean item with nonboolean constant, will never be true + */ + if (sym_is_tristate_constant(e->left.sym) && + sym_is_nonbool_constant(e->right.sym)) + return pexpr_alloc_symbol(data->constants->const_false); + if (sym_is_nonbool_constant(e->left.sym) && + sym_is_tristate_constant(e->right.sym)) + return pexpr_alloc_symbol(data->constants->const_false); + + /* comparing symbol of type unknown with tristate constant */ + if (e->left.sym->type == S_UNKNOWN && + sym_is_tristate_constant(e->right.sym)) + return pexpr_alloc_symbol(data->constants->const_false); + if (sym_is_tristate_constant(e->left.sym) && + e->right.sym->type == S_UNKNOWN) + return pexpr_alloc_symbol(data->constants->const_false); + + /* any other comparison is not supported and should not be executed */ + fprintf(stderr, "Unsupported equality in:"); + expr_fprint(e, stderr); + + return pexpr_alloc_symbol(data->constants->const_false); +} + +/* + * transform an UNEQUAL into a Not(EQUAL) + */ +struct pexpr *expr_calculate_pexpr_y_unequals(struct expr *e, + struct cfdata *data) +{ + return pexpr_not(expr_calculate_pexpr_y_equals(e, data), data); +} + +struct pexpr *expr_calculate_pexpr_y_comp(struct expr *e, struct cfdata *data) +{ + if (!e) + return NULL; + + switch (e->type) { + case E_LTH: + case E_LEQ: + case E_GTH: + case E_GEQ: + /* compare non-Boolean symbol with constant */ + if (sym_is_nonboolean(e->left.sym) && + e->right.sym->type == S_UNKNOWN && + string_is_number(e->right.sym->name) + ) { + return expr_eval_unequal_nonbool_const( + e->left.sym, e->right.sym, e->type, data); + } + if (sym_is_nonboolean(e->right.sym) && + e->left.sym->type == S_UNKNOWN && + string_is_number(e->left.sym->name) + ) { + return expr_eval_unequal_nonbool_const( + e->right.sym, e->left.sym, e->type, data); + } + + /* compare 2 Boolean symbols */ + if (sym_is_boolean(e->left.sym) && sym_is_boolean(e->right.sym)) + return expr_eval_unequal_bool(e->left.sym, e->right.sym, + e->type, data); + + return pexpr_alloc_symbol(data->constants->const_false); + default: + fprintf(stderr, "Unhandled type - %s", __func__); + return NULL; + } +} + +static struct pexpr *pexpr_move_wrapper( + struct pexpr *a, struct pexpr *b, struct cfdata *data, + enum pexpr_move move, + struct pexpr *(*func)(struct pexpr *, struct pexpr *, struct cfdata *)) +{ + struct pexpr *retval = func(a, b, data); + + switch (move) { + case PEXPR_ARG1: + pexpr_put(a); + break; + case PEXPR_ARG2: + pexpr_put(b); + break; + case PEXPR_ARGX: + pexpr_put(a); + pexpr_put(b); + break; + default: + fprintf(stderr, "%s: invalid value for @move - %d\n", __func__, + move); + } + return retval; +} + +struct pexpr *pexpr_and(struct pexpr *a, struct pexpr *b, struct cfdata *data, + enum pexpr_move move) +{ + return pexpr_move_wrapper(a, b, data, move, pexpr_and_share); +} + +/* + * macro to create a pexpr of type AND + */ +struct pexpr *pexpr_and_share(struct pexpr *a, struct pexpr *b, + struct cfdata *data) +{ + struct pexpr *e; + + /* A && A -> A */ + if (a == b || pexpr_test_eq(a, b, data)) { + pexpr_get(a); + return a; + } + + /* simplifications: + * expr && False -> False + * expr && True -> expr + */ + if ((a->type == PE_SYMBOL && + a->left.fexpr == data->constants->const_false) || + (b->type == PE_SYMBOL && + b->left.fexpr == data->constants->const_true)) { + pexpr_get(a); + return a; + } + + if ((b->type == PE_SYMBOL && + b->left.fexpr == data->constants->const_false) || + (a->type == PE_SYMBOL && + a->left.fexpr == data->constants->const_true)) { + pexpr_get(b); + return b; + } + + /* (A && B) && C -> A && B if B == C */ + if (a->type == PE_AND && pexpr_test_eq(a->right.pexpr, b, data)) { + pexpr_get(a); + return a; + } + + /* A && (B && C) -> B && C if A == B */ + if (b->type == PE_AND && pexpr_test_eq(a, b->left.pexpr, data)) { + pexpr_get(b); + return b; + } + + if (a->type == PE_OR && b->type == PE_OR) { + e = NULL; + /* (A || B) && (C || D) -> A || (B && D) if A == C */ + if (pexpr_test_eq(a->left.pexpr, b->left.pexpr, data)) { + e = pexpr_or(a->left.pexpr, + pexpr_and_share(a->right.pexpr, + b->right.pexpr, data), + data, PEXPR_ARG2); + } + /* (A || B) && (C || D) -> B || (A && C) if B == D */ + else if (pexpr_test_eq(a->right.pexpr, b->right.pexpr, data)) { + e = pexpr_or(a->right.pexpr, + pexpr_and_share(a->left.pexpr, + b->left.pexpr, data), + data, PEXPR_ARG2); + } + /* (A || B) && (C || D) -> A || (B && C) if A == D */ + else if (pexpr_test_eq(a->left.pexpr, b->right.pexpr, data)) { + e = pexpr_or(a->left.pexpr, + pexpr_and_share(a->right.pexpr, + b->left.pexpr, data), + data, PEXPR_ARG2); + } + /* (A || B) && (C || D) -> B || (A && D) if B == C */ + else if (pexpr_test_eq(a->right.pexpr, b->left.pexpr, data)) { + e = pexpr_or(a->right.pexpr, + pexpr_and_share(a->left.pexpr, + b->right.pexpr, data), + data, PEXPR_ARG2); + } + if (e) + return e; + } + + /* general case */ + e = xmalloc(sizeof(*e)); + pexpr_get(a); + pexpr_get(b); + pexpr_construct_and(e, a, b, 1); + return e; +} + +struct pexpr *pexpr_or(struct pexpr *a, struct pexpr *b, struct cfdata *data, + enum pexpr_move move) +{ + return pexpr_move_wrapper(a, b, data, move, pexpr_or_share); +} + +/* + * macro to create a pexpr of type OR + */ +struct pexpr *pexpr_or_share(struct pexpr *a, struct pexpr *b, + struct cfdata *data) +{ + struct pexpr *e; + bool cond1, cond2; + + /* A || A -> A */ + if (a == b || pexpr_test_eq(a, b, data)) { + pexpr_get(a); + return a; + } + + /* simplifications: + * A || False -> A + * A || True -> True + */ + cond1 = a->type == PE_SYMBOL && + a->left.fexpr == data->constants->const_false; + cond2 = b->type == PE_SYMBOL && + b->left.fexpr == data->constants->const_true; + if (cond1 || cond2) { + pexpr_get(b); + return b; + } + cond1 = b->type == PE_SYMBOL && + b->left.fexpr == data->constants->const_false; + cond2 = a->type == PE_SYMBOL && + a->left.fexpr == data->constants->const_true; + if (cond1 || cond2) { + pexpr_get(a); + return a; + } + + /* A || (B && C) -> A if (A == B || A == C) */ + if (b->type == PE_AND && (pexpr_test_eq(a, b->left.pexpr, data) || + pexpr_test_eq(a, b->right.pexpr, data))) { + pexpr_get(a); + return a; + } + /* (A && B) || C -> C if (A == C || B == C) */ + if (a->type == PE_AND && (pexpr_test_eq(a->left.pexpr, b, data) || + pexpr_test_eq(a->right.pexpr, b, data))) { + pexpr_get(b); + return b; + } + + /* -A || B -> True if A == B + * A || -B -> True if A == B + */ + cond1 = a->type == PE_NOT && pexpr_test_eq(a->left.pexpr, b, data); + cond2 = b->type == PE_NOT && pexpr_test_eq(a, b->left.pexpr, data); + if (cond1 || cond2) + return pexpr_alloc_symbol(data->constants->const_true); + + if (a->type == PE_AND && b->type == PE_AND) { + e = NULL; + /* (A && B) || (C && D) -> A && (B || D) if (A == C) */ + if (pexpr_test_eq(a->left.pexpr, b->left.pexpr, data)) { + e = pexpr_and(a->left.pexpr, + pexpr_or_share(a->right.pexpr, + b->right.pexpr, data), + data, PEXPR_ARG2); + } + /* (A && B) || (C && D) -> B && (A || C) if (B == D) */ + if (pexpr_test_eq(a->right.pexpr, b->right.pexpr, data)) { + e = pexpr_and(a->right.pexpr, + pexpr_or_share(a->left.pexpr, + b->left.pexpr, data), + data, PEXPR_ARG2); + } + /* (A && B) || (C && D) -> A && (B || C) if (A == D) */ + if (pexpr_test_eq(a->left.pexpr, b->right.pexpr, data)) { + e = pexpr_and(a->left.pexpr, + pexpr_or_share(a->right.pexpr, + b->left.pexpr, data), + data, PEXPR_ARG2); + } + /* (A && B) || (C && D) -> B && (A || D) if (B == C) */ + if (pexpr_test_eq(a->right.pexpr, b->left.pexpr, data)) { + e = pexpr_and(a->right.pexpr, + pexpr_or_share(a->left.pexpr, + b->right.pexpr, data), + data, PEXPR_ARG2); + } + if (e) + return e; + } + + /* (A && B) || (C || D) -> C || D if + * A == C || A == D || B == C || B == D + */ + if (a->type == PE_AND && b->type == PE_OR && + (pexpr_test_eq(a->left.pexpr, b->left.pexpr, data) || + pexpr_test_eq(a->left.pexpr, b->right.pexpr, data) || + pexpr_test_eq(a->right.pexpr, b->left.pexpr, data) || + pexpr_test_eq(a->right.pexpr, b->right.pexpr, data))) { + pexpr_get(b); + return b; + } + /* (C || D) || (A && B) -> C || D if + * A == C || A == D || B == C || B == D + */ + if (a->type == PE_OR && b->type == PE_AND && + (pexpr_test_eq(a->left.pexpr, b->left.pexpr, data) || + pexpr_test_eq(a->left.pexpr, b->right.pexpr, data) || + pexpr_test_eq(a->right.pexpr, b->left.pexpr, data) || + pexpr_test_eq(a->right.pexpr, b->right.pexpr, data))) { + pexpr_get(a); + return a; + } + + /* general case */ + e = xmalloc(sizeof(*e)); + pexpr_get(a); + pexpr_get(b); + pexpr_construct_or(e, a, b, 1); + + return e; +} + +struct pexpr *pexpr_not(struct pexpr *a, struct cfdata *data) +{ + struct pexpr *retval = pexpr_not_share(a, data); + + pexpr_put(a); + return retval; +} + +/* + * Builds NOT(@a) + */ +struct pexpr *pexpr_not_share(struct pexpr *a, struct cfdata *data) +{ + struct pexpr *ret_val; + + if (a->type == PE_SYMBOL && + a->left.fexpr == data->constants->const_false) + ret_val = pexpr_alloc_symbol(data->constants->const_true); + else if (a->type == PE_SYMBOL && + a->left.fexpr == data->constants->const_true) + ret_val = pexpr_alloc_symbol(data->constants->const_false); + /* eliminate double negation */ + else if (a->type == PE_NOT) { + ret_val = a->left.pexpr; + pexpr_get(ret_val); + } + /* De Morgan */ + else if (a->type == PE_AND) { + ret_val = xmalloc(sizeof(*ret_val)); + pexpr_construct_or(ret_val, + pexpr_not_share(a->left.pexpr, data), + pexpr_not_share(a->right.pexpr, data), 1); + } else if (a->type == PE_OR) { + ret_val = xmalloc(sizeof(*ret_val)); + pexpr_construct_and(ret_val, + pexpr_not_share(a->left.pexpr, data), + pexpr_not_share(a->right.pexpr, data), 1); + } else { + ret_val = xmalloc(sizeof(*ret_val)); + pexpr_get(a); + pexpr_construct_not(ret_val, a, 1); + } + + return ret_val; +} + +struct pexpr *pexpr_implies(struct pexpr *a, struct pexpr *b, + struct cfdata *data, enum pexpr_move move) +{ + return pexpr_move_wrapper(a, b, data, move, pexpr_implies_share); +} + +/* + * macro to construct a pexpr for "A implies B" + */ +struct pexpr *pexpr_implies_share(struct pexpr *a, struct pexpr *b, + struct cfdata *data) +{ + /* A => B -> True if A == B */ + if (a == b || pexpr_test_eq(a, b, data)) + return pexpr_alloc_symbol(data->constants->const_true); + + /* (A => B && C) -> (A => C) if A == B */ + if (b->type == PE_AND && pexpr_test_eq(a, b->left.pexpr, data)) + return pexpr_implies_share(a, b->right.pexpr, data); + /* (A => B && C) -> (A => B) if A == C */ + if (b->type == PE_AND && pexpr_test_eq(a, b->right.pexpr, data)) + return pexpr_implies_share(a, b->left.pexpr, data); + + /* (A => B || C) -> True if (A == B || A == C) */ + if (b->type == PE_OR && (pexpr_test_eq(a, b->left.pexpr, data) || + pexpr_test_eq(a, b->right.pexpr, data))) + return pexpr_alloc_symbol(data->constants->const_true); + + /* (A && B => C) -> True if (A == C || B == C) */ + if (a->type == PE_AND && (pexpr_test_eq(a->left.pexpr, b, data) || + pexpr_test_eq(a->right.pexpr, b, data))) + return pexpr_alloc_symbol(data->constants->const_true); + + return pexpr_or(pexpr_not_share(a, data), b, data, PEXPR_ARG1); +} + +/* + * check whether a pexpr is in CNF + */ +bool pexpr_is_cnf(struct pexpr *e) +{ + if (!e) + return false; + + switch (e->type) { + case PE_SYMBOL: + return true; + case PE_AND: + return false; + case PE_OR: + return pexpr_is_cnf(e->left.pexpr) && + pexpr_is_cnf(e->right.pexpr); + case PE_NOT: + return e->left.pexpr->type == PE_SYMBOL; + } + + return false; +} + +/* + * check whether a pexpr is in NNF + */ +bool pexpr_is_nnf(struct pexpr *e) +{ + if (!e) + return false; + + switch (e->type) { + case PE_SYMBOL: + return true; + case PE_AND: + case PE_OR: + return pexpr_is_nnf(e->left.pexpr) && + pexpr_is_nnf(e->right.pexpr); + case PE_NOT: + return e->left.pexpr->type == PE_SYMBOL; + } + + return false; +} + +/* + * return fexpr_both for a symbol + */ +struct pexpr *sym_get_fexpr_both(struct symbol *sym, struct cfdata *data) +{ + return sym->type == S_TRISTATE ? + pexpr_or(pexpr_alloc_symbol(sym->fexpr_m), + pexpr_alloc_symbol(sym->fexpr_y), data, + PEXPR_ARGX) : + pexpr_alloc_symbol(sym->fexpr_y); +} + +/* + * return fexpr_sel_both for a symbol + */ +struct pexpr *sym_get_fexpr_sel_both(struct symbol *sym, struct cfdata *data) +{ + if (!sym->rev_dep.expr) + return pexpr_alloc_symbol(data->constants->const_false); + + return sym->type == S_TRISTATE ? + pexpr_or(pexpr_alloc_symbol(sym->fexpr_sel_m), + pexpr_alloc_symbol(sym->fexpr_sel_y), data, + PEXPR_ARGX) : + pexpr_alloc_symbol(sym->fexpr_sel_y); +} + +/* + * check, if the fexpr is a symbol, a True/False-constant, a literal symbolizing + * a non-boolean or a choice symbol + */ +bool fexpr_is_symbol(struct fexpr *e) +{ + return e->type == FE_SYMBOL || e->type == FE_FALSE || + e->type == FE_TRUE || e->type == FE_NONBOOL || + e->type == FE_CHOICE || e->type == FE_SELECT || + e->type == FE_NPC; +} + +/* + * check whether a pexpr is a symbol or a negated symbol + */ +bool pexpr_is_symbol(struct pexpr *e) +{ + return e->type == PE_SYMBOL || + (e->type == PE_NOT && e->left.pexpr->type == PE_SYMBOL); +} + +/* + * check whether the fexpr is a constant (true/false) + */ +bool fexpr_is_constant(struct fexpr *e, struct cfdata *data) +{ + return e == data->constants->const_true || + e == data->constants->const_false; +} + +/* + * add a fexpr to the satmap + */ +void fexpr_add_to_satmap(struct fexpr *e, struct cfdata *data) +{ + if (e->satval >= data->satmap_size) { + data->satmap = xrealloc(data->satmap, + data->satmap_size * 2 * + sizeof(*data->satmap)); + data->satmap_size *= 2; + } + + data->satmap[e->satval] = e; +} + +/* + * print a fexpr + */ +void fexpr_print(char *tag, struct fexpr *e) +{ + if (!e) + return; + + printf("%s: %s\n", tag, str_get(&e->name)); +} + +/* + * write an fexpr into a string (format needed for testing) + */ +void fexpr_as_char(struct fexpr *e, struct gstr *s) +{ + if (!e) + return; + + switch (e->type) { + case FE_SYMBOL: + case FE_CHOICE: + case FE_SELECT: + case FE_NPC: + case FE_NONBOOL: + str_append(s, "definedEx("); + str_append(s, str_get(&e->name)); + str_append(s, ")"); + return; + case FE_FALSE: + str_append(s, "0"); + return; + case FE_TRUE: + str_append(s, "1"); + return; + default: + return; + } +} + +/* + * write a pexpr into a string + */ +void pexpr_as_char(struct pexpr *e, struct gstr *s, int parent, + struct cfdata *data) +{ + if (!e) + return; + + switch (e->type) { + case PE_SYMBOL: + if (e->left.fexpr == data->constants->const_false) { + str_append(s, "0"); + return; + } + if (e->left.fexpr == data->constants->const_true) { + str_append(s, "1"); + return; + } + str_append(s, "definedEx("); + str_append(s, str_get(&e->left.fexpr->name)); + str_append(s, ")"); + return; + case PE_AND: + if (parent != PE_AND) + str_append(s, "("); + pexpr_as_char(e->left.pexpr, s, PE_AND, data); + str_append(s, " && "); + pexpr_as_char(e->right.pexpr, s, PE_AND, data); + if (parent != PE_AND) + str_append(s, ")"); + return; + case PE_OR: + if (parent != PE_OR) + str_append(s, "("); + pexpr_as_char(e->left.pexpr, s, PE_OR, data); + str_append(s, " || "); + pexpr_as_char(e->right.pexpr, s, PE_OR, data); + if (parent != PE_OR) + str_append(s, ")"); + return; + case PE_NOT: + str_append(s, "!"); + pexpr_as_char(e->left.pexpr, s, PE_NOT, data); + return; + } +} + +/* + * write a pexpr into a string + */ +void pexpr_as_char_short(struct pexpr *e, struct gstr *s, int parent) +{ + if (!e) + return; + + switch (e->type) { + case PE_SYMBOL: + str_append(s, str_get(&e->left.fexpr->name)); + return; + case PE_AND: + if (parent != PE_AND) + str_append(s, "("); + pexpr_as_char_short(e->left.pexpr, s, PE_AND); + str_append(s, " && "); + pexpr_as_char_short(e->right.pexpr, s, PE_AND); + if (parent != PE_AND) + str_append(s, ")"); + return; + case PE_OR: + if (parent != PE_OR) + str_append(s, "("); + pexpr_as_char_short(e->left.pexpr, s, PE_OR); + str_append(s, " || "); + pexpr_as_char_short(e->right.pexpr, s, PE_OR); + if (parent != PE_OR) + str_append(s, ")"); + return; + case PE_NOT: + str_append(s, "!"); + pexpr_as_char_short(e->left.pexpr, s, PE_NOT); + return; + } +} + +/* + * check whether a pexpr contains a specific fexpr + */ +bool pexpr_contains_fexpr(struct pexpr *e, struct fexpr *fe) +{ + if (!e) + return false; + + switch (e->type) { + case PE_SYMBOL: + return e->left.fexpr->satval == fe->satval; + case PE_AND: + case PE_OR: + return pexpr_contains_fexpr(e->left.pexpr, fe) || + pexpr_contains_fexpr(e->right.pexpr, fe); + case PE_NOT: + return e->left.pexpr->left.fexpr->satval == fe->satval; + } + + return false; +} + +/* + * print a fexpr_list + */ +void fexpr_list_print(char *title, struct fexpr_list *list) +{ + struct fexpr_node *node; + bool first = true; + + printf("%s: [", title); + + CF_LIST_FOR_EACH(node, list, fexpr) { + if (first) + first = false; + else + printf(", "); + printf("%s", str_get(&node->elem->name)); + } + + printf("]\n"); +} + +/* + * print a fexl_list + */ +void fexl_list_print(char *title, struct fexl_list *list) +{ + struct fexl_node *node; + + printf("%s:\n", title); + + CF_LIST_FOR_EACH(node, list, fexl) + fexpr_list_print(":", node->elem); +} + +/* + * print a pexpr_list + */ +void pexpr_list_print(char *title, struct pexpr_list *list) +{ + struct pexpr_node *node; + + printf("%s: [", title); + + CF_LIST_FOR_EACH(node, list, pexpr) { + pexpr_print_util(node->elem, -1); + if (node->node.next != &list->list) + printf(", "); + } + + printf("]\n"); +} + +/* + * free an defm_list (and pexpr_put the conditions of the maps and free the + * node->element's) + */ +void defm_list_destruct(struct defm_list *list) +{ + struct defm_node *node; + + CF_LIST_FOR_EACH(node, list, defm) + pexpr_put(node->elem->e); + CF_LIST_FREE(list, defm); +} + +/* + * free a pexpr_list (and pexpr_put the elements) + */ +void pexpr_list_free_put(struct pexpr_list *list) +{ + struct pexpr_node *node; + + CF_LIST_FOR_EACH(node, list, pexpr) + pexpr_put(node->elem); + CF_LIST_FREE(list, pexpr); +} + +/* + * simplify a pexpr in-place + * pexpr && False -> False + * pexpr && True -> pexpr + * pexpr || False -> pexpr + * pexpr || True -> True + */ +static void pexpr_eliminate_yn(struct pexpr *e, struct cfdata *data) +{ + struct pexpr *tmp; + unsigned int ref_count; + + if (!e) + return; + + switch (e->type) { + case PE_AND: + pexpr_eliminate_yn(e->left.pexpr, data); + pexpr_eliminate_yn(e->right.pexpr, data); + if (e->left.pexpr->type == PE_SYMBOL) { + if (e->left.pexpr->left.fexpr == + data->constants->const_false) { + pexpr_put(e->left.pexpr); + pexpr_put(e->right.pexpr); + ref_count = e->ref_count; + pexpr_construct_sym( + e, data->constants->const_false, + ref_count); + return; + } else if (e->left.pexpr->left.fexpr == + data->constants->const_true) { + pexpr_put(e->left.pexpr); + tmp = e->right.pexpr; + ref_count = e->ref_count; + pexpr_shallow_copy(e, tmp, ref_count); + pexpr_put(tmp); + return; + } + } + if (e->right.pexpr->type == PE_SYMBOL) { + if (e->right.pexpr->left.fexpr == + data->constants->const_false) { + pexpr_put(e->left.pexpr); + pexpr_put(e->right.pexpr); + ref_count = e->ref_count; + pexpr_construct_sym( + e, data->constants->const_false, + ref_count); + return; + } else if (e->right.pexpr->left.fexpr == + data->constants->const_true) { + pexpr_put(e->right.pexpr); + tmp = e->left.pexpr; + ref_count = e->ref_count; + pexpr_shallow_copy(e, tmp, ref_count); + pexpr_put(tmp); + return; + } + } + break; + case PE_OR: + pexpr_eliminate_yn(e->left.pexpr, data); + pexpr_eliminate_yn(e->right.pexpr, data); + if (e->left.pexpr->type == PE_SYMBOL) { + if (e->left.pexpr->left.fexpr == + data->constants->const_false) { + pexpr_put(e->left.pexpr); + tmp = e->right.pexpr; + ref_count = e->ref_count; + pexpr_shallow_copy(e, tmp, ref_count); + pexpr_put(tmp); + return; + } else if (e->left.pexpr->left.fexpr == + data->constants->const_true) { + pexpr_put(e->left.pexpr); + pexpr_put(e->right.pexpr); + ref_count = e->ref_count; + pexpr_construct_sym( + e, data->constants->const_true, + ref_count); + return; + } + } + if (e->right.pexpr->type == PE_SYMBOL) { + if (e->right.pexpr->left.fexpr == + data->constants->const_false) { + pexpr_put(e->right.pexpr); + tmp = e->left.pexpr; + ref_count = e->ref_count; + pexpr_shallow_copy(e, tmp, ref_count); + pexpr_put(tmp); + return; + } else if (e->right.pexpr->left.fexpr == + data->constants->const_true) { + pexpr_put(e->left.pexpr); + pexpr_put(e->right.pexpr); + ref_count = e->ref_count; + pexpr_construct_sym(e, + data->constants->const_true, + ref_count); + return; + } + } + default: + break; + } +} + +static void pexpr_shallow_copy(struct pexpr *dest, struct pexpr *org, + unsigned int ref_count) +{ + struct pexpr inter; + + inter.type = org->type; + inter.left = org->left; + inter.right = org->right; + if (org->type == PE_OR || org->type == PE_AND) { + pexpr_get(org->left.pexpr); + pexpr_get(org->right.pexpr); + } else if (org->type == PE_NOT) { + pexpr_get(org->left.pexpr); + } + inter.ref_count = ref_count; + *dest = inter; +} + +/* + * copy a pexpr + */ +struct pexpr *pexpr_deep_copy(const struct pexpr *org) +{ + struct pexpr *e; + + if (!org) + return NULL; + + e = xmalloc(sizeof(*org)); + memcpy(e, org, sizeof(*org)); + e->ref_count = 1; + switch (org->type) { + case PE_SYMBOL: + e->left = org->left; + break; + case PE_AND: + case PE_OR: + e->left.pexpr = pexpr_deep_copy(org->left.pexpr); + e->right.pexpr = pexpr_deep_copy(org->right.pexpr); + break; + case PE_NOT: + e->left.pexpr = pexpr_deep_copy(org->left.pexpr); + break; + } + + return e; +} + +/* + * free a pexpr + */ +void pexpr_free_depr(struct pexpr *e) +{ + if (!e) + return; + + switch (e->type) { + case PE_SYMBOL: + break; + case PE_AND: + case PE_OR: + pexpr_free_depr(e->left.pexpr); + pexpr_free_depr(e->right.pexpr); + break; + case PE_NOT: + pexpr_free_depr(e->left.pexpr); + break; + } + + free(e); +} + +/* + * Increments ref_count and returns @e + */ +struct pexpr *pexpr_get(struct pexpr *e) +{ + ++e->ref_count; + return e; +} + +/* + * Decrements ref_count and if it becomes 0, it recursively puts the references + * to its children and calls ``free(e)``. If @e == NULL, it does nothing. + */ +void pexpr_put(struct pexpr *e) +{ + if (!e) + return; + + if (e->ref_count == 0) { + printd("Invalid call to %s - ref_count is zero\n", __func__); + return; + } + + --e->ref_count; + if (e->ref_count > 0) + return; + + switch (e->type) { + case PE_SYMBOL: + break; + case PE_AND: + case PE_OR: + pexpr_put(e->left.pexpr); + pexpr_put(e->right.pexpr); + break; + case PE_NOT: + pexpr_put(e->left.pexpr); + break; + } + + free(e); +} + +/* + * calls pexpr_put for a NULL-terminated array of struct pexpr * + */ +void _pexpr_put_list(struct pexpr **es) +{ + for (; *es != NULL; es++) + pexpr_put(*es); +} + +#define e1 (*ep1) +#define e2 (*ep2) +/* + * pexpr_eliminate_eq() helper + */ +static void __pexpr_eliminate_eq(enum pexpr_type type, struct pexpr **ep1, + struct pexpr **ep2, struct cfdata *data) +{ + /* recurse down to the leaves */ + if (e1->type == type) { + __pexpr_eliminate_eq(type, &e1->left.pexpr, &e2, data); + __pexpr_eliminate_eq(type, &e1->right.pexpr, &e2, data); + return; + } + if (e2->type == type) { + __pexpr_eliminate_eq(type, &e1, &e2->left.pexpr, data); + __pexpr_eliminate_eq(type, &e1, &e2->right.pexpr, data); + return; + } + + /* e1 and e2 are leaves. Compare them. */ + if (e1->type == PE_SYMBOL && e2->type == PE_SYMBOL && + e1->left.fexpr->satval == e2->left.fexpr->satval && + (e1->left.fexpr == data->constants->const_true || + e2->left.fexpr == data->constants->const_false)) + return; + if (!pexpr_test_eq(e1, e2, data)) + return; + + /* e1 and e2 are equal leaves. Prepare them for elimination. */ + trans_count++; + pexpr_put(e1); + pexpr_put(e2); + switch (type) { + case PE_AND: + e1 = pexpr_alloc_symbol(data->constants->const_true); + e2 = pexpr_alloc_symbol(data->constants->const_true); + break; + case PE_OR: + e1 = pexpr_alloc_symbol(data->constants->const_false); + e2 = pexpr_alloc_symbol(data->constants->const_false); + break; + default: + break; + } +} + +/* + * rewrite pexpr ep1 and ep2 to remove operands common to both + */ +static void pexpr_eliminate_eq(struct pexpr **ep1, struct pexpr **ep2, + struct cfdata *data) +{ + if (!e1 || !e2) + return; + + switch (e1->type) { + case PE_AND: + case PE_OR: + __pexpr_eliminate_eq(e1->type, ep1, ep2, data); + default: + break; + } + if (e1->type != e2->type) + switch (e2->type) { + case PE_AND: + case PE_OR: + __pexpr_eliminate_eq(e2->type, ep1, ep2, data); + default: + break; + } + pexpr_eliminate_yn(e1, data); + pexpr_eliminate_yn(e2, data); +} +#undef e1 +#undef e2 + +/* + * check whether 2 pexpr are equal + */ +bool pexpr_test_eq(struct pexpr *e1, struct pexpr *e2, struct cfdata *data) +{ + bool res; + int old_count; + + if (!e1 || !e2) + return false; + + if (e1->type != e2->type) + return false; + + switch (e1->type) { + case PE_SYMBOL: + return e1->left.fexpr->satval == e2->left.fexpr->satval; + case PE_AND: + case PE_OR: + e1 = pexpr_deep_copy(e1); + e2 = pexpr_deep_copy(e2); + old_count = trans_count; + pexpr_eliminate_eq(&e1, &e2, data); + res = (e1->type == PE_SYMBOL && e2->type == PE_SYMBOL && + e1->left.fexpr->satval == e2->left.fexpr->satval); + pexpr_put(e1); + pexpr_put(e2); + trans_count = old_count; + return res; + case PE_NOT: + return pexpr_test_eq(e1->left.pexpr, e2->left.pexpr, data); + } + + return false; +} + +/* + * print a pexpr + */ +static void pexpr_print_util(struct pexpr *e, int prevtoken) +{ + if (!e) + return; + + switch (e->type) { + case PE_SYMBOL: + printf("%s", str_get(&e->left.fexpr->name)); + break; + case PE_AND: + if (prevtoken != PE_AND && prevtoken != -1) + printf("("); + pexpr_print_util(e->left.pexpr, PE_AND); + printf(" && "); + pexpr_print_util(e->right.pexpr, PE_AND); + if (prevtoken != PE_AND && prevtoken != -1) + printf(")"); + break; + case PE_OR: + if (prevtoken != PE_OR && prevtoken != -1) + printf("("); + pexpr_print_util(e->left.pexpr, PE_OR); + printf(" || "); + pexpr_print_util(e->right.pexpr, PE_OR); + if (prevtoken != PE_OR && prevtoken != -1) + printf(")"); + break; + case PE_NOT: + printf("!"); + pexpr_print_util(e->left.pexpr, PE_NOT); + break; + } +} +void pexpr_print(char *tag, struct pexpr *e, int prevtoken) +{ + printf("%s: ", tag); + pexpr_print_util(e, prevtoken); + printf("\n"); +} + +/* + * convert a fexpr to a pexpr + */ +struct pexpr *pexpr_alloc_symbol(struct fexpr *fe) +{ + struct pexpr *pe = xmalloc(sizeof(*pe)); + + pexpr_construct_sym(pe, fe, 1); + return pe; +} + +void pexpr_construct_or(struct pexpr *e, struct pexpr *left, + struct pexpr *right, unsigned int ref_count) +{ + e->type = PE_OR; + e->left.pexpr = left; + e->right.pexpr = right; + e->ref_count = ref_count; +} + +void pexpr_construct_and(struct pexpr *e, struct pexpr *left, + struct pexpr *right, unsigned int ref_count) +{ + e->type = PE_AND; + e->left.pexpr = left; + e->right.pexpr = right; + e->ref_count = ref_count; +} + +void pexpr_construct_not(struct pexpr *e, struct pexpr *left, + unsigned int ref_count) +{ + e->type = PE_NOT; + e->left.pexpr = left; + e->right.pexpr = NULL; + e->ref_count = ref_count; +} + +void pexpr_construct_sym(struct pexpr *e, struct fexpr *left, + unsigned int ref_count) +{ + e->type = PE_SYMBOL; + e->left.fexpr = left; + e->right.pexpr = NULL; + e->ref_count = ref_count; +} diff --git a/scripts/kconfig/cf_expr.h b/scripts/kconfig/cf_expr.h new file mode 100644 index 000000000000..894260bcb3b2 --- /dev/null +++ b/scripts/kconfig/cf_expr.h @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Patrick Franz + */ + +#ifndef CF_EXPR_H +#define CF_EXPR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "cf_defs.h" + +/* call pexpr_put for a list of pexpr's */ +#define PEXPR_PUT(...) _pexpr_put_list((struct pexpr *[]){ __VA_ARGS__, NULL }) + +/* create a fexpr */ +struct fexpr *fexpr_create(int satval, enum fexpr_type type, char *name); + +/* create the fexpr for a symbol */ +void sym_create_fexpr(struct symbol *sym, struct cfdata *data); + +struct pexpr *expr_calculate_pexpr_both(struct expr *e, struct cfdata *data); +struct pexpr *expr_calculate_pexpr_y(struct expr *e, struct cfdata *data); +struct pexpr *expr_calculate_pexpr_m(struct expr *e, struct cfdata *data); +struct pexpr *expr_calculate_pexpr_y_and(struct expr *a, struct expr *b, + struct cfdata *data); +struct pexpr *expr_calculate_pexpr_m_and(struct expr *a, struct expr *b, + struct cfdata *data); +struct pexpr *expr_calculate_pexpr_both_and(struct expr *a, struct expr *b, + struct cfdata *data); +struct pexpr *expr_calculate_pexpr_y_or(struct expr *a, struct expr *b, + struct cfdata *data); +struct pexpr *expr_calculate_pexpr_m_or(struct expr *a, struct expr *b, + struct cfdata *data); +struct pexpr *expr_calculate_pexpr_both_or(struct expr *a, struct expr *b, + struct cfdata *data); +struct pexpr *expr_calculate_pexpr_y_not(struct expr *e, struct cfdata *data); +struct pexpr *expr_calculate_pexpr_m_not(struct expr *e, struct cfdata *data); +struct pexpr *expr_calculate_pexpr_y_equals(struct expr *e, + struct cfdata *data); +struct pexpr *expr_calculate_pexpr_y_unequals(struct expr *e, + struct cfdata *data); +struct pexpr *expr_calculate_pexpr_y_comp(struct expr *e, struct cfdata *data); + +/* macro to create a pexpr of type AND */ +struct pexpr *pexpr_and_share(struct pexpr *a, struct pexpr *b, + struct cfdata *data); +struct pexpr *pexpr_and(struct pexpr *a, struct pexpr *b, struct cfdata *data, + enum pexpr_move move); + +/* macro to create a pexpr of type OR */ +struct pexpr *pexpr_or_share(struct pexpr *a, struct pexpr *b, + struct cfdata *data); +struct pexpr *pexpr_or(struct pexpr *a, struct pexpr *b, struct cfdata *data, + enum pexpr_move move); + +/* macro to create a pexpr of type NOT */ +struct pexpr *pexpr_not_share(struct pexpr *a, struct cfdata *data); +struct pexpr *pexpr_not(struct pexpr *a, struct cfdata *data); + +/* check whether a pexpr is in CNF */ +bool pexpr_is_cnf(struct pexpr *e); + +/* check whether a pexpr is in NNF */ +bool pexpr_is_nnf(struct pexpr *e); + +/* return fexpr_both for a symbol */ +struct pexpr *sym_get_fexpr_both(struct symbol *sym, struct cfdata *data); + +/* return fexpr_sel_both for a symbol */ +struct pexpr *sym_get_fexpr_sel_both(struct symbol *sym, struct cfdata *data); + +/* create the fexpr of a non-boolean symbol for a specific value */ +struct fexpr *sym_create_nonbool_fexpr(struct symbol *sym, char *value, + struct cfdata *data); + +/* + * return the fexpr of a non-boolean symbol for a specific value, NULL if + * non-existent + */ +struct fexpr *sym_get_nonbool_fexpr(struct symbol *sym, char *value); + +/* + * return the fexpr of a non-boolean symbol for a specific value, if it exists + * otherwise create it + */ +struct fexpr *sym_get_or_create_nonbool_fexpr(struct symbol *sym, char *value, + struct cfdata *data); + +/* macro to construct a pexpr for "A implies B" */ +struct pexpr *pexpr_implies_share(struct pexpr *a, struct pexpr *b, + struct cfdata *data); +struct pexpr *pexpr_implies(struct pexpr *a, struct pexpr *b, + struct cfdata *data, enum pexpr_move move); + +/* check, if the fexpr is a symbol, a True/False-constant, a literal symbolising + * a non-boolean or a choice symbol + */ +bool fexpr_is_symbol(struct fexpr *e); + +/* check whether a pexpr is a symbol or a negated symbol */ +bool pexpr_is_symbol(struct pexpr *e); + +/* check whether the fexpr is a constant (true/false) */ +bool fexpr_is_constant(struct fexpr *e, struct cfdata *data); + +/* add a fexpr to the satmap */ +void fexpr_add_to_satmap(struct fexpr *e, struct cfdata *data); + +/* print an fexpr */ +void fexpr_print(char *tag, struct fexpr *e); + +/* write an fexpr into a string (format needed for testing) */ +void fexpr_as_char(struct fexpr *e, struct gstr *s); + +/* write pn pexpr into a string */ +void pexpr_as_char_short(struct pexpr *e, struct gstr *s, int parent); + +/* write an fexpr into a string (format needed for testing) */ +void pexpr_as_char(struct pexpr *e, struct gstr *s, int parent, + struct cfdata *data); + +/* check whether a pexpr contains a specific fexpr */ +bool pexpr_contains_fexpr(struct pexpr *e, struct fexpr *fe); + +/* print a fexpr_list */ +void fexpr_list_print(char *title, struct fexpr_list *list); + +/* print a fexl_list */ +void fexl_list_print(char *title, struct fexl_list *list); + +/* print a pexpr_list */ +void pexpr_list_print(char *title, struct pexpr_list *list); + +/* free an pexpr_list (and pexpr_put the elements) */ +void pexpr_list_free_put(struct pexpr_list *list); + +/* free a defm_list (and pexpr_put the conditions of the maps) */ +void defm_list_destruct(struct defm_list *list); + +/* check whether 2 pexpr are equal */ +bool pexpr_test_eq(struct pexpr *e1, struct pexpr *e2, struct cfdata *data); + +/* copy a pexpr */ +struct pexpr *pexpr_deep_copy(const struct pexpr *org); + +void pexpr_construct_sym(struct pexpr *e, struct fexpr *left, + unsigned int ref_count); +void pexpr_construct_not(struct pexpr *e, struct pexpr *left, + unsigned int ref_count); +void pexpr_construct_and(struct pexpr *e, struct pexpr *left, + struct pexpr *right, unsigned int ref_count); +void pexpr_construct_or(struct pexpr *e, struct pexpr *left, + struct pexpr *right, unsigned int ref_count); + +/* free a pexpr */ +void pexpr_free_depr(struct pexpr *e); + +/* give up a reference to e. Also see struct pexpr. */ +void pexpr_put(struct pexpr *e); +/* Used by PEXPR_PUT(). Not to be used directly. */ +void _pexpr_put_list(struct pexpr **es); + +/* acquire a reference to e. Also see struct pexpr. */ +struct pexpr *pexpr_get(struct pexpr *e); + +/* print a pexpr */ +void pexpr_print(char *tag, struct pexpr *e, int prevtoken); + +/* convert a fexpr to a pexpr */ +struct pexpr *pexpr_alloc_symbol(struct fexpr *fe); + +#ifdef __cplusplus +} +#endif + +#endif From patchwork Mon Oct 28 03:49:44 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13852974 Received: from mail-ed1-f53.google.com (mail-ed1-f53.google.com [209.85.208.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7798E18C92F; Mon, 28 Oct 2024 03:50:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730087421; cv=none; b=O+8QebY29XWEXCl0X60/4/tN8uM81u294oPhCzKg2lFG1CwsNuDnLpLiZHLjwmn9us24m9P9bHr5hporiFV3509mFuAlU5qjo6re3B/K+Um8z0+x6Q0qcqRPipxmopKBOk/jgZZo5yfb7/FCRQfuqKxljnjB7XzR4hsbKg0dS/I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730087421; c=relaxed/simple; bh=Zqkg69yHB1cp5P56awU1Q/VFo7gtS7GE+Qs1Swc9ftU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=W+d56w/2KX8PAPhQY2I8iB0Bk0a/wo69rM6xNOAZVxoIxIc6jIXiwOKW2YWc3Y5xtXNqoUDNxZs6BLMWLBgHRl7BOuDKEH72mZui554F+PHVsgOBeo3bVRdidX19IiHrXt6UWNSnvYGaW7nHnZoc+HggufvREH8SLExj7es7JDs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=aeQVreMx; arc=none smtp.client-ip=209.85.208.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="aeQVreMx" Received: by mail-ed1-f53.google.com with SMTP id 4fb4d7f45d1cf-5c96df52c52so4822897a12.1; Sun, 27 Oct 2024 20:50:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1730087416; x=1730692216; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=mGi/rT7zC3Lf3ArXtJ8Xyb3CR5sq3UP8RYZtM0KjqRg=; b=aeQVreMxrlexOCkM3/tTdMLxU4MjDcMyLLY2iKTYI2M3CvBSFCwOsk6lg4T4uh94hV hMYUCtK2vbz1TTosVbp7BSKWuLcItMOqCPIifVUoDz3Y/83Vlt+4zyPuHB1O72P6lu4n wzmaTYHuwFQaCVMc8NaGBmZbj7ZEiWyrgeRennXMCgIbWSFKBdKt9Gan8OQdS6fZnMti +KrDB/fFIaX4ZE3V9LvwOVQv70SAVfNJUPigQFec0iZ6xglGuTW8Nl94y66ZCv//QMSE Zx5wcULG3ZEupo0GOyIHFMX6LzQK2llb4vX5ZwqYS8KV8/wuKhOJ5MygTL3Bmc+bGGI3 R63Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1730087416; x=1730692216; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=mGi/rT7zC3Lf3ArXtJ8Xyb3CR5sq3UP8RYZtM0KjqRg=; b=av4LeTQy7DBbpgjerAFWhjgL95xMh08I9ZKaaQOfzXiGEAdcZFAn+fd9TS2vtyBuo8 XDik4GVWnKGrFsxl0EiRlxL8f5IKrIuBA81d/OQseiWcIk1WHSF7AiMA7WaI0fUvAbla MKZXTMnOo8OmU0b8kyjGzHMn6F4QhgReeeE3eHuwR4mYGdyO2hrQSos7Kqpx+i+wjmyb 7eGJgYZa3UvflaTeEOp0t8UsbsIJeDj01UFw7USMyDDcazhiSc336Gh1XA+ebOfIg08U a9aCcG2gKZOVBnGMIoVhxb89dlV8vzEurDQLw279+rB7C/+ZxqGs9N/uzmejt5Qk8Oos KiAA== X-Forwarded-Encrypted: i=1; AJvYcCX756xv7phPEwjHfgevafyTv6ls8av0n/tDpugB+0K7P+Ehrf6zknXziGQ1zlAZmfyDCjtVK7OhXRoz+dA=@vger.kernel.org X-Gm-Message-State: AOJu0Ywn3qtYPVh2G6tTywxS3jjMlgYjFOtfKMZKn6IrgpQQbE8a5vfG nzzRpQmwsBUhFh9we8CPCWNofc1I7FtggEV28H2MQ5QlywFQKRArxT5oEw== X-Google-Smtp-Source: AGHT+IEkSVWts8YXMvWew1s0/esBBCtYqKJVaLC3EKw/LaEu2K5/Yq7HNRmab8gt7NonaSv+sbkNVA== X-Received: by 2002:a17:907:31cb:b0:a99:5773:3612 with SMTP id a640c23a62f3a-a9de5f6653bmr668735566b.36.1730087415382; Sun, 27 Oct 2024 20:50:15 -0700 (PDT) Received: from localhost.localdomain ([2a02:908:e842:bf20:78b:e59b:2b0:d2e9]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9b30f5932fsm334599366b.168.2024.10.27.20.50.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 27 Oct 2024 20:50:14 -0700 (PDT) From: Ole Schuerks To: linux-kbuild@vger.kernel.org Cc: ole0811sch@gmail.com, jude.gyimah@rub.de, thorsten.berger@rub.de, deltaone@debian.org, jan.sollmann@rub.de, mcgrof@kernel.org, masahiroy@kernel.org, linux-kernel@vger.kernel.org, nathan@kernel.org, nicolas@fjasle.eu Subject: [PATCH v6 06/11] kconfig: Add files for RangeFix Date: Mon, 28 Oct 2024 04:49:44 +0100 Message-Id: <20241028034949.95322-7-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20241028034949.95322-1-ole0811sch@gmail.com> References: <20241028034949.95322-1-ole0811sch@gmail.com> Precedence: bulk X-Mailing-List: linux-kbuild@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The algorithm RangeFix is used to resolve the conflicts. This is the implementation of the algorithm. Co-developed-by: Patrick Franz Signed-off-by: Patrick Franz Co-developed-by: Ibrahim Fayaz Signed-off-by: Ibrahim Fayaz Reviewed-by: Luis Chamberlain Tested-by: Evgeny Groshev Suggested-by: Sarah Nadi Suggested-by: Thorsten Berger Signed-off-by: Thorsten Berger Signed-off-by: Ole Schuerks --- scripts/kconfig/cf_rangefix.c | 1136 +++++++++++++++++++++++++++++++++ scripts/kconfig/cf_rangefix.h | 21 + 2 files changed, 1157 insertions(+) create mode 100644 scripts/kconfig/cf_rangefix.c create mode 100644 scripts/kconfig/cf_rangefix.h diff --git a/scripts/kconfig/cf_rangefix.c b/scripts/kconfig/cf_rangefix.c new file mode 100644 index 000000000000..4c710fcf56a9 --- /dev/null +++ b/scripts/kconfig/cf_rangefix.c @@ -0,0 +1,1136 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Patrick Franz + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "lkc.h" +#include "cf_defs.h" +#include "cf_expr.h" +#include "list.h" +#include "list_types.h" +#include "cf_rangefix.h" +#include "internal.h" +#include "cf_utils.h" +#include "cf_defs.h" + +#define MAX_DIAGNOSES 3 +#define MAX_SECONDS 120 +#define PRINT_UNSAT_CORE true +#define PRINT_DIAGNOSES false +#define PRINT_DIAGNOSIS_FOUND true +#define MINIMISE_DIAGNOSES false +#define MINIMISE_UNSAT_CORE true + +static struct sfl_list *diagnoses_symbol; + +static struct fexl_list *generate_diagnoses(PicoSAT *pico, struct cfdata *data); + +static void add_fexpr_to_constraint_set(struct fexpr_list *C, + struct cfdata *data); +static void set_assumptions(PicoSAT *pico, struct fexpr_list *c, + struct cfdata *data); +static void fexpr_add_assumption(PicoSAT *pico, struct fexpr *e, int satval); +static struct fexpr_list *get_unsat_core_soft(PicoSAT *pico, + struct cfdata *data); +static void minimise_unsat_core(PicoSAT *pico, struct fexpr_list *C, + struct cfdata *data); + +static struct fexpr_list *get_difference(struct fexpr_list *C, + struct fexpr_list *E0); +static bool has_intersection(struct fexpr_list *e, struct fexpr_list *X); +static struct fexpr_list *fexpr_list_union(struct fexpr_list *A, + struct fexpr_list *B); +static struct fexl_list *fexl_list_union(struct fexl_list *A, + struct fexl_list *B); +static bool is_subset_of(struct fexpr_list *A, struct fexpr_list *B); +static void print_unsat_core(struct fexpr_list *list); +static bool diagnosis_contains_fexpr(struct fexpr_list *diagnosis, + struct fexpr *e); +static bool diagnosis_contains_symbol(struct sfix_list *diagnosis, + struct symbol *sym); + +static void print_diagnoses(struct fexl_list *diag); +static void print_diagnoses_symbol(struct sfl_list *diag_sym); + +static struct sfl_list *convert_diagnoses(struct fexl_list *diagnoses, + struct cfdata *data); +static struct sfix_list *convert_diagnosis(struct fexpr_list *diagnosis, + struct cfdata *data); +static struct symbol_fix *symbol_fix_create(struct fexpr *e, + enum symbolfix_type type, + struct fexpr_list *diagnosis); +static struct sfl_list *minimise_diagnoses(PicoSAT *pico, + struct fexl_list *diagnoses, + struct cfdata *data); + +static tristate calculate_new_tri_val(struct fexpr *e, + struct fexpr_list *diagnosis); +static const char *calculate_new_string_value(struct fexpr *e, + struct fexpr_list *diagnosis); +static bool fexpr_list_has_length_1(struct fexpr_list *list); + +/* count assumptions, only used for debugging */ +static unsigned int nr_of_assumptions = 0, nr_of_assumptions_true; + +/* -------------------------------------- */ + +struct sfl_list *rangefix_run(PicoSAT *pico, struct cfdata *data) +{ + clock_t start, end; + double time; + struct fexl_list *diagnoses; + struct fexl_node *node; + + printd("Starting RangeFix...\n"); + printd("Generating diagnoses..."); + + /* generate the diagnoses */ + start = clock(); + diagnoses = generate_diagnoses(pico, data); + end = clock(); + + time = ((double) (end - start)) / CLOCKS_PER_SEC; + printd("Generating diagnoses...done. (%.6f secs.)\n", time); + + if (PRINT_DIAGNOSES) { + printd("Diagnoses (only for debugging):\n"); + print_diagnoses(diagnoses); + printd("\n"); + } + + /* convert diagnoses of fexpr to diagnoses of symbols */ + if (MINIMISE_DIAGNOSES) + diagnoses_symbol = minimise_diagnoses(pico, diagnoses, data); + else + diagnoses_symbol = convert_diagnoses(diagnoses, data); + + printd("\n"); + + CF_LIST_FOR_EACH(node, diagnoses, fexl) + CF_LIST_FREE(node->elem, fexpr); + CF_LIST_FREE(diagnoses, fexl); + + return diagnoses_symbol; +} + +/* + * generate the diagnoses + */ +static struct fexl_list *generate_diagnoses(PicoSAT *pico, struct cfdata *data) +{ + CF_DEF_LIST(C, fexpr); + CF_DEF_LIST(empty_diagnosis, fexpr); + CF_DEF_LIST(E, fexl); + CF_DEF_LIST(R, fexl); + size_t num_diagnoses = 0; + struct fexpr_list *X, *e, *E2; + struct fexl_list *E_R_Union; + clock_t start_t, end_t; + double time_t; + + /* create constraint set C */ + add_fexpr_to_constraint_set(C, data); + + if (PRINT_UNSAT_CORE) + printd("\n"); + + /* init E with an empty diagnosis */ + CF_PUSH_BACK(E, empty_diagnosis, fexl); + + /* start the clock */ + start_t = clock(); + + while (!list_empty(&E->list)) { + /* get random diagnosis */ + struct fexl_node *E0_node = + list_first_entry(&E->list, struct fexl_node, node); + struct fexpr_list *E0 = E0_node->elem; + + /* calculate C\E0 */ + struct fexpr_list *c = get_difference(C, E0); + + struct fexl_node *node, *next; + int res; + + /* set assumptions */ + nr_of_assumptions = 0; + nr_of_assumptions_true = 0; + set_assumptions(pico, c, data); + CF_LIST_FREE(c, fexpr); + + res = picosat_sat(pico, -1); + + if (res == PICOSAT_SATISFIABLE) { + if (PRINT_DIAGNOSIS_FOUND && CFDEBUG) + fexpr_list_print("DIAGNOSIS FOUND", E0); + + list_del(&E0_node->node); + if (!list_empty(&E0->list)) { + CF_PUSH_BACK(R, E0, fexl); + ++num_diagnoses; + } else + CF_LIST_FREE(E0, fexpr); + + if (num_diagnoses >= MAX_DIAGNOSES) + goto DIAGNOSES_FOUND; + + continue; + + } else if (res == PICOSAT_UNSATISFIABLE) { + + } else if (res == PICOSAT_UNKNOWN) { + printd("UNKNOWN\n"); + } else { + perror("Doh."); + } + + /* check elapsed time */ + end_t = clock(); + time_t = ((double) (end_t - start_t)) / CLOCKS_PER_SEC; + if (time_t > (double) MAX_SECONDS) + goto DIAGNOSES_FOUND; + + /* abort and return results if cancelled by user */ + if (stop_rangefix) { + stop_rangefix = false; + goto DIAGNOSES_FOUND; + } + + /* get unsat core from SAT solver */ + X = get_unsat_core_soft(pico, data); + + /* minimise the unsat core */ + if (MINIMISE_UNSAT_CORE) + minimise_unsat_core(pico, X, data); + + if (PRINT_UNSAT_CORE) + print_unsat_core(X); + + list_for_each_entry_safe(node, next, &E->list, node) { + struct fexpr_node *fnode; + + /* get partial diagnosis */ + e = node->elem; + + /* check, if there is an intersection between e and X + * if there is, go to the next partial diagnosis + */ + if (has_intersection(e, X)) + continue; + + /* for each fexpr in the core */ + CF_LIST_FOR_EACH(fnode, X, fexpr) { + struct fexpr *x = fnode->elem; + bool E2_subset_of_E1; + struct fexl_node *lnode; + CF_DEF_LIST(E_without_e, fexl); + CF_DEF_LIST(x_set, fexpr); + struct fexpr_list *E1; + + /* create {x} */ + CF_PUSH_BACK(x_set, x, fexpr); + + /* create E' = e U {x} */ + E1 = fexpr_list_union(e, x_set); + + /* create (E\e) U R */ + CF_LIST_FOR_EACH(lnode, E, fexl) { + if (lnode->elem == e) + continue; + CF_PUSH_BACK(E_without_e, + lnode->elem, fexl); + } + E_R_Union = fexl_list_union(E_without_e, R); + + E2_subset_of_E1 = false; + + /* E" in (E\e) U R */ + CF_LIST_FOR_EACH(lnode, E_R_Union, fexl) { + E2 = lnode->elem; + + /* E" subset of E' ? */ + if (is_subset_of(E2, E1)) { + E2_subset_of_E1 = true; + break; + } + } + + CF_LIST_FREE(E_without_e, fexl); + CF_LIST_FREE(E_R_Union, fexl); + CF_LIST_FREE(x_set, fexpr); + + /* there exists no E" that is a subset of E' */ + if (!E2_subset_of_E1) + CF_PUSH_BACK(E, E1, fexl); + else + CF_LIST_FREE(E1, fexpr); + } + + CF_LIST_FREE(e, fexpr); + + list_del(&node->node); + } + CF_LIST_FREE(X, fexpr); + } + + struct fexl_node *node; +DIAGNOSES_FOUND: + CF_LIST_FREE(C, fexpr); + CF_LIST_FOR_EACH(node, E, fexl) + CF_LIST_FREE(node->elem, fexpr); + CF_LIST_FREE(E, fexl); + + return R; +} + +/* + * add the fexpr to the constraint set C + */ +static void add_fexpr_to_constraint_set(struct fexpr_list *C, + struct cfdata *data) +{ + struct symbol *sym; + + for_all_symbols(sym) { + /* must be a proper symbol */ + if (sym->type == S_UNKNOWN) + continue; + + /* + * don't need the conflict symbols they are handled separately + */ + if (sym_is_sdv(data->sdv_symbols, sym)) + continue; + + /* must have a prompt and a name */ + if (!sym->name || !sym_has_prompt(sym)) + continue; + + if (sym->type == S_BOOLEAN) + CF_PUSH_BACK(C, sym->fexpr_y, fexpr); + else if (sym->type == S_TRISTATE) { + CF_PUSH_BACK(C, sym->fexpr_y, fexpr); + CF_PUSH_BACK(C, sym->fexpr_m, fexpr); + } else if (sym->type == S_INT || sym->type == S_HEX || + sym->type == S_STRING) { + struct fexpr_node *node; + + CF_LIST_FOR_EACH(node, sym->nb_vals, fexpr) + CF_PUSH_BACK(C, node->elem, fexpr); + } else { + perror("Error adding variables to constraint set C."); + } + } +} + +/* + * check whether the fexpr symbolises the no-value-set fexpr for a non-boolean + * symbol + */ +static bool fexpr_is_novalue(struct fexpr *e) +{ + if (!sym_is_nonboolean(e->sym)) + return false; + + return e == + list_first_entry(&e->sym->nb_vals->list, struct fexpr_node, node) + ->elem; +} + +static void set_assumptions_sdv(PicoSAT *pico, struct sdv_list *arr) +{ + struct symbol_dvalue *sdv; + struct sdv_node *node; + struct symbol *sym; + + CF_LIST_FOR_EACH(node, arr, sdv) { + int lit_y; + + sdv = node->elem; + sym = sdv->sym; + + lit_y = sym->fexpr_y->satval; + + if (sym->type == S_BOOLEAN) { + switch (sdv->tri) { + case yes: + picosat_assume(pico, lit_y); + sym->fexpr_y->assumption = true; + nr_of_assumptions_true++; + break; + case no: + picosat_assume(pico, -lit_y); + sym->fexpr_y->assumption = false; + break; + case mod: + perror("Should not happen.\n"); + } + nr_of_assumptions++; + } else if (sym->type == S_TRISTATE) { + int lit_m = sym->fexpr_m->satval; + + switch (sdv->tri) { + case yes: + picosat_assume(pico, lit_y); + sym->fexpr_y->assumption = true; + picosat_assume(pico, -lit_m); + sym->fexpr_m->assumption = false; + nr_of_assumptions_true++; + break; + case mod: + picosat_assume(pico, -lit_y); + sym->fexpr_y->assumption = false; + picosat_assume(pico, lit_m); + sym->fexpr_m->assumption = true; + nr_of_assumptions_true++; + break; + case no: + picosat_assume(pico, -lit_y); + sym->fexpr_y->assumption = false; + picosat_assume(pico, -lit_m); + sym->fexpr_y->assumption = false; + } + nr_of_assumptions += 2; + } + } +} + +/* + * set the assumptions for the next run of Picosat + */ +static void set_assumptions(PicoSAT *pico, struct fexpr_list *c, + struct cfdata *data) +{ + struct fexpr_node *node; + + CF_LIST_FOR_EACH(node, c, fexpr) + fexpr_add_assumption(pico, node->elem, node->elem->satval); + + /* set assumptions for the conflict-symbols */ + set_assumptions_sdv(pico, data->sdv_symbols); +} + +/* + * set the assumtption for a fexpr for the next run of Picosat + */ +static void fexpr_add_assumption(PicoSAT *pico, struct fexpr *e, int satval) +{ + struct symbol *sym = e->sym; + + if (sym->type == S_BOOLEAN) { + int tri_val = sym_get_tristate_value(sym); + + if (tri_val == yes) { + picosat_assume(pico, satval); + e->assumption = true; + nr_of_assumptions_true++; + } else { + picosat_assume(pico, -satval); + e->assumption = false; + } + nr_of_assumptions++; + } + + if (sym->type == S_TRISTATE) { + int tri_val = sym_get_tristate_value(sym); + + if (e->tri == yes) { + if (tri_val == yes) { + picosat_assume(pico, satval); + e->assumption = true; + nr_of_assumptions_true++; + } else { + picosat_assume(pico, -satval); + e->assumption = false; + } + } else if (e->tri == mod) { + if (tri_val == mod) { + picosat_assume(pico, satval); + e->assumption = true; + nr_of_assumptions_true++; + } else { + picosat_assume(pico, -satval); + e->assumption = false; + } + } + nr_of_assumptions++; + } + + if (sym->type == S_INT || sym->type == S_HEX || sym->type == S_STRING) { + + char *string_val = (char *) sym_get_string_value(sym); + + if (sym->type == S_STRING && !strcmp(string_val, "")) + return; + + /* check, if e symbolises the no-value-set fexpr */ + if (fexpr_is_novalue(e)) { + if (!sym_nonbool_has_value_set(sym)) { + picosat_assume(pico, satval); + e->assumption = true; + nr_of_assumptions_true++; + } else { + picosat_assume(pico, -satval); + e->assumption = false; + } + } + /* check whena string-symbol has value "" */ + else if (sym->type == S_STRING && !strcmp(string_val, "")) { + if (sym_nonbool_has_value_set(sym)) { + picosat_assume(pico, satval); + e->assumption = true; + nr_of_assumptions_true++; + } else { + picosat_assume(pico, -satval); + e->assumption = false; + } + } else { + if (!strcmp(str_get(&e->nb_val), string_val) && + sym_nonbool_has_value_set(sym)) { + picosat_assume(pico, satval); + e->assumption = true; + nr_of_assumptions_true++; + } else { + picosat_assume(pico, -satval); + e->assumption = false; + } + } + nr_of_assumptions++; + } +} + +/* + * get the unsatisfiable soft constraints from the last run of Picosat + */ +static struct fexpr_list *get_unsat_core_soft(PicoSAT *pico, + struct cfdata *data) +{ + CF_DEF_LIST(ret, fexpr); + struct fexpr *e; + + int lit; + const int *i = picosat_failed_assumptions(pico); + + lit = abs(*i++); + + while (lit != 0) { + e = data->satmap[lit]; + + if (!sym_is_sdv(data->sdv_symbols, e->sym)) + CF_PUSH_BACK(ret, e, fexpr); + + lit = abs(*i++); + } + + return ret; +} + +/* + * minimise the unsat core C + */ +static void minimise_unsat_core(PicoSAT *pico, struct fexpr_list *C, + struct cfdata *data) +{ + struct fexpr_node *node, *tmp; + + /* no need to check further */ + if (fexpr_list_has_length_1(C)) + return; + + list_for_each_entry_safe(node, tmp, &C->list, node) { + CF_DEF_LIST(c_set, fexpr); + struct fexpr_list *t; + int res; + + if (fexpr_list_has_length_1(C)) + return; + + /* create C\c */ + CF_PUSH_BACK(c_set, node->elem, fexpr); + t = get_difference(C, c_set); + + /* invoke PicoSAT */ + set_assumptions(pico, t, data); + + res = picosat_sat(pico, -1); + + if (res == PICOSAT_UNSATISFIABLE) { + list_del(&node->node); + free(node); + } + + CF_LIST_FREE(c_set, fexpr); + CF_LIST_FREE(t, fexpr); + } +} + + +/* + * Calculate C\E0 + */ +static struct fexpr_list *get_difference(struct fexpr_list *C, + struct fexpr_list *E0) +{ + CF_DEF_LIST(ret, fexpr); + struct fexpr_node *node1, *node2; + + CF_LIST_FOR_EACH(node1, C, fexpr) { + bool found = false; + + CF_LIST_FOR_EACH(node2, E0, fexpr) { + if (node1->elem->satval == node2->elem->satval) { + found = true; + break; + } + } + if (!found) + CF_PUSH_BACK(ret, node1->elem, fexpr); + } + + return ret; +} + +/* + * check, if there is an intersection between e and X + */ +static bool has_intersection(struct fexpr_list *e, struct fexpr_list *X) +{ + struct fexpr_node *node1, *node2; + + CF_LIST_FOR_EACH(node1, e, fexpr) + CF_LIST_FOR_EACH(node2, X, fexpr) + if (node1->elem->satval == node2->elem->satval) + return true; + + return false; +} + +/* + * get the union of 2 fexpr_list + */ +static struct fexpr_list *fexpr_list_union(struct fexpr_list *A, + struct fexpr_list *B) +{ + struct fexpr_list *ret = CF_LIST_COPY(A, fexpr); + struct fexpr_node *node1, *node2; + bool found; + + CF_LIST_FOR_EACH(node2, B, fexpr) { + found = false; + CF_LIST_FOR_EACH(node1, A, fexpr) { + if (node2->elem->satval == node1->elem->satval) { + found = true; + break; + } + } + if (!found) + CF_PUSH_BACK(ret, node2->elem, fexpr); + } + + return ret; +} + +/* + * get the union of 2 fexl_list + */ +static struct fexl_list *fexl_list_union(struct fexl_list *A, + struct fexl_list *B) +{ + struct fexl_list *ret = CF_LIST_COPY(A, fexl); + struct fexl_node *node1, *node2; + bool found; + + CF_LIST_FOR_EACH(node2, B, fexl) { + found = false; + CF_LIST_FOR_EACH(node1, A, fexl) { + if (node2->elem == node1->elem) { + found = true; + break; + } + } + if (!found) + CF_PUSH_BACK(ret, node2->elem, fexl); + } + + return ret; +} + +/* + * check, whether A is a subset of B + */ +static bool is_subset_of(struct fexpr_list *A, struct fexpr_list *B) +{ + struct fexpr_node *node1, *node2; + bool found; + + CF_LIST_FOR_EACH(node1, A, fexpr) { + found = false; + CF_LIST_FOR_EACH(node2, B, fexpr) { + if (node1->elem->satval == node2->elem->satval) { + found = true; + break; + } + } + if (!found) + return false; + } + + return true; +} + +/* + * print an unsat core + */ +static void print_unsat_core(struct fexpr_list *list) +{ + struct fexpr_node *node; + bool first = true; + + printd("Unsat core: ["); + + CF_LIST_FOR_EACH(node, list, fexpr) { + if (first) + first = false; + else + printd(", "); + printd("%s", str_get(&node->elem->name)); + printd(" <%s>", node->elem->assumption == true ? "T" : "F"); + } + + printd("]\n"); +} + + +/* + * check if a diagnosis contains a fexpr + */ +static bool diagnosis_contains_fexpr(struct fexpr_list *diagnosis, + struct fexpr *e) +{ + struct fexpr_node *node; + + CF_LIST_FOR_EACH(node, diagnosis, fexpr) + if (node->elem->satval == e->satval) + return true; + + return false; +} + +/* + * check if a diagnosis contains a symbol + */ +static bool diagnosis_contains_symbol(struct sfix_list *diagnosis, + struct symbol *sym) +{ + struct sfix_node *node; + + CF_LIST_FOR_EACH(node, diagnosis, sfix) + if (sym == node->elem->sym) + return true; + + return false; +} + +/* + * print the diagnoses of type fexpr_list + */ +static void print_diagnoses(struct fexl_list *diag) +{ + struct fexl_node *lnode; + unsigned int i = 1; + + CF_LIST_FOR_EACH(lnode, diag, fexl) { + struct fexpr_node *node; + bool first = true; + + printd("%d: [", i++); + CF_LIST_FOR_EACH(node, lnode->elem, fexpr) { + char *new_val = node->elem->assumption ? "false" : + "true"; + + if (first) + first = false; + else + printd(", "); + printd("%s => %s", str_get(&node->elem->name), new_val); + } + printd("]\n"); + } +} + +/* + * print a single diagnosis of type symbol_fix + */ +void print_diagnosis_symbol(struct sfix_list *diag_sym) +{ + struct symbol_fix *fix; + struct sfix_node *node; + + printd("["); + + CF_LIST_FOR_EACH(node, diag_sym, sfix) { + fix = node->elem; + + if (fix->type == SF_BOOLEAN) + printd("%s => %s", fix->sym->name, + tristate_get_char(fix->tri)); + else if (fix->type == SF_NONBOOLEAN) + printd("%s => %s", fix->sym->name, + str_get(&fix->nb_val)); + else + perror("NB not yet implemented."); + + if (node->node.next != &diag_sym->list) + printd(", "); + } + printd("]\n"); +} + +/* + * print the diagnoses of type symbol_fix + */ +static void print_diagnoses_symbol(struct sfl_list *diag_sym) +{ + struct sfl_node *arr; + unsigned int i = 1; + + CF_LIST_FOR_EACH(arr, diag_sym, sfl) { + printd("%d: ", i++); + print_diagnosis_symbol(arr->elem); + } +} + +/* + * convert a single diagnosis of fexpr into a diagnosis of symbols + */ +static struct sfix_list *convert_diagnosis(struct fexpr_list *diagnosis, + struct cfdata *data) +{ + CF_DEF_LIST(diagnosis_symbol, sfix); + struct fexpr *e; + struct symbol_fix *fix; + struct symbol_dvalue *sdv; + struct sdv_node *snode; + struct fexpr_node *fnode; + + /* set the values for the conflict symbols */ + CF_LIST_FOR_EACH(snode, data->sdv_symbols, sdv) { + sdv = snode->elem; + fix = xmalloc(sizeof(*fix)); + fix->sym = sdv->sym; + fix->type = SF_BOOLEAN; + fix->tri = sdv->tri; + CF_PUSH_BACK(diagnosis_symbol, fix, sfix); + } + + CF_LIST_FOR_EACH(fnode, diagnosis, fexpr) { + enum symbolfix_type type; + + e = fnode->elem; + + /* diagnosis already contains symbol, so continue */ + if (diagnosis_contains_symbol(diagnosis_symbol, e->sym)) + continue; + + if (sym_is_boolean(e->sym)) + type = SF_BOOLEAN; + else if (sym_is_nonboolean(e->sym)) + type = SF_NONBOOLEAN; + else + type = SF_DISALLOWED; + fix = symbol_fix_create(e, type, diagnosis); + + CF_PUSH_BACK(diagnosis_symbol, fix, sfix); + } + + return diagnosis_symbol; +} + +/* + * convert the diagnoses of fexpr into diagnoses of symbols + * it is easier to handle symbols when applying fixes + */ +static struct sfl_list *convert_diagnoses(struct fexl_list *diag_arr, + struct cfdata *data) +{ + struct fexl_node *lnode; + + diagnoses_symbol = CF_LIST_INIT(sfl); + + CF_LIST_FOR_EACH(lnode, diag_arr, fexl) { + struct sfix_list *fix = convert_diagnosis(lnode->elem, data); + + CF_PUSH_BACK(diagnoses_symbol, fix, sfl); + } + + return diagnoses_symbol; +} + +/* + * create a symbol_fix given a fexpr + */ +static struct symbol_fix *symbol_fix_create(struct fexpr *e, + enum symbolfix_type type, + struct fexpr_list *diagnosis) +{ + struct symbol_fix *fix = malloc(sizeof(struct symbol_fix)); + + fix->sym = e->sym; + fix->type = type; + + switch (type) { + case SF_BOOLEAN: + fix->tri = calculate_new_tri_val(e, diagnosis); + break; + case SF_NONBOOLEAN: + fix->nb_val = str_new(); + str_append(&fix->nb_val, + calculate_new_string_value(e, diagnosis)); + break; + default: + perror("Illegal symbolfix_type.\n"); + } + + return fix; +} + +/* + * remove symbols from the diagnosis, which will be set automatically: + * 1. symbol gets selected + * 2. choice symbol gets enabled/disabled automatically + * 3. symbol uses a default value + */ +static struct sfl_list *minimise_diagnoses(PicoSAT *pico, + struct fexl_list *diagnoses, + struct cfdata *data) +{ + clock_t start, end; + double time; + struct fexpr_list *d; + struct sfix_list *diagnosis_symbol; + CF_DEF_LIST(diagnoses_symbol, sfl); + struct fexpr *e; + int satval, deref = 0; + struct symbol_fix *fix; + struct fexl_node *flnode; + CF_DEF_LIST(C, fexpr); + + printd("Minimising diagnoses..."); + + start = clock(); + + /* create soft constraint set C */ + add_fexpr_to_constraint_set(C, data); + + CF_LIST_FOR_EACH(flnode, diagnoses, fexl) { + struct fexpr_node *fnode; + struct sfix_node *snode, *snext; + struct fexpr_list *C_without_d; + int res; + + d = flnode->elem; + + /* + * set assumptions for those symbols that don't need to be + * changed + */ + C_without_d = get_difference(C, d); + set_assumptions(pico, C_without_d, data); + CF_LIST_FREE(C_without_d, fexpr); + CF_LIST_FREE(C, fexpr); + + + /* flip the assumptions from the diagnosis */ + CF_LIST_FOR_EACH(fnode, d, fexpr) { + e = fnode->elem; + satval = e->assumption ? -(e->satval) : e->satval; + picosat_assume(pico, satval); + } + + res = picosat_sat(pico, -1); + if (res != PICOSAT_SATISFIABLE) + perror("Diagnosis not satisfiable (minimise)."); + + diagnosis_symbol = convert_diagnosis(d, data); + + /* check if symbol gets selected */ + list_for_each_entry_safe(snode, snext, &diagnosis_symbol->list, + node) { + fix = snode->elem; + + /* symbol is never selected, continue */ + if (!fix->sym->fexpr_sel_y) + continue; + + /* check, whether the symbol was selected anyway */ + if (fix->sym->type == S_BOOLEAN && fix->tri == yes) + deref = picosat_deref( + pico, fix->sym->fexpr_sel_y->satval); + else if (fix->sym->type == S_TRISTATE && + fix->tri == yes) + deref = picosat_deref( + pico, fix->sym->fexpr_sel_y->satval); + else if (fix->sym->type == S_TRISTATE && + fix->tri == mod) + deref = picosat_deref( + pico, fix->sym->fexpr_sel_m->satval); + + if (deref == 1) + list_del(&snode->node); + else + deref = 0; + } + CF_PUSH_BACK(diagnoses_symbol, diagnosis_symbol, sfl); + } + + end = clock(); + time = ((double) (end - start)) / CLOCKS_PER_SEC; + + printd("done. (%.6f secs.)\n", time); + + return diagnoses_symbol; +} + +/* + * list the diagnoses and let user choose a diagnosis to be applied + */ +struct sfix_list *ask_user_choose_fix(struct sfl_list *diag) +{ + int choice; + struct sfl_node *ret; + + printd("=== GENERATED DIAGNOSES ===\n"); + printd("0: No changes wanted\n"); + print_diagnoses_symbol(diag); + + printd("\n> Choose option: "); + scanf("%d", &choice); + + /* no changes wanted */ + if (choice == 0) + return NULL; + + ret = list_at_index(choice - 1, &diag->list, struct sfl_node, node); + return ret ? ret->elem : NULL; +} + + +/* + * calculate the new value for a boolean symbol given a diagnosis and an fexpr + */ +static tristate calculate_new_tri_val(struct fexpr *e, + struct fexpr_list *diagnosis) +{ + assert(sym_is_boolean(e->sym)); + + /* return the opposite of the last assumption for booleans */ + if (e->sym->type == S_BOOLEAN) + return e->assumption ? no : yes; + + /* new values for tristate must be deduced from the diagnosis */ + if (e->sym->type == S_TRISTATE) { + /* fexpr_y */ + if (e->tri == yes) { + if (e->assumption == true) + /* + * if diagnosis contains fexpr_m, fexpr_m was + * false => new value is mod + */ + return diagnosis_contains_fexpr( + diagnosis, e->sym->fexpr_m) ? + mod : + no; + else if (e->assumption == false) + /* + * if fexpr_y is set to true, the new value + * must be yes + */ + return yes; + } + /* fexpr_m */ + if (e->tri == mod) { + if (e->assumption == true) + /* + * if diagnosis contains fexpr_y, fexpr_y was + * false => new value is yes + */ + return diagnosis_contains_fexpr( + diagnosis, e->sym->fexpr_m) ? + yes : + no; + else if (e->assumption == false) + /* + * if diagnosis contains fexpr_m, the new value + * must be mod + */ + return mod; + } + perror("Should not get here.\n"); + } + + perror("Error calculating new tristate value.\n"); + return no; +} + +/* + * calculate the new value for a non-boolean symbol given a diagnosis and an + * fexpr + */ +static const char *calculate_new_string_value(struct fexpr *e, + struct fexpr_list *diagnosis) +{ + struct fexpr_node *node; + struct fexpr *e2; + + assert(sym_is_nonboolean(e->sym)); + + /* if assumption was false before, this is the new value because only 1 + * variable can be true + */ + if (e->assumption == false) + return str_get(&e->nb_val); + + /* a diagnosis always contains 2 variables for the same non-boolean + * symbol one is set to true, the other to false + * otherwise you'd set 2 variables to true, which is not allowed + */ + CF_LIST_FOR_EACH(node, diagnosis, fexpr) { + e2 = node->elem; + + /* not interested in other symbols or the same fexpr */ + if (e->sym != e2->sym || e->satval == e2->satval) + continue; + + return str_get(&e2->nb_val); + } + + perror("Error calculating new string value.\n"); + return ""; +} + +static bool fexpr_list_has_length_1(struct fexpr_list *list) +{ + struct fexpr_node *node; + bool first = true; + + CF_LIST_FOR_EACH(node, list, fexpr) { + if (first) + first = false; + else + return false; + } + return true; +} diff --git a/scripts/kconfig/cf_rangefix.h b/scripts/kconfig/cf_rangefix.h new file mode 100644 index 000000000000..aab3f52172e3 --- /dev/null +++ b/scripts/kconfig/cf_rangefix.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Patrick Franz + */ + +#ifndef CF_RANGEFIX_H +#define CF_RANGEFIX_H + +#include "picosat_functions.h" +#include "cf_defs.h" + +/* initialize RangeFix and return the diagnoses */ +struct sfl_list *rangefix_run(PicoSAT *pico, struct cfdata *data); + +/* ask user which fix to apply */ +struct sfix_list *ask_user_choose_fix(struct sfl_list *diag); + +/* print a single diagnosis of type symbol_fix */ +void print_diagnosis_symbol(struct sfix_list *diag_sym); + +#endif From patchwork Mon Oct 28 03:49:45 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13852976 Received: from mail-ej1-f52.google.com (mail-ej1-f52.google.com [209.85.218.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4315118A92C; Mon, 28 Oct 2024 03:50:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730087423; cv=none; b=pjpJbsNlkzLAuZR0GBmBB9yBCxioJaewZ3g4LsLhMwEqvAL9eWj3IkyLQF8cIyZ40Gw+cOZ1ryeXay+FW339hp6EXYCC2zCFCiV68HI3ByUdBpZ2KuMNfOMnuT3mVsYTaMcRY6XI4b+MrtE5yNQaQWUPzzHXN9KHz0tWJ7mz9jM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730087423; c=relaxed/simple; bh=NPf5vJ2VlxShJWih9FowWzT8bXiJsj2hnF9I2y51xRU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=dHeSrrIGiRDvfW7dokicWJFYJHUQfgcxMtUXH5YMrpEwDjvJPHgdR/+qVKJ/uQ82I2GG20/bPUCMM4fYwdfNdmIYvhqOcx6FbxMr7B2xd+2NDxs5pMAhfnRi8P/7ZerIjVpAJ6SMCKGy+IxW/eKaaHj81UfQxjX+bko3sp9A994= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=kZ/Jzk4j; arc=none smtp.client-ip=209.85.218.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="kZ/Jzk4j" Received: by mail-ej1-f52.google.com with SMTP id a640c23a62f3a-a99f629a7aaso609558266b.1; Sun, 27 Oct 2024 20:50:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1730087417; x=1730692217; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=rZfPpGpXR97KacXspfYJfMPUSiTX7fw8QE3JIOdHhIU=; b=kZ/Jzk4jcNql1EddwcwLzOV4s7XIWbXqLgbrOhE5Ea2jBSZ22HT8zDg7/TpoWvwvrB mNPVeqayAF6CSt0Y4pwvTgWR2AULyPQKZFO6f9ZDvpUnntKwy9qbbpQNEwHQe1drcq7h ftYtbbelCnEkBngfCnIprie0gOJfPv5bDHherdBQJ8PxEq2xHijapqz1X1CZSoLwCM1O kW7gx0h+Xt+KYcX3JycEpgWOtYwqAleqermMBFN2EH2874K6wubGSPvO6c5V67sdQY5G rrYkISHekFLoOqsRCv4B1J1W6dShvPKHFIEhkIlL1D+vcoZDSV8qui8SUJ0woUSFEikw DwgQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1730087417; x=1730692217; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=rZfPpGpXR97KacXspfYJfMPUSiTX7fw8QE3JIOdHhIU=; b=p/ObbQpK40mBPtVMcjqZPrpNd7qY1gTb7ZECvEaow5hRMoqS901K2lmzTv2w/7TnUz CK18I8iOLI7QUo2VtYQbaIRsm8spPXV5uCY2pw/VmQ9oC6qAPE2yJsHLkNudY3YagM2B GVKlc+Y0KNhNlWfqR91pRvIuW5CSoqAZ7vTlOZtIM53lvVIlm0Z/uRSjPogLLJmlSyDe vVV2enezMfat5mCRok9S/wogJ3TzvWvCyRQyG54hBGQyRteot+R/ZUuur/O4YNLWnCfP 3ifRSztbh+xss+LhPf30hpMynMmzHAdFkAcaH4T5depOXnog/ImCyQQ5/ib3aMdL8DEc sHEQ== X-Forwarded-Encrypted: i=1; AJvYcCUXiFI6bpad0fO5o7dPRiz3L/BIGmdbt/h3+fL7lEk5nr+iG3Q5ZWy5OguEtS/8vykCpl7knP1SFzuYf3k=@vger.kernel.org X-Gm-Message-State: AOJu0YxdrH0CmNU3J3FvwS4xZWZ+OpmoCjQft2AK2vQYy6WmIpjJdCEQ +XzB1TLbvObRTn6S2lQUgb/AiLd1l3K8fHSp8c/4PG8P3a4qvS+RhXVbfA== X-Google-Smtp-Source: AGHT+IHdUwMcWgCC07jYShVssrqtGX3k5/QSK50mxi6pGvDRppL70kaOytlxIS4QC/GUmJkjhkAMnw== X-Received: by 2002:a17:907:86a0:b0:a9a:49a8:35b2 with SMTP id a640c23a62f3a-a9ad1a5f909mr1024865166b.20.1730087417269; Sun, 27 Oct 2024 20:50:17 -0700 (PDT) Received: from localhost.localdomain ([2a02:908:e842:bf20:78b:e59b:2b0:d2e9]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9b30f5932fsm334599366b.168.2024.10.27.20.50.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 27 Oct 2024 20:50:16 -0700 (PDT) From: Ole Schuerks To: linux-kbuild@vger.kernel.org Cc: ole0811sch@gmail.com, jude.gyimah@rub.de, thorsten.berger@rub.de, deltaone@debian.org, jan.sollmann@rub.de, mcgrof@kernel.org, masahiroy@kernel.org, linux-kernel@vger.kernel.org, nathan@kernel.org, nicolas@fjasle.eu Subject: [PATCH v6 07/11] kconfig: Add files with utility functions Date: Mon, 28 Oct 2024 04:49:45 +0100 Message-Id: <20241028034949.95322-8-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20241028034949.95322-1-ole0811sch@gmail.com> References: <20241028034949.95322-1-ole0811sch@gmail.com> Precedence: bulk X-Mailing-List: linux-kbuild@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 This commit contains various helper functions used in the project. Co-developed-by: Patrick Franz Signed-off-by: Patrick Franz Co-developed-by: Ibrahim Fayaz Signed-off-by: Ibrahim Fayaz Reviewed-by: Luis Chamberlain Tested-by: Evgeny Groshev Suggested-by: Sarah Nadi Suggested-by: Thorsten Berger Signed-off-by: Thorsten Berger Signed-off-by: Ole Schuerks --- scripts/kconfig/cf_utils.c | 980 +++++++++++++++++++++++++++++++++++++ scripts/kconfig/cf_utils.h | 112 +++++ 2 files changed, 1092 insertions(+) create mode 100644 scripts/kconfig/cf_utils.c create mode 100644 scripts/kconfig/cf_utils.h diff --git a/scripts/kconfig/cf_utils.c b/scripts/kconfig/cf_utils.c new file mode 100644 index 000000000000..8dbcee451209 --- /dev/null +++ b/scripts/kconfig/cf_utils.c @@ -0,0 +1,980 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Patrick Franz + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "internal.h" +#include "picosat_functions.h" +#include "cf_utils.h" +#include "cf_expr.h" +#include "list.h" + +#define SATMAP_INIT_SIZE 2 + +static PicoSAT *pico; + +static void unfold_cnf_clause(struct pexpr *e); +static void build_cnf_tseytin(struct pexpr *e, struct cfdata *data); + +static void build_cnf_tseytin_top_and(struct pexpr *e, struct cfdata *data); +static void build_cnf_tseytin_top_or(struct pexpr *e, struct cfdata *data); + +static void build_cnf_tseytin_tmp(struct pexpr *e, struct fexpr *t, + struct cfdata *data); +static void build_cnf_tseytin_and(struct pexpr *e, struct fexpr *t, + struct cfdata *data); +static void build_cnf_tseytin_or(struct pexpr *e, struct fexpr *t, + struct cfdata *data); +static int pexpr_get_satval(struct pexpr *e); + +/* + * parse Kconfig-file and read .config + */ +void init_config(const char *Kconfig_file) +{ + conf_parse(Kconfig_file); + conf_read(NULL); +} + +/* + * initialize satmap + */ +void init_data(struct cfdata *data) +{ + /* create hashtable with all fexpr */ + data->satmap = xcalloc(SATMAP_INIT_SIZE, sizeof(typeof(*data->satmap))); + data->satmap_size = SATMAP_INIT_SIZE; + + printd("done.\n"); +} + +/* + * create SAT-variables for all fexpr + */ +void create_sat_variables(struct cfdata *data) +{ + struct symbol *sym; + + printd("Creating SAT-variables..."); + + for_all_symbols(sym) { + sym->constraints = CF_LIST_INIT(pexpr); + sym_create_fexpr(sym, data); + } + + printd("done.\n"); +} + +/* + * create various constants + */ +void create_constants(struct cfdata *data) +{ + printd("Creating constants..."); + + /* create TRUE and FALSE constants */ + data->constants->const_false = + fexpr_create(data->sat_variable_nr++, FE_FALSE, "False"); + // const_false = fexpr_create(sat_variable_nr++, FE_FALSE, "False"); + fexpr_add_to_satmap(data->constants->const_false, data); + + data->constants->const_true = + fexpr_create(data->sat_variable_nr++, FE_TRUE, "True"); + fexpr_add_to_satmap(data->constants->const_true, data); + + /* add fexpr of constants to tristate constants */ + symbol_yes.fexpr_y = data->constants->const_true; + symbol_yes.fexpr_m = data->constants->const_false; + + symbol_mod.fexpr_y = data->constants->const_false; + symbol_mod.fexpr_m = data->constants->const_true; + + symbol_no.fexpr_y = data->constants->const_false; + symbol_no.fexpr_m = data->constants->const_false; + + /* create symbols yes/mod/no as fexpr */ + data->constants->symbol_yes_fexpr = fexpr_create(0, FE_SYMBOL, "y"); + data->constants->symbol_yes_fexpr->sym = &symbol_yes; + data->constants->symbol_yes_fexpr->tri = yes; + + data->constants->symbol_mod_fexpr = fexpr_create(0, FE_SYMBOL, "m"); + data->constants->symbol_mod_fexpr->sym = &symbol_mod; + data->constants->symbol_mod_fexpr->tri = mod; + + data->constants->symbol_no_fexpr = fexpr_create(0, FE_SYMBOL, "n"); + data->constants->symbol_no_fexpr->sym = &symbol_no; + data->constants->symbol_no_fexpr->tri = no; + + printd("done.\n"); +} + +/* + * create a temporary SAT-variable + */ +struct fexpr *create_tmpsatvar(struct cfdata *data) +{ + char *name = get_tmp_var_as_char(data->tmp_variable_nr); + struct fexpr *t = + fexpr_create(data->sat_variable_nr++, FE_TMPSATVAR, name); + + ++data->tmp_variable_nr; + fexpr_add_to_satmap(t, data); + + free(name); + return t; +} + +/* + * return a temporary SAT variable as string + */ +char *get_tmp_var_as_char(int i) +{ + char *val = malloc(sizeof(char) * 18); + + snprintf(val, 18, "T_%d", i); + return val; +} + +/* + * return a tristate value as a char * + */ +char *tristate_get_char(tristate val) +{ + switch (val) { + case yes: + return "yes"; + case mod: + return "mod"; + case no: + return "no"; + default: + return ""; + } +} + +/* + *check whether an expr can evaluate to mod + */ +bool expr_can_evaluate_to_mod(struct expr *e) +{ + if (!e) + return false; + + switch (e->type) { + case E_SYMBOL: + return e->left.sym == &symbol_mod || + e->left.sym->type == S_TRISTATE; + case E_AND: + case E_OR: + return expr_can_evaluate_to_mod(e->left.expr) || + expr_can_evaluate_to_mod(e->right.expr); + case E_NOT: + return expr_can_evaluate_to_mod(e->left.expr); + default: + return false; + } +} + +/* + * check whether an expr is a non-Boolean constant + */ +bool expr_is_nonbool_constant(struct expr *e) +{ + if (e->type != E_SYMBOL) + return false; + if (e->left.sym->type != S_UNKNOWN) + return false; + + if (e->left.sym->flags & SYMBOL_CONST) + return true; + + return string_is_number(e->left.sym->name) || + string_is_hex(e->left.sym->name); +} + +/* + * check whether a symbol is a non-Boolean constant + */ +bool sym_is_nonbool_constant(struct symbol *sym) +{ + if (sym->type != S_UNKNOWN) + return false; + + if (sym->flags & SYMBOL_CONST) + return true; + + return string_is_number(sym->name) || string_is_hex(sym->name); +} + +/* + * check, if the symbol is a tristate-constant + */ +bool sym_is_tristate_constant(struct symbol *sym) +{ + return sym == &symbol_yes || sym == &symbol_mod || sym == &symbol_no; +} + +/* + * check, if a symbol is of type boolean or tristate + */ +bool sym_is_boolean(struct symbol *sym) +{ + return sym->type == S_BOOLEAN || sym->type == S_TRISTATE; +} + +/* + * check, if a symbol is a boolean/tristate or a tristate constant + */ +bool sym_is_bool_or_triconst(struct symbol *sym) +{ + return sym_is_tristate_constant(sym) || sym_is_boolean(sym); +} + +/* + * check, if a symbol is of type int, hex, or string + */ +bool sym_is_nonboolean(struct symbol *sym) +{ + return sym->type == S_INT || sym->type == S_HEX || + sym->type == S_STRING; +} + +/* + * check, if a symbol has a prompt + */ +bool sym_has_prompt(struct symbol *sym) +{ + struct property *prop; + + for_all_prompts(sym, prop) + return true; + + return false; +} + +/* + * return the prompt of the symbol if there is one, NULL otherwise + */ +struct property *sym_get_prompt(struct symbol *sym) +{ + struct property *prop; + + for_all_prompts(sym, prop) + return prop; + + return NULL; +} + +/* + * return the condition for the property, NULL if there is none. To be pexpr_put + * by caller. + */ +struct pexpr *prop_get_condition(struct property *prop, struct cfdata *data) +{ + if (prop == NULL) + return NULL; + + /* if there is no condition, return True */ + if (!prop->visible.expr) + return pexpr_alloc_symbol(data->constants->const_true); + + return expr_calculate_pexpr_both(prop->visible.expr, data); +} + +/* + * return the default property, NULL if none exists or can be satisfied + */ +struct property *sym_get_default_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_defaults(sym, prop) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + if (prop->visible.tri != no) + return prop; + } + return NULL; +} + +/* + * check whether a non-boolean symbol has a value set + */ +bool sym_nonbool_has_value_set(struct symbol *sym) +{ + /* + * The built constraints make the following constraints: + * + * visible -> not 'n' + * sym->dir_dep not fulfilled -> 'n' + * invisible -> (no default's condition is fulfilled <-> 'n') + */ + struct property *prompt; + struct property *p; + + if (!sym_is_nonboolean(sym)) + return false; + + /* cannot have a value with unmet dependencies */ + if (sym->dir_dep.expr && sym->dir_dep.tri == no) + return false; + + /* visible prompt => value set */ + prompt = sym_get_prompt(sym); + if (prompt != NULL && prompt->visible.tri != no) + return true; + + /* invisible prompt => must get value from default value */ + p = sym_get_default_prop(sym); + return p != NULL; +} + +/* + * return pointer to the name of the symbol or the current prompt-text, if it + * is a choice symbol + */ +const char *sym_get_name(struct symbol *sym) +{ + if (sym_is_choice(sym)) { + struct property *prompt = sym_get_prompt(sym); + + if (prompt == NULL) + return ""; + + return prompt->text; + } else { + return sym->name; + } +} + +/* + * check whether symbol is to be changed + */ +bool sym_is_sdv(struct sdv_list *list, struct symbol *sym) +{ + struct sdv_node *node; + + CF_LIST_FOR_EACH(node, list, sdv) + if (sym == node->elem->sym) + return true; + + return false; +} + +/* + * print a symbol's name + */ +void print_sym_name(struct symbol *sym) +{ + printf("Symbol: "); + if (sym_is_choice(sym)) { + struct property *prompt = sym_get_prompt(sym); + + printf("(Choice) %s", prompt->text); + } else { + printf("%s", sym->name); + } + printf("\n"); +} + +/* + * print all constraints for a symbol + */ +void print_sym_constraint(struct symbol *sym) +{ + struct pexpr_node *node; + + CF_LIST_FOR_EACH(node, sym->constraints, pexpr) + pexpr_print("::", node->elem, -1); +} + +/* + * print a default map + */ +void print_default_map(struct defm_list *map) +{ + struct default_map *entry; + struct defm_node *node; + + CF_LIST_FOR_EACH(node, map, defm) { + struct gstr s = str_new(); + + entry = node->elem; + + str_append(&s, "\t"); + str_append(&s, str_get(&entry->val->name)); + str_append(&s, " ->"); + pexpr_print(strdup(str_get(&s)), entry->e, -1); + str_free(&s); + } +} + +/* + * check whether a string is a number + */ +bool string_is_number(char *s) +{ + int len = strlen(s); + int i = 0; + + while (i < len) { + if (!isdigit(s[i])) + return false; + i++; + } + + return true; +} + +/* + * check whether a string is a hexadecimal number + */ +bool string_is_hex(char *s) +{ + int len = strlen(s); + int i = 2; + + if (len >= 3 && s[0] == '0' && s[1] == 'x') { + while (i < len) { + if (!isxdigit(s[i])) + return false; + i++; + } + return true; + } else { + return false; + } +} + +/* + * initialize PicoSAT + */ +PicoSAT *initialize_picosat(void) +{ + PicoSAT *pico; + + printd("\nInitializing PicoSAT..."); + pico = picosat_init(); + picosat_enable_trace_generation(pico); + printd("done.\n"); + + return pico; +} + +/* + * construct the CNF-clauses from the constraints + */ +void construct_cnf_clauses(PicoSAT *p, struct cfdata *data) +{ + struct symbol *sym; + + pico = p; + + /* adding unit-clauses for constants */ + sat_add_clause(2, pico, -(data->constants->const_false->satval)); + sat_add_clause(2, pico, data->constants->const_true->satval); + + for_all_symbols(sym) { + struct pexpr_node *node; + + if (sym->type == S_UNKNOWN) + continue; + + CF_LIST_FOR_EACH(node, sym->constraints, pexpr) { + if (pexpr_is_cnf(node->elem)) { + unfold_cnf_clause(node->elem); + picosat_add(pico, 0); + } else { + build_cnf_tseytin(node->elem, data); + } + + } + } +} + +/* + * helper function to add an expression to a CNF-clause + */ +static void unfold_cnf_clause(struct pexpr *e) +{ + switch (e->type) { + case PE_SYMBOL: + picosat_add(pico, e->left.fexpr->satval); + break; + case PE_OR: + unfold_cnf_clause(e->left.pexpr); + unfold_cnf_clause(e->right.pexpr); + break; + case PE_NOT: + picosat_add(pico, -(e->left.pexpr->left.fexpr->satval)); + break; + default: + perror("Not in CNF, FE_EQUALS."); + } +} + +/* + * build CNF-clauses for a pexpr not in CNF + */ +static void build_cnf_tseytin(struct pexpr *e, struct cfdata *data) +{ + switch (e->type) { + case PE_AND: + build_cnf_tseytin_top_and(e, data); + break; + case PE_OR: + build_cnf_tseytin_top_or(e, data); + break; + default: + perror("Expression not a propositional logic formula. root."); + } +} + +/* + * split up a pexpr of type AND as both sides must be satisfied + */ +static void build_cnf_tseytin_top_and(struct pexpr *e, struct cfdata *data) +{ + if (pexpr_is_cnf(e->left.pexpr)) + unfold_cnf_clause(e->left.pexpr); + else + build_cnf_tseytin(e->left.pexpr, data); + + if (pexpr_is_cnf(e->right.pexpr)) + unfold_cnf_clause(e->right.pexpr); + else + build_cnf_tseytin(e->right.pexpr, data); + +} + +static void build_cnf_tseytin_top_or(struct pexpr *e, struct cfdata *data) +{ + struct fexpr *t1 = NULL, *t2 = NULL; + int a, b; + + /* set left side */ + if (pexpr_is_symbol(e->left.pexpr)) { + a = pexpr_get_satval(e->left.pexpr); + } else { + t1 = create_tmpsatvar(data); + a = t1->satval; + } + + /* set right side */ + if (pexpr_is_symbol(e->right.pexpr)) { + b = pexpr_get_satval(e->right.pexpr); + } else { + t2 = create_tmpsatvar(data); + b = t2->satval; + } + + /* A v B */ + sat_add_clause(3, pico, a, b); + + /* traverse down the tree to build more constraints if needed */ + if (!pexpr_is_symbol(e->left.pexpr)) { + if (t1 == NULL) + perror("t1 is NULL."); + + build_cnf_tseytin_tmp(e->left.pexpr, t1, data); + } + + if (!pexpr_is_symbol(e->right.pexpr)) { + if (t2 == NULL) + perror("t2 is NULL."); + + build_cnf_tseytin_tmp(e->right.pexpr, t2, data); + } +} + +/* + * build the sub-expressions + */ +static void build_cnf_tseytin_tmp(struct pexpr *e, struct fexpr *t, + struct cfdata *data) +{ + switch (e->type) { + case PE_AND: + build_cnf_tseytin_and(e, t, data); + break; + case PE_OR: + build_cnf_tseytin_or(e, t, data); + break; + default: + perror("Expression not a propositional logic formula. root."); + } +} + +/* + * build the Tseytin sub-expressions for a pexpr of type AND + */ +static void build_cnf_tseytin_and(struct pexpr *e, struct fexpr *t, + struct cfdata *data) +{ + struct fexpr *t1 = NULL, *t2 = NULL; + int a, b, c; + + assert(t != NULL); + + /* set left side */ + if (pexpr_is_symbol(e->left.pexpr)) { + a = pexpr_get_satval(e->left.pexpr); + } else { + t1 = create_tmpsatvar(data); + a = t1->satval; + } + + /* set right side */ + if (pexpr_is_symbol(e->right.pexpr)) { + b = pexpr_get_satval(e->right.pexpr); + } else { + t2 = create_tmpsatvar(data); + b = t2->satval; + } + + c = t->satval; + + /* -A v -B v C */ + sat_add_clause(4, pico, -a, -b, c); + /* A v -C */ + sat_add_clause(3, pico, a, -c); + /* B v -C */ + sat_add_clause(3, pico, b, -c); + + /* traverse down the tree to build more constraints if needed */ + if (!pexpr_is_symbol(e->left.pexpr)) { + if (t1 == NULL) + perror("t1 is NULL."); + + build_cnf_tseytin_tmp(e->left.pexpr, t1, data); + } + if (!pexpr_is_symbol(e->right.pexpr)) { + if (t2 == NULL) + perror("t2 is NULL."); + + build_cnf_tseytin_tmp(e->right.pexpr, t2, data); + } +} + +/* + * build the Tseytin sub-expressions for a pexpr of type + */ +static void build_cnf_tseytin_or(struct pexpr *e, struct fexpr *t, + struct cfdata *data) +{ + struct fexpr *t1 = NULL, *t2 = NULL; + int a, b, c; + + assert(t != NULL); + + /* set left side */ + if (pexpr_is_symbol(e->left.pexpr)) { + a = pexpr_get_satval(e->left.pexpr); + } else { + t1 = create_tmpsatvar(data); + a = t1->satval; + } + + /* set right side */ + if (pexpr_is_symbol(e->right.pexpr)) { + b = pexpr_get_satval(e->right.pexpr); + } else { + t2 = create_tmpsatvar(data); + b = t2->satval; + } + + c = t->satval; + + /* A v B v -C */ + sat_add_clause(4, pico, a, b, -c); + /* -A v C */; + sat_add_clause(3, pico, -a, c); + /* -B v C */ + sat_add_clause(3, pico, -b, c); + + /* traverse down the tree to build more constraints if needed */ + if (!pexpr_is_symbol(e->left.pexpr)) { + if (t1 == NULL) + perror("t1 is NULL."); + + build_cnf_tseytin_tmp(e->left.pexpr, t1, data); + } + if (!pexpr_is_symbol(e->right.pexpr)) { + if (t2 == NULL) + perror("t2 is NULL."); + + build_cnf_tseytin_tmp(e->right.pexpr, t2, data); + } +} + +/* + * add a clause to PicoSAT + * First argument must be the SAT solver + */ +void sat_add_clause(int num, ...) +{ + va_list valist; + int lit; + PicoSAT *pico; + + if (num <= 1) + return; + + va_start(valist, num); + + pico = va_arg(valist, PicoSAT *); + + /* access all the arguments assigned to valist */ + for (int i = 1; i < num; i++) { + lit = va_arg(valist, int); + picosat_add(pico, lit); + } + picosat_add(pico, 0); + + va_end(valist); +} + +/* + * return the SAT-variable for a pexpr that is a symbol + */ +static int pexpr_get_satval(struct pexpr *e) +{ + if (!pexpr_is_symbol(e)) { + perror("pexpr is not a symbol."); + return -1; + } + + switch (e->type) { + case PE_SYMBOL: + return e->left.fexpr->satval; + case PE_NOT: + return -(e->left.pexpr->left.fexpr->satval); + default: + perror("Not a symbol."); + } + + return -1; +} + +/* + * start PicoSAT + */ +void picosat_solve(PicoSAT *pico, struct cfdata *data) +{ + clock_t start, end; + double time; + int res; + + printd("Solving SAT-problem..."); + + start = clock(); + res = picosat_sat(pico, -1); + end = clock(); + + time = ((double) (end - start)) / CLOCKS_PER_SEC; + printd("done. (%.6f secs.)\n\n", time); + + if (res == PICOSAT_SATISFIABLE) { + printd("===> PROBLEM IS SATISFIABLE <===\n"); + + } else if (res == PICOSAT_UNSATISFIABLE) { + struct fexpr *e; + int lit; + const int *i; + + printd("===> PROBLEM IS UNSATISFIABLE <===\n"); + + /* print unsat core */ + printd("\nPrinting unsatisfiable core:\n"); + + i = picosat_failed_assumptions(pico); + lit = abs(*i++); + + while (lit != 0) { + e = data->satmap[lit]; + + printd("(%d) %s <%d>\n", lit, str_get(&e->name), + e->assumption); + lit = abs(*i++); + } + } else { + printd("Unknown if satisfiable.\n"); + } +} + +/* + * add assumption for a symbol to the SAT-solver + */ +void sym_add_assumption(PicoSAT *pico, struct symbol *sym) +{ + if (sym_is_boolean(sym)) { + int tri_val = sym_get_tristate_value(sym); + + sym_add_assumption_tri(pico, sym, tri_val); + return; + } + + if (sym_is_nonboolean(sym)) { + bool first; + struct fexpr *not_set = list_first_entry(&sym->nb_vals->list, + struct fexpr_node, node) + ->elem; + struct fexpr_node *node; + + const char *string_val = sym_get_string_value(sym); + + if (sym->type == S_STRING && !strcmp(string_val, "")) + return; + + /* symbol does not have a value */ + if (!sym_nonbool_has_value_set(sym)) { + bool first = true; + + /* set value for sym=n */ + picosat_assume(pico, not_set->satval); + not_set->assumption = true; + + CF_LIST_FOR_EACH(node, sym->nb_vals, fexpr) { + if (first) { + first = false; + continue; + } + picosat_assume(pico, -(node->elem->satval)); + node->elem->assumption = false; + } + + return; + } + + /* symbol does have a value set */ + + /* set value for sym=n */ + picosat_assume(pico, -(not_set->satval)); + not_set->assumption = false; + + first = true; + /* set value for all other fexpr */ + CF_LIST_FOR_EACH(node, sym->nb_vals, fexpr) { + if (first) { + first = false; + continue; + } + + if (strcmp(str_get(&node->elem->nb_val), string_val) == + 0) { + picosat_assume(pico, node->elem->satval); + node->elem->assumption = true; + } else { + picosat_assume(pico, -(node->elem->satval)); + node->elem->assumption = false; + } + } + } +} + +/* + * add assumption for a boolean symbol to the SAT-solver + */ +void sym_add_assumption_tri(PicoSAT *pico, struct symbol *sym, tristate tri_val) +{ + if (sym->type == S_BOOLEAN) { + int a = sym->fexpr_y->satval; + + switch (tri_val) { + case no: + picosat_assume(pico, -a); + sym->fexpr_y->assumption = false; + break; + case mod: + perror("Should not happen. Boolean symbol is set to mod.\n"); + break; + case yes: + + picosat_assume(pico, a); + sym->fexpr_y->assumption = true; + break; + } + } + if (sym->type == S_TRISTATE) { + int a = sym->fexpr_y->satval; + int a_m = sym->fexpr_m->satval; + + switch (tri_val) { + case no: + picosat_assume(pico, -a); + picosat_assume(pico, -a_m); + sym->fexpr_y->assumption = false; + sym->fexpr_m->assumption = false; + break; + case mod: + picosat_assume(pico, -a); + picosat_assume(pico, a_m); + sym->fexpr_y->assumption = false; + sym->fexpr_m->assumption = true; + break; + case yes: + picosat_assume(pico, a); + picosat_assume(pico, -a_m); + sym->fexpr_y->assumption = true; + sym->fexpr_m->assumption = false; + break; + } + } +} + +/* + * add assumptions for the symbols to be changed to the SAT solver + */ +void sym_add_assumption_sdv(PicoSAT *pico, struct sdv_list *list) +{ + struct symbol_dvalue *sdv; + struct sdv_node *node; + int lit_y, lit_m; + + CF_LIST_FOR_EACH(node, list, sdv) { + sdv = node->elem; + lit_y = sdv->sym->fexpr_y->satval; + + if (sdv->sym->type == S_BOOLEAN) { + switch (sdv->tri) { + case yes: + picosat_assume(pico, lit_y); + break; + case no: + picosat_assume(pico, -lit_y); + break; + case mod: + perror("Should not happen.\n"); + } + } else if (sdv->sym->type == S_TRISTATE) { + lit_m = sdv->sym->fexpr_m->satval; + + switch (sdv->tri) { + case yes: + picosat_assume(pico, lit_y); + picosat_assume(pico, -lit_m); + break; + case mod: + picosat_assume(pico, -lit_y); + picosat_assume(pico, lit_m); + break; + case no: + picosat_assume(pico, -lit_y); + picosat_assume(pico, -lit_m); + } + } + } +} diff --git a/scripts/kconfig/cf_utils.h b/scripts/kconfig/cf_utils.h new file mode 100644 index 000000000000..7e4d890428b7 --- /dev/null +++ b/scripts/kconfig/cf_utils.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Patrick Franz + */ + +#ifndef CF_UTILS_H +#define CF_UTILS_H + +#include "expr.h" +#include "cf_defs.h" +#include "picosat_functions.h" + +/* parse Kconfig-file and read .config */ +void init_config(const char *Kconfig_file); + +/* initialize satmap and cnf_clauses */ +void init_data(struct cfdata *data); + +/* assign SAT-variables to all fexpr and create the sat_map */ +void create_sat_variables(struct cfdata *data); + +/* create True/False constants */ +void create_constants(struct cfdata *data); + +/* create a temporary SAT-variable */ +struct fexpr *create_tmpsatvar(struct cfdata *data); + +/* return a temporary SAT variable as string */ +char *get_tmp_var_as_char(int i); + +/* return a tristate value as a char * */ +char *tristate_get_char(tristate val); + +/* check whether an expr can evaluate to mod */ +bool expr_can_evaluate_to_mod(struct expr *e); + +/* check whether an expr is a non-Boolean constant */ +bool expr_is_nonbool_constant(struct expr *e); + +/* check whether a symbol is a non-Boolean constant */ +bool sym_is_nonbool_constant(struct symbol *sym); + +/* check, if the symbol is a tristate-constant */ +bool sym_is_tristate_constant(struct symbol *sym); + +/* check, if a symbol is of type boolean or tristate */ +bool sym_is_boolean(struct symbol *sym); + +/* check, if a symbol is a boolean/tristate or a tristate constant */ +bool sym_is_bool_or_triconst(struct symbol *sym); + +/* check, if a symbol is of type int, hex, or string */ +bool sym_is_nonboolean(struct symbol *sym); + +/* check, if a symbol has a prompt */ +bool sym_has_prompt(struct symbol *sym); + +/* return the prompt of the symbol, if there is one */ +struct property *sym_get_prompt(struct symbol *sym); + +/* return the condition for the property, True if there is none */ +struct pexpr *prop_get_condition(struct property *prop, struct cfdata *data); + +/* return the default property, NULL if none exists or can be satisfied */ +struct property *sym_get_default_prop(struct symbol *sym); + +/* check whether a non-boolean symbol has a value set */ +bool sym_nonbool_has_value_set(struct symbol *sym); + +/* return the name of the symbol */ +const char *sym_get_name(struct symbol *sym); + +/* check whether symbol is to be changed */ +bool sym_is_sdv(struct sdv_list *list, struct symbol *sym); + +/* print a symbol's name */ +void print_sym_name(struct symbol *sym); + +/* print all constraints for a symbol */ +void print_sym_constraint(struct symbol *sym); + +/* print a default map */ +void print_default_map(struct defm_list *map); + +/* check whether a string is a number */ +bool string_is_number(char *s); + +/* check whether a string is a hexadecimal number */ +bool string_is_hex(char *s); + +/* initialize PicoSAT */ +PicoSAT *initialize_picosat(void); + +/* construct the CNF-clauses from the constraints */ +void construct_cnf_clauses(PicoSAT *pico, struct cfdata *data); + +/* add a clause to PicoSAT */ +void sat_add_clause(int num, ...); + +/* start PicoSAT */ +void picosat_solve(PicoSAT *pico, struct cfdata *data); + +/* add assumption for a symbol to the SAT-solver */ +void sym_add_assumption(PicoSAT *pico, struct symbol *sym); + +/* add assumption for a boolean symbol to the SAT-solver */ +void sym_add_assumption_tri(PicoSAT *pico, struct symbol *sym, tristate tri_val); + +/* add assumptions for the symbols to be changed to the SAT solver */ +void sym_add_assumption_sdv(PicoSAT *pico, struct sdv_list *list); + +#endif From patchwork Mon Oct 28 03:49:46 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13852977 Received: from mail-ej1-f48.google.com (mail-ej1-f48.google.com [209.85.218.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1225418CBF1; Mon, 28 Oct 2024 03:50:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730087424; cv=none; b=oKBNzpcYB118VY6ct8wWRj34sn8r8ZMkP8zXzXO+XfEVzQfjwwrO7y9QlxGk7YvJTdMfQ7fHouadTzWV3ZRMLIGyCuwAPQgVTYEMgbWSllvC8lXIfhewll+J2FMc1X7j3wffTzMcBmsMt4SI3VHtJO1ZMOwHjee/Jt8WjOgx8lk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730087424; c=relaxed/simple; bh=Q+z5TFtLdyfX/zWRO0djAxbQzSd65+vB0/zjoBn0ka8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=j2dF9LRqhA6F5uqIemXCpnW3YLv0h784UiSJCeXXzW2473KcZ0z/ZJ0tkaB/QPnIt4wtH3Q0OrlvSNE6ia0fYr8kuGH8o5gZY7ZbF6i2knH3+KoUQSGFlJHglRedFoJ8ppI+erYOx1xqKLvYzRaP8zRjyLh4vknCDvbI1BpUi60= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=jBj9HOVU; arc=none smtp.client-ip=209.85.218.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="jBj9HOVU" Received: by mail-ej1-f48.google.com with SMTP id a640c23a62f3a-a99cc265e0aso590073166b.3; Sun, 27 Oct 2024 20:50:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1730087419; x=1730692219; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=9rET4KlQy+mdwuN8tRIe2NXs5qx/OQpgV+t16TCnGB4=; b=jBj9HOVUD/WwHlOuz14pTPyD5l94143bGiR7tsxEfJ5rAN9gw2KwScb+four8g0WH+ 0oKqNybSv55btcdtGyK6v5YYWJcGOkD21wKUMnqtrJfNRqBvVEophMIfOa0ybTuEtfqZ 7kdq6lEptC3bongwCZ4EtOeZzqarhME0AGh8xMqBrqB0VbNvoRy2vKBlHWritsXk5qUb 01o0/A2hmmVWohE4mY864B9IebfrCSSCuiK1UqqFDJrm2MqSKRV1LWI1w5rvew0dJiXk Et7Lkc/5WCJM2D1X/ngcIWslP/9C2FBCf+w3wbt5ap5c9gY2Ttt/xbEmaSVbvGr1XAjv 3YMQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1730087419; x=1730692219; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=9rET4KlQy+mdwuN8tRIe2NXs5qx/OQpgV+t16TCnGB4=; b=b6A5JR5NHjBBa/qZmjbx8fJiIKH1LDlLIBhFY81XYWNFtKSLCKAZBhD0f3Sj5HeVfu 9yHGt9L42xglNymEhdrLCcm943q7Rf/TBvgPmPOsZ6YQPCQUU6v27R55GxwO8kOgrL34 Yx1fPr5QlG3Qz5lSzGymR/LBH7J+fbNab1R/x4rjVBjBAtu+3xpTUurx5+7kAqWxoVtN pttfvEyWEX78BLxmFUfHaZ/1rRWzvUV9ev6djlevJNjlYu3TXYNqQXVXauPQoieBcPjd Tas5mE5Uv54GrA8j+7Ocglme+nxtN8pTi0fuC5kYOa3NyySIBEE8NltXJ54lWLZ9Tw14 rltA== X-Forwarded-Encrypted: i=1; AJvYcCUi8SgbgUfCiJ9q7bce2e8zbH9K8rVerG6CVqKJe1cwXiETqi8unGVksSdsbRqhg/6wfBkj3yi+dv/KosQ=@vger.kernel.org X-Gm-Message-State: AOJu0YxVFNEAZV9HB2/PVP82BJUg1eU830mJIQv16Q7ByrvG8RV3tRPX 47nno2qYDsdcRrx8kPu6v9p6MzjeHMyUx0gNY7JXAZi20B3g7PyvPIDRZQ== X-Google-Smtp-Source: AGHT+IEM0pbWkIsbLJltl+gUtWjz0y6ri+RAaHZ2AvUbOTz5c7JiykekUjFDaMYjCHP2OVHJBMYT4w== X-Received: by 2002:a17:906:794b:b0:a99:5601:7dc1 with SMTP id a640c23a62f3a-a9de61d4377mr623415766b.49.1730087419123; Sun, 27 Oct 2024 20:50:19 -0700 (PDT) Received: from localhost.localdomain ([2a02:908:e842:bf20:78b:e59b:2b0:d2e9]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9b30f5932fsm334599366b.168.2024.10.27.20.50.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 27 Oct 2024 20:50:18 -0700 (PDT) From: Ole Schuerks To: linux-kbuild@vger.kernel.org Cc: ole0811sch@gmail.com, jude.gyimah@rub.de, thorsten.berger@rub.de, deltaone@debian.org, jan.sollmann@rub.de, mcgrof@kernel.org, masahiroy@kernel.org, linux-kernel@vger.kernel.org, nathan@kernel.org, nicolas@fjasle.eu Subject: [PATCH v6 08/11] kconfig: Add tools Date: Mon, 28 Oct 2024 04:49:46 +0100 Message-Id: <20241028034949.95322-9-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20241028034949.95322-1-ole0811sch@gmail.com> References: <20241028034949.95322-1-ole0811sch@gmail.com> Precedence: bulk X-Mailing-List: linux-kbuild@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 This commit contains the actual API to be used from a configurator. Furthermore, it contains a tool to print all constraints into a file. Co-developed-by: Patrick Franz Signed-off-by: Patrick Franz Co-developed-by: Ibrahim Fayaz Signed-off-by: Ibrahim Fayaz Reviewed-by: Luis Chamberlain Tested-by: Evgeny Groshev Suggested-by: Sarah Nadi Suggested-by: Thorsten Berger Signed-off-by: Thorsten Berger Signed-off-by: Ole Schuerks --- scripts/kconfig/.gitignore | 1 + scripts/kconfig/cfoutconfig.c | 149 +++++++++++++++ scripts/kconfig/configfix.c | 351 ++++++++++++++++++++++++++++++++++ scripts/kconfig/configfix.h | 31 +++ 4 files changed, 532 insertions(+) create mode 100644 scripts/kconfig/cfoutconfig.c create mode 100644 scripts/kconfig/configfix.c create mode 100644 scripts/kconfig/configfix.h diff --git a/scripts/kconfig/.gitignore b/scripts/kconfig/.gitignore index 0b2ff775b2e3..23446f70083e 100644 --- a/scripts/kconfig/.gitignore +++ b/scripts/kconfig/.gitignore @@ -5,3 +5,4 @@ /[gmnq]conf-cflags /[gmnq]conf-libs /qconf-moc.cc +/cfoutconfig diff --git a/scripts/kconfig/cfoutconfig.c b/scripts/kconfig/cfoutconfig.c new file mode 100644 index 000000000000..d7cc853ed741 --- /dev/null +++ b/scripts/kconfig/cfoutconfig.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Patrick Franz + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" +#include "picosat_functions.h" +#include "cf_expr.h" +#include "cf_utils.h" +#include "cf_constraints.h" + +#define OUTFILE_CONSTRAINTS "./scripts/kconfig/cfout_constraints.txt" +#define OUTFILE_DIMACS "./scripts/kconfig/cfout_constraints.dimacs" + +static void write_constraints_to_file(struct cfdata *data); +static void write_dimacs_to_file(PicoSAT *pico, struct cfdata *data); + +/* -------------------------------------- */ + +int main(int argc, char *argv[]) +{ + clock_t start, end; + double time; + PicoSAT *pico; + + static struct constants constants = {NULL, NULL, NULL, NULL, NULL}; + static struct cfdata data = { + 1, // unsigned int sat_variable_nr + 1, // unsigned int tmp_variable_nr + NULL, // struct fexpr *satmap + 0, // size_t satmap_size + &constants // struct constants *constants + }; + if (!load_picosat()) { + printf("You need to install PicoSAT first\n"); + return EXIT_FAILURE; + } + + printf("\nCreating constraints and CNF clauses..."); + /* measure time for constructing constraints and clauses */ + start = clock(); + + /* parse Kconfig-file and read .config */ + init_config(argv[1]); + + /* initialize satmap and cnf_clauses */ + init_data(&data); + + /* creating constants */ + create_constants(&data); + + /* assign SAT variables & create sat_map */ + create_sat_variables(&data); + + /* get the constraints */ + build_constraints(&data); + + end = clock(); + time = ((double) (end - start)) / CLOCKS_PER_SEC; + + printd("done. (%.6f secs.)\n", time); + + /* start PicoSAT */ + pico = picosat_init(); + picosat_enable_trace_generation(pico); + printd("Building CNF-clauses..."); + start = clock(); + + /* construct the CNF clauses */ + construct_cnf_clauses(pico, &data); + + end = clock(); + time = ((double) (end - start)) / CLOCKS_PER_SEC; + printf("done. (%.6f secs.)\n", time); + + printf("\n"); + + /* write constraints into file */ + start = clock(); + printf("Writing constraints..."); + write_constraints_to_file(&data); + end = clock(); + time = ((double) (end - start)) / CLOCKS_PER_SEC; + printf("done. (%.6f secs.)\n", time); + + /* write SAT problem in DIMACS into file */ + start = clock(); + printf("Writing SAT problem in DIMACS..."); + write_dimacs_to_file(pico, &data); + end = clock(); + time = ((double) (end - start)) / CLOCKS_PER_SEC; + printf("done. (%.6f secs.)\n", time); + + printf("\nConstraints have been written into %s\n", OUTFILE_CONSTRAINTS); + printf("DIMACS-output has been written into %s\n", OUTFILE_DIMACS); + + return 0; +} + +static void write_constraints_to_file(struct cfdata *data) +{ + FILE *fd = fopen(OUTFILE_CONSTRAINTS, "w"); + struct symbol *sym; + + for_all_symbols(sym) { + struct pexpr_node *node; + + if (sym->type == S_UNKNOWN) + continue; + + list_for_each_entry(node, &sym->constraints->list, node) { + struct gstr s = str_new(); + + pexpr_as_char(node->elem, &s, 0, data); + fprintf(fd, "%s\n", str_get(&s)); + str_free(&s); + } + } + fclose(fd); +} + +static void add_comment(FILE *fd, struct fexpr *e) +{ + fprintf(fd, "c %d %s\n", e->satval, str_get(&e->name)); +} + +static void write_dimacs_to_file(PicoSAT *pico, struct cfdata *data) +{ + FILE *fd = fopen(OUTFILE_DIMACS, "w"); + + unsigned int i; + + for (i = 1; i < data->sat_variable_nr; i++) + add_comment(fd, data->satmap[i]); + + picosat_print(pico, fd); + fclose(fd); +} diff --git a/scripts/kconfig/configfix.c b/scripts/kconfig/configfix.c new file mode 100644 index 000000000000..02cb2af229df --- /dev/null +++ b/scripts/kconfig/configfix.c @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Patrick Franz + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "configfix.h" +#include "internal.h" +#include "picosat_functions.h" +#include "cf_utils.h" +#include "cf_constraints.h" +#include "cf_rangefix.h" +#include "cf_defs.h" +#include "expr.h" +#include "list.h" +#include "lkc.h" + +bool CFDEBUG; +bool stop_rangefix; + +static PicoSAT *pico; +static bool init_done; +static struct sym_list *conflict_syms; + +static bool sdv_within_range(struct sdv_list *symbols); + +/* -------------------------------------- */ + +struct sfix_list **run_satconf(struct symbol_dvalue **symbols, size_t n, + size_t *num_solutions) +{ + CF_DEF_LIST(symbols_list, sdv); + struct sfl_list *solutions; + struct sfix_list **solutions_arr; + struct sfl_node *node; + size_t i; + + i = 0; + for (i = 0; i < n; ++i) + CF_PUSH_BACK(symbols_list, symbols[i], sdv); + + solutions = run_satconf_list(symbols_list); + *num_solutions = list_size(&solutions->list); + solutions_arr = xcalloc(*num_solutions, sizeof(struct sfix_list *)); + i = 0; + CF_LIST_FOR_EACH(node, solutions, sfl) + solutions_arr[i++] = node->elem; + CF_LIST_FREE(solutions, sfl); + return solutions_arr; +} + +struct sfl_list *run_satconf_list(struct sdv_list *symbols) +{ + clock_t start, end; + double time; + struct symbol *sym; + struct sdv_node *node; + int res; + struct sfl_list *ret; + + static struct constants constants = {NULL, NULL, NULL, NULL, NULL}; + static struct cfdata data = { + 1, // unsigned int sat_variable_nr + 1, // unsigned int tmp_variable_nr + NULL, // struct fexpr *satmap + 0, // size_t satmap_size + &constants, // struct constants *constants + NULL // array with conflict-symbols + }; + + + /* check whether all values can be applied -> no need to run */ + if (sdv_within_range(symbols)) { + printd("\nAll symbols are already within range.\n\n"); + return CF_LIST_INIT(sfl); + } + + if (!init_done) { + printd("\n"); + printd("Init..."); + + /* measure time for constructing constraints and clauses */ + start = clock(); + + /* initialize satmap and cnf_clauses */ + init_data(&data); + + /* creating constants */ + create_constants(&data); + + /* assign SAT variables & create sat_map */ + create_sat_variables(&data); + + /* get the constraints */ + build_constraints(&data); + + end = clock(); + time = ((double)(end - start)) / CLOCKS_PER_SEC; + + printd("done. (%.6f secs.)\n", time); + + /* start PicoSAT */ + pico = initialize_picosat(); + printd("Building CNF-clauses..."); + start = clock(); + + /* construct the CNF clauses */ + construct_cnf_clauses(pico, &data); + + end = clock(); + time = ((double)(end - start)) / CLOCKS_PER_SEC; + + printd("done. (%.6f secs.)\n", time); + + printd("CNF-clauses added: %d\n", + picosat_added_original_clauses(pico)); + + init_done = true; + } + + /* copy array with symbols to change */ + data.sdv_symbols = CF_LIST_COPY(symbols, sdv); + + /* add assumptions for conflict-symbols */ + sym_add_assumption_sdv(pico, data.sdv_symbols); + + /* add assumptions for all other symbols */ + for_all_symbols(sym) { + if (sym->type == S_UNKNOWN) + continue; + + if (!sym_is_sdv(data.sdv_symbols, sym)) + sym_add_assumption(pico, sym); + } + + /* store the conflict symbols */ + conflict_syms = CF_LIST_INIT(sym); + CF_LIST_FOR_EACH(node, data.sdv_symbols, sdv) + CF_PUSH_BACK(conflict_syms, node->elem->sym, sym); + + printd("Solving SAT-problem..."); + start = clock(); + + res = picosat_sat(pico, -1); + + end = clock(); + time = ((double)(end - start)) / CLOCKS_PER_SEC; + printd("done. (%.6f secs.)\n\n", time); + + if (res == PICOSAT_SATISFIABLE) { + printd("===> PROBLEM IS SATISFIABLE <===\n"); + + ret = CF_LIST_INIT(sfl); + + } else if (res == PICOSAT_UNSATISFIABLE) { + printd("===> PROBLEM IS UNSATISFIABLE <===\n"); + printd("\n"); + + ret = rangefix_run(pico, &data); + } else { + printd("Unknown if satisfiable.\n"); + + ret = CF_LIST_INIT(sfl); + } + + CF_LIST_FREE(data.sdv_symbols, sdv); + return ret; +} + +/* + * check whether a symbol is a conflict symbol + */ +static bool sym_is_conflict_sym(struct symbol *sym) +{ + struct sym_node *node; + + CF_LIST_FOR_EACH(node, conflict_syms, sym) + if (sym == node->elem) + return true; + + return false; +} + +/* + * check whether all conflict symbols are set to their target values + */ +static bool syms_have_target_value(struct sfix_list *list) +{ + struct symbol_fix *fix; + struct sfix_node *node; + + CF_LIST_FOR_EACH(node, list, sfix) { + fix = node->elem; + + if (!sym_is_conflict_sym(fix->sym)) + continue; + + sym_calc_value(fix->sym); + + if (sym_is_boolean(fix->sym)) { + if (fix->tri != sym_get_tristate_value(fix->sym)) + return false; + } else { + if (strcmp(str_get(&fix->nb_val), + sym_get_string_value(fix->sym)) != 0) + return false; + } + } + + return true; +} + +/* + * + * apply the fixes from a diagnosis + */ +int apply_fix(struct sfix_list *fix) +{ + struct symbol_fix *sfix; + struct sfix_node *node, *next; + unsigned int no_symbols_set = 0, iterations = 0, manually_changed = 0; + size_t fix_size = list_size(&fix->list); + struct sfix_list *tmp = CF_LIST_COPY(fix, sfix); + + printd("Trying to apply fixes now...\n"); + + while (no_symbols_set < fix_size && !syms_have_target_value(fix)) { + if (iterations > fix_size * 2) { + printd("\nCould not apply all values :-(.\n"); + return manually_changed; + } + + list_for_each_entry_safe(node, next, &tmp->list, node) { + sfix = node->elem; + + /* update symbol's current value */ + sym_calc_value(sfix->sym); + + /* value already set? */ + if (sfix->type == SF_BOOLEAN) { + if (sfix->tri == + sym_get_tristate_value(sfix->sym)) { + list_del(&node->node); + no_symbols_set++; + continue; + } + } else if (sfix->type == SF_NONBOOLEAN) { + if (strcmp(str_get(&sfix->nb_val), + sym_get_string_value(sfix->sym)) == + 0) { + list_del(&node->node); + no_symbols_set++; + continue; + } + } else { + perror("Error applying fix. Value set for disallowed."); + } + + /* could not set value, try next */ + if (sfix->type == SF_BOOLEAN) { + if (!sym_set_tristate_value(sfix->sym, + sfix->tri)) + continue; + } else if (sfix->type == SF_NONBOOLEAN) { + if (!sym_set_string_value( + sfix->sym, + str_get(&sfix->nb_val))) + continue; + } else { + perror("Error applying fix. Value set for disallowed."); + } + + /* could set value, remove from tmp */ + manually_changed++; + if (sfix->type == SF_BOOLEAN) { + printd("%s set to %s.\n", + sym_get_name(sfix->sym), + tristate_get_char(sfix->tri)); + } else if (sfix->type == SF_NONBOOLEAN) { + printd("%s set to %s.\n", + sym_get_name(sfix->sym), + str_get(&sfix->nb_val)); + } + + list_del(&node->node); + no_symbols_set++; + } + + iterations++; + } + + printd("Fixes successfully applied.\n"); + + return manually_changed; +} + +/* + * stop RangeFix after the next iteration + */ +void interrupt_rangefix(void) +{ + stop_rangefix = true; +} + +/* + * check whether all symbols are already within range + */ +static bool sdv_within_range(struct sdv_list *symbols) +{ + struct symbol_dvalue *sdv; + struct sdv_node *node; + + CF_LIST_FOR_EACH(node, symbols, sdv) { + sdv = node->elem; + + assert(sym_is_boolean(sdv->sym)); + + if (sdv->tri == sym_get_tristate_value(sdv->sym)) + continue; + + if (!sym_tristate_within_range(sdv->sym, sdv->tri)) + return false; + } + + return true; +} + +/* + * for use in .cc files + */ +struct sfix_list *select_solution(struct sfl_list *solutions, int index) +{ + return list_at_index(index, &solutions->list, struct sfl_node, node) + ->elem; +} + +struct symbol_fix *select_symbol(struct sfix_list *solution, int index) +{ + return list_at_index(index, &solution->list, struct sfix_node, node) + ->elem; +} diff --git a/scripts/kconfig/configfix.h b/scripts/kconfig/configfix.h new file mode 100644 index 000000000000..186273fddabf --- /dev/null +++ b/scripts/kconfig/configfix.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Patrick Franz + */ + +#ifndef CONFIGFIX_H +#define CONFIGFIX_H + +/* make functions accessible from xconfig */ +#ifdef __cplusplus +extern "C" { +#endif + +/* include own definitions */ +#include "cf_defs.h" + +/* external functions */ +struct sfix_list **run_satconf(struct symbol_dvalue **symbols, size_t n, + size_t *num_solutions); +struct sfl_list *run_satconf_list(struct sdv_list *symbols); +int apply_fix(struct sfix_list *fix); +int run_satconf_cli(const char *Kconfig_file); +void interrupt_rangefix(void); +struct sfix_list *select_solution(struct sfl_list *solutions, int index); +struct symbol_fix *select_symbol(struct sfix_list *solution, int index); + +/* make functions accessible from xconfig */ +#ifdef __cplusplus +} +#endif +#endif From patchwork Mon Oct 28 03:49:47 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13852979 Received: from mail-ej1-f49.google.com (mail-ej1-f49.google.com [209.85.218.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F277618CC07; Mon, 28 Oct 2024 03:50:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730087427; cv=none; b=lAJXNd4yRXYcCgVyTUmGvE/qfCrcIKDenURPc+k9Y5JqYt0eJKpbPNP5+XYAjo8T5N3Ga7h5ZyYds8UxY9hSpV/hmB4I2ifK06C4iwxHl2yoeoYF+RG4msEd+HNXWR83ZsV9Eu8B+xIvpyOxspbRl5TKb6C/7y5Ncan/FZXytg4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730087427; c=relaxed/simple; bh=74in5p3OafFMmivZN5SGE2t2R5tt9i1EO/WqOwWF0rQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=iBEIldUFmDiAcvFsZVlk/jdrT9kXAkAJxKILsOxjfRFPF+Q2ZUHkwukOm6j1CZVuV0m8H259Z2TxzCeKZ1JylmSNv5PvFUUCJWXUcjvjG8itQQ6GyQoAN1mUjwbVx61gtZSWuRMdpk+KcBCGs0ZKLgY7e8tmCLXfLzAvQRfCXts= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=RwQKaJPB; arc=none smtp.client-ip=209.85.218.49 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="RwQKaJPB" Received: by mail-ej1-f49.google.com with SMTP id a640c23a62f3a-a9a0c40849cso636718466b.3; Sun, 27 Oct 2024 20:50:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1730087421; x=1730692221; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=6ZF1Fu1a/3XRLiIbO32gAuENb0GF29YprTUjnrCbVd8=; b=RwQKaJPBEmzro6uFK5SQvw+Ysbl5RY9hAc/Be3B1KooACqMaVdaE+CJJ86nowIcoHj +Yymp4IdTjIchNUbjPT2LnJ4qaIlkrd5ITgoaMtBG6KTmo40XGbJ4HHESxKYI72OnmXP oQxFaZWdsEQwhA6/IXeWsx8HHMfCXMfp2XvreP8HsQJnWRftOKx8Qa8KEIfTg15O+V+r RyOOMD2rpIbvLBin/MOQcIEDtxuLIi5mSUQs7v2Dk1G4gZXlAJlKXjoC7vdYPmSckVZf wriCXMGJ/dn9Z16nEYLMlyh2kpAEOSBqCP8cvLsyhpY4GFGWnJ3FSoN4sBsL/PyhrgKq 2Aig== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1730087421; x=1730692221; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=6ZF1Fu1a/3XRLiIbO32gAuENb0GF29YprTUjnrCbVd8=; b=QFGf9Uxl5YAQRfI4CWpl6kHHZfCTMkdTaEEHp+eTsMa0rHhEIhIh71bftm4spCxzSp wwydpAGxxv1sDvsHWOg0PWR+VpgPE3czSNsxL418NVg4wxUNdlXrHBRfw3To3wdKro+5 TEQ53la0Rpol+OEZJivpERZriL1peTnOGEpFqJoR1Yg+wt0zLGDjWVQ50+F2Rid9SWlR z2rDqIInyWyK65TBYfaK4hBweTXEqKPxiNKJDoxgwEEijP1Xvs/82xfGBzK0I4fIqWDy zoXisQMDKX0LxI7NZQ4D0+P8JTL+dOqGzAskt/aPByXiojfinDOSTxTOyu1NWh1G0sgx XGXw== X-Forwarded-Encrypted: i=1; AJvYcCWx3kN+CmekDiK0rzouTm7kwQ9uG4NUm4rgB1qIv2oGQGpd5pec83epniNuXeV2ol+9XtOMNqpI0ic8aA4=@vger.kernel.org X-Gm-Message-State: AOJu0Yyb3rHfhfKByLkDovTRz9/EgCHwZC0/BmmS3dO5s7jagp9CzR47 oAAqmRpm1Z0Y3PBf190rQOC8J1akeLCYcRwHrI7Gv06OHam8Gl+ejME9Gw== X-Google-Smtp-Source: AGHT+IEcA9Qpxt6kAK3eaxaZMQN6mWD/CMT8pqw7zbFyEZynDxUVFPkQIuUWQr3MJHYM2k5J0dNH3A== X-Received: by 2002:a17:907:72d1:b0:a99:65c6:7f34 with SMTP id a640c23a62f3a-a9de5c91b63mr601649666b.7.1730087421040; Sun, 27 Oct 2024 20:50:21 -0700 (PDT) Received: from localhost.localdomain ([2a02:908:e842:bf20:78b:e59b:2b0:d2e9]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9b30f5932fsm334599366b.168.2024.10.27.20.50.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 27 Oct 2024 20:50:20 -0700 (PDT) From: Ole Schuerks To: linux-kbuild@vger.kernel.org Cc: ole0811sch@gmail.com, jude.gyimah@rub.de, thorsten.berger@rub.de, deltaone@debian.org, jan.sollmann@rub.de, mcgrof@kernel.org, masahiroy@kernel.org, linux-kernel@vger.kernel.org, nathan@kernel.org, nicolas@fjasle.eu Subject: [PATCH v6 09/11] kconfig: Add xconfig-modifications Date: Mon, 28 Oct 2024 04:49:47 +0100 Message-Id: <20241028034949.95322-10-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20241028034949.95322-1-ole0811sch@gmail.com> References: <20241028034949.95322-1-ole0811sch@gmail.com> Precedence: bulk X-Mailing-List: linux-kbuild@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The tool can be called from any configurator. We chose to modify xconfig for this purpose. These files contain the necessary modifications to xconfig in order to resolve conflicts. Co-developed-by: Patrick Franz Signed-off-by: Patrick Franz Co-developed-by: Ibrahim Fayaz Signed-off-by: Ibrahim Fayaz Reviewed-by: Luis Chamberlain Tested-by: Evgeny Groshev Suggested-by: Sarah Nadi Suggested-by: Thorsten Berger Signed-off-by: Thorsten Berger Signed-off-by: Ole Schuerks --- scripts/kconfig/qconf.cc | 676 ++++++++++++++++++++++++++++++++++++++- scripts/kconfig/qconf.h | 111 +++++++ 2 files changed, 785 insertions(+), 2 deletions(-) diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index 90f139480eda..e899fa573db4 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -19,21 +19,40 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lkc.h" +#include #include -#include #include "lkc.h" #include "qconf.h" +#include "configfix.h" +#include "picosat_functions.h" #include "images.h" +static QString tristate_value_to_string(tristate val); +static tristate string_value_to_tristate(QString s); static QApplication *configApp; static ConfigSettings *configSettings; QAction *ConfigMainWindow::saveAction; +static bool picosat_available; + ConfigSettings::ConfigSettings() : QSettings("kernel.org", "qconf") { @@ -406,7 +425,7 @@ void ConfigList::updateSelection(void) ConfigItem* item = (ConfigItem*)selectedItems().first(); if (!item) return; - + emit selectionChanged(selectedItems()); menu = item->menu; emit menuChanged(menu); if (!menu) @@ -540,6 +559,7 @@ void ConfigList::changeValue(ConfigItem* item) } if (oldexpr != newexpr) ConfigList::updateListForAll(); + emit updateConflictsViewColorization(); break; default: break; @@ -899,6 +919,7 @@ void ConfigList::contextMenuEvent(QContextMenuEvent *e) action, &QAction::setChecked); action->setChecked(showName); headerPopup->addAction(action); + headerPopup->addAction(addSymbolFromContextMenu); } headerPopup->exec(e->globalPos()); @@ -919,6 +940,7 @@ QList ConfigList::allLists; QAction *ConfigList::showNormalAction; QAction *ConfigList::showAllAction; QAction *ConfigList::showPromptAction; +QAction *ConfigList::addSymbolFromContextMenu; void ConfigList::setAllOpen(bool open) { @@ -1239,7 +1261,11 @@ ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow *parent) info, &ConfigInfoView::setInfo); connect(list, &ConfigList::menuChanged, parent, &ConfigMainWindow::setMenuLink); + connect(list, &ConfigList::menuChanged, + parent, &ConfigMainWindow::conflictSelected); + connect(list, &ConfigList::updateConflictsViewColorization, this, + &ConfigSearchWindow::updateConflictsViewColorizationFowarder); layout1->addWidget(split); QVariant x, y; @@ -1262,6 +1288,11 @@ ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow *parent) this, &ConfigSearchWindow::saveSettings); } +void ConfigSearchWindow::updateConflictsViewColorizationFowarder(void) +{ + emit updateConflictsViewColorization(); +} + void ConfigSearchWindow::saveSettings(void) { if (!objectName().isEmpty()) { @@ -1349,6 +1380,24 @@ ConfigMainWindow::ConfigMainWindow(void) helpText = new ConfigInfoView(split2, "help"); setTabOrder(configList, helpText); + split3 = new QSplitter(split2); + split3->setOrientation(Qt::Vertical); + conflictsView = new ConflictsView(split3, "help"); + /* + * conflictsSelected signal in conflictsview triggers when a conflict is + * selected in the view. this line connects that event to conflictselected + * event in mainwindow which updates the selection to match (in the + * configlist) the symbol that was selected. + */ + connect(conflictsView, &ConflictsView::conflictSelected, this, + &ConfigMainWindow::conflictSelected); + connect(conflictsView, &ConflictsView::refreshMenu, this, + &ConfigMainWindow::refreshMenu); + connect(menuList, &ConfigList::updateConflictsViewColorization, + conflictsView, &ConflictsView::updateConflictsViewColorization); + connect(configList, &ConfigList::updateConflictsViewColorization, + conflictsView, &ConflictsView::updateConflictsViewColorization); + configList->setFocus(); backAction = new QAction(QPixmap(xpm_back), "Back", this); @@ -1415,6 +1464,10 @@ ConfigMainWindow::ConfigMainWindow(void) ConfigList::showAllAction->setCheckable(true); ConfigList::showPromptAction = new QAction("Show Prompt Options", optGroup); ConfigList::showPromptAction->setCheckable(true); + ConfigList::addSymbolFromContextMenu = + new QAction("Add symbol from context menu"); + connect(ConfigList::addSymbolFromContextMenu, &QAction::triggered, + conflictsView, &ConflictsView::addSymbol); QAction *showDebugAction = new QAction("Show Debug Info", this); showDebugAction->setCheckable(true); @@ -1470,6 +1523,8 @@ ConfigMainWindow::ConfigMainWindow(void) connect(configList, &ConfigList::menuChanged, helpText, &ConfigInfoView::setInfo); + connect(configList, &ConfigList::menuChanged, + conflictsView, &ConflictsView::menuChanged); connect(configList, &ConfigList::menuSelected, this, &ConfigMainWindow::changeMenu); connect(configList, &ConfigList::itemSelected, @@ -1478,6 +1533,8 @@ ConfigMainWindow::ConfigMainWindow(void) this, &ConfigMainWindow::goBack); connect(menuList, &ConfigList::menuChanged, helpText, &ConfigInfoView::setInfo); + connect(menuList, &ConfigList::menuChanged, + conflictsView, &ConflictsView::menuChanged); connect(menuList, &ConfigList::menuSelected, this, &ConfigMainWindow::changeMenu); @@ -1687,6 +1744,13 @@ void ConfigMainWindow::showSplitView(void) menuList->setFocus(); } +void ConfigMainWindow::conflictSelected(struct menu *men) +{ + configList->clearSelection(); + menuList->clearSelection(); + emit(setMenuLink(men)); +} + void ConfigMainWindow::showFullView(void) { singleViewAction->setEnabled(true); @@ -1822,6 +1886,536 @@ void ConfigMainWindow::conf_changed(bool dirty) saveAction->setEnabled(dirty); } +void ConfigMainWindow::refreshMenu(void) +{ + configList->updateListAll(); +} + +void QTableWidget::dropEvent(QDropEvent *event) +{ +} + +void ConflictsView::addPicoSatNote(QToolBar &toolbar) +{ + QLabel &label = *new QLabel; + auto &iconLabel = *new QLabel(); + iconLabel.setPixmap( + style()->standardIcon( + QStyle::StandardPixmap::SP_MessageBoxInformation) + .pixmap(20, 20)); + label.setText( + "The conflict resolver requires that PicoSAT is available as a library."); + QAction &showDialog = *new QAction(); + showDialog.setIconText("Install PicoSAT..."); + toolbar.addWidget(&iconLabel); + toolbar.addWidget(&label); + toolbar.addAction(&showDialog); + connect(&showDialog, &QAction::triggered, + [this]() { (new PicoSATInstallInfoWindow(this))->show(); }); +} + +ConflictsView::ConflictsView(QWidget *parent, const char *name) + : Parent(parent) +{ + /* + * - topLevelLayout + * - picoSatContainer + * - picoSatLayout + * - ... + * - conflictsViewContainer + * - horizontalLayout + * - verticalLayout + * - solutionLayout + */ + currentSelectedMenu = nullptr; + setObjectName(name); + QVBoxLayout *topLevelLayout = new QVBoxLayout(this); + QWidget *conflictsViewContainer = new QWidget; + if (!picosat_available) { + conflictsViewContainer->setDisabled(true); + QWidget *picoSatContainer = new QWidget; + topLevelLayout->addWidget(picoSatContainer); + QHBoxLayout *picoSatLayout = new QHBoxLayout(picoSatContainer); + QToolBar *picoToolbar = new QToolBar(picoSatContainer); + picoSatLayout->addWidget(picoToolbar); + picoSatLayout->addStretch(); + addPicoSatNote(*picoToolbar); + } + topLevelLayout->addWidget(conflictsViewContainer); + + QHBoxLayout *horizontalLayout = new QHBoxLayout(conflictsViewContainer); + QVBoxLayout *verticalLayout = new QVBoxLayout; + verticalLayout->setContentsMargins(0, 0, 0, 0); + conflictsToolBar = + new QToolBar("ConflictTools", conflictsViewContainer); + // toolbar buttons [n] [m] [y] [calculate fixes] [remove] + QAction *addSymbol = new QAction("Add Symbol"); + QAction *setConfigSymbolAsNo = new QAction("N"); + QAction *setConfigSymbolAsModule = new QAction("M"); + QAction *setConfigSymbolAsYes = new QAction("Y"); + fixConflictsAction_ = new QAction("Calculate Fixes"); + QAction *removeSymbol = new QAction("Remove Symbol"); + QMovie *loadingGif = new QMovie("scripts/kconfig/loader.gif"); + auto loadingLabel = new QLabel; + + if (loadingGif->isValid()) { + loadingGif->setScaledSize(loadingGif->scaledSize().scaled( + 20, 20, Qt::KeepAspectRatioByExpanding)); + loadingGif->start(); + loadingLabel->setMovie(loadingGif); + } else { + loadingLabel->setText("Calculating..."); + } + + //if you change the order of buttons here, change the code where + //module button was disabled if symbol is boolean, selecting module button + //depends on a specific index in list of action + fixConflictsAction_->setCheckable(false); + conflictsToolBar->addAction(addSymbol); + conflictsToolBar->addAction(setConfigSymbolAsNo); + conflictsToolBar->addAction(setConfigSymbolAsModule); + conflictsToolBar->addAction(setConfigSymbolAsYes); + conflictsToolBar->addAction(fixConflictsAction_); + conflictsToolBar->addAction(removeSymbol); + // loadingLabel->setMargin(5); + loadingLabel->setContentsMargins(5, 5, 5, 5); + loadingAction = conflictsToolBar->addWidget(loadingLabel); + loadingAction->setVisible(false); + + verticalLayout->addWidget(conflictsToolBar); + + connect(addSymbol, &QAction::triggered, this, + &ConflictsView::addSymbol); + connect(setConfigSymbolAsNo, &QAction::triggered, this, + &ConflictsView::changeToNo); + connect(setConfigSymbolAsModule, &QAction::triggered, this, + &ConflictsView::changeToModule); + connect(setConfigSymbolAsYes, &QAction::triggered, this, + &ConflictsView::changeToYes); + connect(removeSymbol, &QAction::triggered, this, + &ConflictsView::removeSymbol); + connect(this, &ConflictsView::resultsReady, this, + &ConflictsView::updateResults); + //connect clicking 'calculate fixes' to 'change all symbol values to fix all conflicts' + // no longer used anymore for now. + connect(fixConflictsAction_, &QAction::triggered, this, + &ConflictsView::calculateFixes); + + conflictsTable = (QTableWidget *)new dropAbleView(this); + conflictsTable->setRowCount(0); + conflictsTable->setColumnCount(3); + conflictsTable->setSelectionBehavior(QAbstractItemView::SelectRows); + conflictsTable->setEditTriggers(QAbstractItemView::NoEditTriggers); + + conflictsTable->setHorizontalHeaderLabels( + QStringList() << "Name" << "Wanted value" << "Current value"); + verticalLayout->addWidget(conflictsTable); + + conflictsTable->setDragDropMode(QAbstractItemView::DropOnly); + setAcceptDrops(true); + + connect(conflictsTable, &QTableWidget::cellClicked, this, + &ConflictsView::cellClicked); + horizontalLayout->addLayout(verticalLayout); + + // populate the solution view on the right hand side: + QVBoxLayout *solutionLayout = new QVBoxLayout(); + solutionLayout->setContentsMargins(0, 0, 0, 0); + solutionSelector = new QComboBox(); + connect(solutionSelector, + QOverload::of(&QComboBox::currentIndexChanged), + [=](int index) { changeSolutionTable(index); }); + solutionTable = new QTableWidget(); + solutionTable->setRowCount(0); + solutionTable->setColumnCount(2); + solutionTable->setHorizontalHeaderLabels(QStringList() + << "Name" << "New Value"); + solutionTable->setEditTriggers(QAbstractItemView::NoEditTriggers); + + applyFixButton = new QPushButton("Apply Selected solution"); + connect(applyFixButton, &QPushButton::clicked, this, + &ConflictsView::applyFixButtonClick); + + numSolutionLabel = new QLabel("Solutions:"); + solutionLayout->addWidget(numSolutionLabel); + solutionLayout->addWidget(solutionSelector); + solutionLayout->addWidget(solutionTable); + solutionLayout->addWidget(applyFixButton); + + horizontalLayout->addLayout(solutionLayout); +} + +void ConflictsView::applyFixButtonClick() +{ + signed int solution_number = solutionSelector->currentIndex(); + + if (solution_number == -1 || solution_output == NULL) { + return; + } + + apply_fix(solution_output[solution_number]); + + ConfigList::updateListForAll(); + for (int i = 0; i < conflictsTable->rowCount(); i++) { + conflictsTable->item(i, 2)->setText( + conflictsTable->item(i, 1)->text()); + } + updateConflictsViewColorization(); + QMessageBox msgBox; + msgBox.setText("The solution has been applied."); + msgBox.exec(); +} + +void ConflictsView::changeToYes() +{ + QItemSelectionModel *select = conflictsTable->selectionModel(); + if (select->hasSelection()) { + QModelIndexList rows = select->selectedRows(); + for (int i = 0; i < rows.count(); i++) { + conflictsTable->item(rows[i].row(), 1) + ->setText(tristate_value_to_string(yes)); + } + } +} + +void ConflictsView::changeToModule() +{ + QItemSelectionModel *select = conflictsTable->selectionModel(); + if (select->hasSelection()) { + QModelIndexList rows = select->selectedRows(); + for (int i = 0; i < rows.count(); i++) { + conflictsTable->item(rows[i].row(), 1) + ->setText(tristate_value_to_string(mod)); + } + } +} + +void ConflictsView::changeToNo() +{ + QItemSelectionModel *select = conflictsTable->selectionModel(); + if (select->hasSelection()) { + QModelIndexList rows = select->selectedRows(); + for (int i = 0; i < rows.count(); i++) { + conflictsTable->item(rows[i].row(), 1) + ->setText(tristate_value_to_string(no)); + } + } +} + +void ConflictsView::menuChanged(struct menu *m) +{ + currentSelectedMenu = m; +} + +void ConflictsView::addSymbol() +{ + addSymbolFromMenu(currentSelectedMenu); +} + +void ConflictsView::selectionChanged(QList selection) +{ + currentSelection = selection; +} + +void ConflictsView::addSymbolFromMenu(struct menu *m) +{ + // adds a symbol to the conflict resolver list + if (m != nullptr) { + if (m->sym != nullptr) { + struct symbol *sym = m->sym; + tristate currentval = sym_get_tristate_value(sym); + //if symbol is not added yet: + QAbstractItemModel *tableModel = + conflictsTable->model(); + QModelIndexList matches = tableModel->match( + tableModel->index(0, 0), Qt::DisplayRole, + QString(sym->name)); + if (matches.isEmpty()) { + conflictsTable->insertRow( + conflictsTable->rowCount()); + conflictsTable->setItem( + conflictsTable->rowCount() - 1, 0, + new QTableWidgetItem(sym->name)); + conflictsTable->setItem( + conflictsTable->rowCount() - 1, 1, + new QTableWidgetItem( + tristate_value_to_string( + currentval))); + conflictsTable->setItem( + conflictsTable->rowCount() - 1, 2, + new QTableWidgetItem( + tristate_value_to_string( + currentval))); + } else { + conflictsTable->item(matches[0].row(), 2) + ->setText(tristate_value_to_string( + currentval)); + } + } + } +} + +void ConflictsView::addSymbolFromContextMenu() +{ + struct menu *menu; + + if (currentSelection.count() < 0) { + return; + } + for (auto el : currentSelection) { + ConfigItem *item = (ConfigItem *)el; + if (!item) { + continue; + } + menu = item->menu; + addSymbolFromMenu(menu); + } +} + +void ConflictsView::removeSymbol() +{ + QItemSelectionModel *select = conflictsTable->selectionModel(); + QAbstractItemModel *itemModel = select->model(); + if (select->hasSelection()) { + QModelIndexList rows = select->selectedRows(); + itemModel->removeRows(rows[0].row(), rows.size()); + } +} + +void ConflictsView::cellClicked(int row, int column) +{ + auto itemText = conflictsTable->item(row, 0)->text().toUtf8().data(); + struct property *prop; + struct menu *men; + struct symbol *sym = sym_find(itemText); + + if (sym == NULL) + return; + prop = sym->prop; + men = prop->menu; + // uncommenting following like somehow disables click signal of 'apply selected solution' + if (sym->type == symbol_type::S_BOOLEAN) { + //disable module button + conflictsToolBar->actions()[2]->setDisabled(true); + } else { + //enable module button + conflictsToolBar->actions()[2]->setDisabled(false); + } + if (column == 1) { + // cycle to new value + tristate old_val = string_value_to_tristate( + conflictsTable->item(row, 1)->text()); + tristate new_val = old_val; + switch (old_val) { + case no: + new_val = mod; + break; + case mod: + new_val = yes; + break; + case yes: + new_val = no; + break; + } + if (sym->type == S_BOOLEAN && new_val == mod) + new_val = yes; + conflictsTable->item(row, 1)->setText( + tristate_value_to_string(new_val)); + } + emit(conflictSelected(men)); +} + +void ConflictsView::changeSolutionTable(int solution_number) +{ + size_t i; + + if (solution_output == nullptr || solution_number < 0) { + return; + } + struct sfix_list *selected_solution = solution_output[solution_number]; + current_solution_number = solution_number; + solutionTable->setRowCount(0); + i = 0; + for (struct list_head *curr = selected_solution->list.next; + curr != &selected_solution->list; curr = curr->next, ++i) { + solutionTable->insertRow(solutionTable->rowCount()); + struct symbol_fix *cur_symbol = + select_symbol(selected_solution, i); + + QTableWidgetItem *symbol_name = + new QTableWidgetItem(cur_symbol->sym->name); + + solutionTable->setItem(solutionTable->rowCount() - 1, 0, + symbol_name); + + if (cur_symbol->type == symbolfix_type::SF_BOOLEAN) { + QTableWidgetItem *symbol_value = new QTableWidgetItem( + tristate_value_to_string(cur_symbol->tri)); + solutionTable->setItem(solutionTable->rowCount() - 1, 1, + symbol_value); + } else if (cur_symbol->type == symbolfix_type::SF_NONBOOLEAN) { + QTableWidgetItem *symbol_value = + new QTableWidgetItem(cur_symbol->nb_val.s); + solutionTable->setItem(solutionTable->rowCount() - 1, 1, + symbol_value); + } else { + QTableWidgetItem *symbol_value = + new QTableWidgetItem(cur_symbol->disallowed.s); + solutionTable->setItem(solutionTable->rowCount() - 1, 1, + symbol_value); + } + } + updateConflictsViewColorization(); +} + +void ConflictsView::updateConflictsViewColorization(void) +{ + auto green = QColor(0, 170, 0); + auto red = QColor(255, 0, 0); + auto grey = QColor(180, 180, 180); + + if (solutionTable->rowCount() == 0 || current_solution_number < 0) + return; + + for (int i = 0; i < solutionTable->rowCount(); i++) { + QTableWidgetItem *symbol = solutionTable->item(i, 0); + //symbol from solution list + struct symbol_fix *cur_symbol = select_symbol( + solution_output[current_solution_number], i); + + // if symbol is editable but the value is not the target value from solution we got, the color is red + // if symbol is editable but the value is the target value from solution we got, the color is green + // if symbol is not editable , the value is not the target value, the color is grey + // if symbol is not editable , the value is the target value, the color is green + auto editable = sym_string_within_range( + cur_symbol->sym, + tristate_value_to_string(cur_symbol->tri) + .toStdString() + .c_str()); + auto _symbol = + solutionTable->item(i, 0)->text().toUtf8().data(); + struct symbol *sym_ = sym_find(_symbol); + + tristate current_value_of_symbol = sym_get_tristate_value(sym_); + tristate target_value_of_symbol = string_value_to_tristate( + solutionTable->item(i, 1)->text()); + bool symbol_value_same_as_target = current_value_of_symbol == + target_value_of_symbol; + + if (editable && !symbol_value_same_as_target) { + symbol->setForeground(red); + } else if (editable && symbol_value_same_as_target) { + symbol->setForeground(green); + } else if (!editable && !symbol_value_same_as_target) { + symbol->setForeground(grey); + } else if (!editable && symbol_value_same_as_target) { + symbol->setForeground(green); + } + } +} + +void ConflictsView::runSatConfAsync() +{ + //loop through the rows in conflicts table adding each row into the array: + struct symbol_dvalue *p = nullptr; + std::vector wanted_symbols; + + p = static_cast(calloc( + conflictsTable->rowCount(), sizeof(struct symbol_dvalue))); + if (!p) { + printf("memory allocation error\n"); + return; + } + + for (int i = 0; i < conflictsTable->rowCount(); i++) { + struct symbol_dvalue *tmp = (p + i); + auto _symbol = + conflictsTable->item(i, 0)->text().toUtf8().data(); + struct symbol *sym = sym_find(_symbol); + + tmp->sym = sym; + tmp->type = static_cast( + sym->type == symbol_type::S_BOOLEAN ? 0 : 1); + tmp->tri = string_value_to_tristate( + conflictsTable->item(i, 1)->text()); + wanted_symbols.push_back(tmp); + } + fixConflictsAction_->setText("Cancel"); + loadingAction->setVisible(true); + + solution_output = run_satconf(wanted_symbols.data(), + wanted_symbols.size(), &num_solutions); + + free(p); + emit resultsReady(); + { + std::lock_guard lk{ satconf_mutex }; + satconf_cancelled = true; + } + satconf_cancellation_cv.notify_one(); +} + +void ConflictsView::updateResults(void) +{ + fixConflictsAction_->setText("Calculate Fixes"); + loadingAction->setVisible(false); + if (!(solution_output == nullptr || num_solutions == 0)) { + solutionSelector->clear(); + for (unsigned int i = 0; i < num_solutions; i++) + solutionSelector->addItem(QString::number(i + 1)); + // populate the solution table from the first solution gotten + numSolutionLabel->setText( + QString("Solutions: (%1) found").arg(num_solutions)); + changeSolutionTable(0); + } else { + QMessageBox msgBox; + msgBox.setText("All symbols are already within range."); + msgBox.exec(); + } + if (runSatConfAsyncThread->joinable()) { + runSatConfAsyncThread->join(); + delete runSatConfAsyncThread; + runSatConfAsyncThread = nullptr; + } +} + +void ConflictsView::calculateFixes() +{ + if (conflictsTable->rowCount() == 0) { + printd("table is empty\n"); + return; + } + + if (runSatConfAsyncThread == nullptr) { + // fire away asynchronous call + std::unique_lock lk{ satconf_mutex }; + + numSolutionLabel->setText(QString("Solutions: ")); + solutionSelector->clear(); + solutionTable->setRowCount(0); + satconf_cancelled = false; + runSatConfAsyncThread = + new std::thread(&ConflictsView::runSatConfAsync, this); + } else { + printd("Interrupting rangefix\n"); + interrupt_rangefix(); + std::unique_lock lk{ satconf_mutex }; + satconf_cancellation_cv.wait(lk, [this] { + return satconf_cancelled == true; + }); + } +} + +void ConflictsView::changeAll(void) +{ + // not implemented for now + return; +} + +ConflictsView::~ConflictsView(void) +{ +} + void fixup_rootmenu(struct menu *menu) { struct menu *child; @@ -1871,6 +2465,8 @@ int main(int ac, char** av) fixup_rootmenu(&rootmenu); //zconfdump(stdout); + picosat_available = load_picosat(); + configApp = new QApplication(ac, av); configSettings = new ConfigSettings(); @@ -1891,3 +2487,79 @@ int main(int ac, char** av) return 0; } + +dropAbleView::dropAbleView(QWidget *parent) + : QTableWidget(parent) +{ +} + +dropAbleView::~dropAbleView() +{ +} +void dropAbleView::dropEvent(QDropEvent *event) +{ + event->acceptProposedAction(); +} + +static QString tristate_value_to_string(tristate val) +{ + switch (val) { + case yes: + return QString::fromStdString("Y"); + case mod: + return QString::fromStdString("M"); + case no: + return QString::fromStdString("N"); + default: + return QString::fromStdString(""); + } +} + +static tristate string_value_to_tristate(QString s) +{ + if (s == "Y") + return tristate::yes; + else if (s == "M") + return tristate::mod; + else if (s == "N") + return tristate::no; + else + return tristate::no; +} + +PicoSATInstallInfoWindow::PicoSATInstallInfoWindow(QWidget *parent) + : QDialog(parent) +{ + QVBoxLayout &layout = *new QVBoxLayout(this); + QLabel &text = *new QLabel(); + text.setWordWrap(true); + layout.addWidget(&text); + text.setTextFormat(Qt::MarkdownText); + text.setTextInteractionFlags(Qt::TextSelectableByMouse); + text.setTextInteractionFlags(Qt::TextBrowserInteraction); + text.setOpenExternalLinks(true); + text.setText(R""""( +Install the picosat package or build the library yourself: + +## Debian-based distributions + +```sh +sudo apt install picosat +``` + +## Fedora + +```sh +sudo dnf install picosat +``` + +## Other + +You can also build PicoSAT yourself from the +[sources](https://fmv.jku.at/picosat/picosat-965.tar.gz). You need to compile +PicoSAT with tracing enabled as a shared library under the name of +"libpicosat-trace.so", "libpicosat-trace.so.0" or "libpicosat-trace.so.1". +Tracing can be enabled using the `configure.sh` script with the `--trace` +option. + )""""); +} diff --git a/scripts/kconfig/qconf.h b/scripts/kconfig/qconf.h index aab25ece95c6..807b7a47544c 100644 --- a/scripts/kconfig/qconf.h +++ b/scripts/kconfig/qconf.h @@ -14,8 +14,17 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #include "expr.h" +#include "cf_defs.h" + class ConfigList; class ConfigItem; @@ -80,6 +89,8 @@ public slots: void parentSelected(void); void gotFocus(struct menu *); void showNameChanged(bool on); + void selectionChanged(QList selection); + void updateConflictsViewColorization(); public: void updateListAll(void) @@ -111,6 +122,82 @@ public slots: static void updateListAllForAll(); static QAction *showNormalAction, *showAllAction, *showPromptAction; + static QAction *addSymbolFromContextMenu; + +}; + +class ConflictsView : public QWidget { + Q_OBJECT + typedef class QWidget Parent; +private: + QAction *loadingAction; +public: + ConflictsView(QWidget *parent, const char *name = 0); + ~ConflictsView(void); + void addSymbolFromMenu(struct menu *m); + int current_solution_number = -1; + +public slots: + void cellClicked(int, int); + void changeAll(); + // triggered by Qactions on the tool bar that adds/remove symbol + void addSymbol(); + // triggered from config list right click -> add symbols + void addSymbolFromContextMenu(); + void removeSymbol(); + void menuChanged(struct menu *); + void changeToNo(); + void changeToYes(); + void changeToModule(); + void selectionChanged(QList selection); + + void applyFixButtonClick(); + void updateConflictsViewColorization(); + void updateResults(); + + // switches the solution table with selected solution index from solution_output + void changeSolutionTable(int solution_number); + + // calls satconfig to solve to get wanted value to current value + void calculateFixes(); +signals: + void showNameChanged(bool); + void showRangeChanged(bool); + void showDataChanged(bool); + void conflictSelected(struct menu *); + void refreshMenu(); + void resultsReady(); +public: + QTableWidget *conflictsTable; + + // the comobox on the right hand side. used to select a solution after + // getting solution from satconfig + QComboBox *solutionSelector{nullptr}; + + // the table which shows the selected solution showing variable = New value changes + QTableWidget *solutionTable{nullptr}; + + // Apply fixes button on the solution view + QPushButton *applyFixButton{nullptr}; + + struct sfix_list **solution_output{nullptr}; + size_t num_solutions; + + QToolBar *conflictsToolBar; + struct menu *currentSelectedMenu; + QLabel *numSolutionLabel{nullptr}; + // currently selected config items in configlist. + QList currentSelection; + QAction *fixConflictsAction_{nullptr}; + void runSatConfAsync(); + std::thread *runSatConfAsyncThread{nullptr}; + + std::mutex satconf_mutex; + std::condition_variable satconf_cancellation_cv; + bool satconf_cancelled{false}; + +private: + void addPicoSatNote(QToolBar &layout); }; class ConfigItem : public QTreeWidgetItem { @@ -214,6 +301,12 @@ public slots: bool _showDebug; }; +class PicoSATInstallInfoWindow : public QDialog { + Q_OBJECT +public: + PicoSATInstallInfoWindow(QWidget *parent); +}; + class ConfigSearchWindow : public QDialog { Q_OBJECT typedef class QDialog Parent; @@ -223,6 +316,9 @@ class ConfigSearchWindow : public QDialog { public slots: void saveSettings(void); void search(void); + void updateConflictsViewColorizationFowarder(); +signals: + void updateConflictsViewColorization(); protected: QLineEdit* editField; @@ -258,6 +354,8 @@ public slots: void showIntro(void); void showAbout(void); void saveSettings(void); + void conflictSelected(struct menu *); + void refreshMenu(); protected: void closeEvent(QCloseEvent *e); @@ -266,10 +364,23 @@ public slots: ConfigList *menuList; ConfigList *configList; ConfigInfoView *helpText; + ConflictsView *conflictsView; + QToolBar *conflictsToolBar; QAction *backAction; QAction *singleViewAction; QAction *splitViewAction; QAction *fullViewAction; QSplitter *split1; QSplitter *split2; + QSplitter *split3; +}; + +class dropAbleView : public QTableWidget +{ +public: + dropAbleView(QWidget *parent = nullptr); + ~dropAbleView(); + +protected: + void dropEvent(QDropEvent *event); }; From patchwork Mon Oct 28 03:49:48 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13852978 Received: from mail-ej1-f53.google.com (mail-ej1-f53.google.com [209.85.218.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F0EB718D62F; Mon, 28 Oct 2024 03:50:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730087427; cv=none; b=VGbCMewQkMjafPIqDtquInq8cLJtrwSMiiGVFT1N3RwIZfWk9bLPnd16mTmfJL6OTLeoH2DHWjz1h+iZ15CCx3ku0/golnbod5wwnrOs0CMyLqKgvkGNDRgnl62OakKsAzmCQ9vLZy/oFbL0HwT9VUSY48QuqN4EcJzqikVJnvA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730087427; c=relaxed/simple; bh=S/2EAtWp6JPLCvIE0+ZuLlyNzRcL0LTLF2JmlRHtFNQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=NzEmWbIgFJW2wvfldvVRJQLEhHEqgrY7TirMJm6stsuwh49+edUCrqCPUPzqDjD6dMLoreGW0KH3PUkhECtyytxu+V2Wp1lPz06IxKSGcFKQLfX8rKPq9fQsNFSYPA1cXWRNIdqjfWGmPdUAtRgx/YxrL2Twb80DPhwWmkTgLI4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=AiSHriuc; arc=none smtp.client-ip=209.85.218.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="AiSHriuc" Received: by mail-ej1-f53.google.com with SMTP id a640c23a62f3a-a9a0c7abaa6so471348866b.2; Sun, 27 Oct 2024 20:50:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1730087423; x=1730692223; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=FQwdxRW+PlqWzgd2iVA2/71Jy8/QEccppk3FUkvYKjs=; b=AiSHriuc0EWALVpY75I6gh9lbZeyjQtxgJX7JL50Z0Iswva36CqPx0TXNyVACiaOak L/PwmsVXrTX5vj6dYpD9GloQNLsuLTXChN4AFr0DptU/QLfUUz2tW1XM3waaJfxUvE57 IXCW549jbknS5JMB3eAYGqniEuiXZqUVcZFEXIN5dFLVH7X5ahJmNVpiuoXl/7zxMAEI ECcxHPQstdNwchIR4xCpnvhv6Mux7njvFgvRCtgz+y6GrPOt3/5V37rfeWTi13dSbG9/ UNN82gwHfwNrBwZl0E0KOb0PzIX874eLh+6ispczM14rp5IXXSFcz0Jit9ipAUBqshAv Zj1w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1730087423; x=1730692223; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=FQwdxRW+PlqWzgd2iVA2/71Jy8/QEccppk3FUkvYKjs=; b=xOX/Jd2dwO54Vcdu8WuS2+35Qjm/EYeVtrWwaMiCtwib5BbMb/+Z4XEei21XedX6sb hZNXAE4pW2z0rELq6xxJuj5p99aCZa/iNq/XXbjvXL+XaOKSGNtBKmRo2tJgHHyrA7Lu LnfIh0Qhd1cBeooevFcF1RpZ4XEb/X2KtBqhPaKS5wWAKQu9wV0Sxzco8e5xtAlYg/09 JHDxZ/sWLjnWs0TUcgG/wpGjOdJUYrm7yjrZ5txujveyfa3qf+TpYkewn3V7hoTpv8Vn 64qvg+K8LnML7JtT7beXYVNb64LhlUDnlC8uZ8sk1mQvGbNGEjbzfrCjn7W0rzIZ9Li2 m1kw== X-Forwarded-Encrypted: i=1; AJvYcCWIc2X9JZylkpzI+eZM+fqsgY9PwEt7iY7oOPgWCoN2w2Sf1SiEACu5twJZko4X2mfHUTlnLQuxZ5VNJJ0=@vger.kernel.org X-Gm-Message-State: AOJu0YyWl+CuYPINTA7JeqqCAp1+uIgJsNhE1au/7H9K3WEtyAIG7mIg jvG5ib3verqQmSTsR1njPo2X2mppTAAiwzYdqWGKcPCJNlo6aFf4cEkTEQ== X-Google-Smtp-Source: AGHT+IFpus0bkIGgWdb7A87mgGgBvb/1ztMTbcHRvJCtg3YR7M7/fj644AL/cb4Exc11XlcI3LoG6A== X-Received: by 2002:a17:907:9487:b0:a99:c0be:a8ac with SMTP id a640c23a62f3a-a9de5ee477dmr657347966b.37.1730087423033; Sun, 27 Oct 2024 20:50:23 -0700 (PDT) Received: from localhost.localdomain ([2a02:908:e842:bf20:78b:e59b:2b0:d2e9]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9b30f5932fsm334599366b.168.2024.10.27.20.50.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 27 Oct 2024 20:50:22 -0700 (PDT) From: Ole Schuerks To: linux-kbuild@vger.kernel.org Cc: ole0811sch@gmail.com, jude.gyimah@rub.de, thorsten.berger@rub.de, deltaone@debian.org, jan.sollmann@rub.de, mcgrof@kernel.org, masahiroy@kernel.org, linux-kernel@vger.kernel.org, nathan@kernel.org, nicolas@fjasle.eu Subject: [PATCH v6 10/11] kconfig: Add loader.gif Date: Mon, 28 Oct 2024 04:49:48 +0100 Message-Id: <20241028034949.95322-11-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20241028034949.95322-1-ole0811sch@gmail.com> References: <20241028034949.95322-1-ole0811sch@gmail.com> Precedence: bulk X-Mailing-List: linux-kbuild@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add the GIF used as the loading indicator. We created the file from scratch using GIMP. It's also available at https://github.com/delta-one/linux/blob/configfix_20240607-patches-5/scripts/kconfig/loader.gif Signed-off-by: Ole Schuerks --- scripts/kconfig/loader.gif | Bin 0 -> 4177 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 scripts/kconfig/loader.gif GIT binary patch literal 4177 zcmcJSdoowDoS2_pjR&n>ennze`g}IK4nH%)TK$4p_fnSMn@-@K!=f z&NbG2m_V+Xp1M@hY5I}j9lCcF8FkoN$Lg9xyJyywTS}_topYfBh=z}E+zX~&X@~Z7 zWwC?N6Et$;(6sf6m>*Q!9Y@3n4qI~B{*2*&JMp{j93dCfR}8&fnA|<-J%Dpc4e=?! z$CcKh?Aw&4n8k!^b0@s-<@ulvN=3vWd}4Pj<@aPH8BSe9S5!b)ZYHF%DofRVx&2(VB|lP$JbTW~4|~?9o^)xsr>T_FaOMeTW~UJY#8xwZ7(ut4rxYQj#g>b+ZO%QJ@^Z8fqF_ zat%VFE5O#268USLUt%=dGZ#@{=%emoFZO08k}5{L7wyg&gn)=}=HQ4Xph?ZfBgl*= zO~8N4qQC3Nzuigs>dtwLX}rpV71p%3K&}tA0GLQQn?(6JC(=Bh0iZm z`ZTMw0lTp1F9Fq+E6E=ou1;2vsv>uGfuk_C-Rog%R^dP0y?^>%rsyMAvtfT-WAlo|7B_y>^SY zDPytw;pi`ds{-PjfGA819Mk}4%vb{PF5F)^{&^#-iFg&A=qHKTeTpLYWoQSUkltuy zr;lBYLuu!r4NFKZ0v3dwCdTmYWNyj+(?EGQ8E-%$i8SrdwH)wNXf_Tmt6%;x+r&DT z9sDpuT7Wn-q)1HlbCU~nO%}5NSRymbc{=ke$=@v5Rexpvet50zL&TNt`N8JM$RLVu zgV&7306>22W$2~H-c2|MQwIBrY64Ah8nTzH4u$(gcq;O1*#Ak95o)N4;$f??ju`6b^YYb@`)m9zRg)#C zkfCHU&~i4!@TdL@gySuUz|nco+zGRm_NpCTQkT1(HPTx!ut_DE1OY(*BNS_W6P?_D zfnEUPFs_dj!~4@mzG=+w!=9UJ6C&xuD7bWV(e?-RDF?bSZVaqRAo%TCJO*M~6{9T& zOK_{1b!{!^7fIXFB9}U9(7@Ukq2Y9BPme~%eI)yMy;n*FI0#;DM59@Osk_YO`TNO@ zZWjI50k~>{;0@^67~X<#Q|&in%1U#I(K|Qg?)8>`b8zy>VcW6RcS?PZXcsHpg%JG@ z{MXyJSI5w0Qc9+$m}gGmD;4TJo1}^^R1U0Jd7h+K5SS#nyR-IL_E9a9zN6XR^W)V) z;}f3*0Y#gV%?U6I;S?;03}@g*GyuFs`Qpv^HoSdhP3+xqnc$~3(xTo|Ho3~hl?a_X z?^d5VqEk%5U?DSW#*yuSN}P;bN^>D(R{a#-5*bkP7H$cB5g_`hy&bW)`oJZ_7pIsn z3Uw*ASI*?Dq|<=q&J@nD^$||EKE(|~=_)1wZ{T=i_}os+Y|9tIdk#KPxzrirF?usk zG+o}Z$i*|vYkMnQA<#8`oKv{CXMU|Sa<|NTXRutd;pW<)8W6shZ{O8Y?_3Q@*v$6= zBt&+lpk{#+Q=>fM(-N^Pb8MDAcr(~EPkLVrLJ?PNfkj>iX}D8(L!fUbmI$(hL~w^} zjb_JHkh`u*QDUWS?w~-GvrI>{D#_oK)>poWT%_;=i=ON@SC`uS+EnLYzthXItI<>CSA4HTdnMV}FE7pr5@AQN)>>L;IJ`TSn;j+H z^Fny=#nG*PE%r$7AW2ZHyhP^yj|9~>tqDqvay8Vhmb~NNzg`EQaEez13#aH=RZ0x( z!&@-)k8ed>?h#9dZw<6@syeSD0IUaL)Ewq4^cLQx?aCW_y%J2yL=;|RtLtHzVle%{k0%etg%-DzbPLOMiyU)>r-tTGP=1biBvA_aV*Mvvpm>S>TZxr!khws*s>`k>SA5ZtF{7H0owqv5I z(JDFjGb0}e?Ytqzgc0CrL~OvMcrhb}{S6py<9=0T=ku)aE_kqRkTf7x-X>G@&Yg06 zEBSb{pAVgmW;GvJMtiqg32}5HsPa`7SL#P z)s@W+#7(^1(~YJzkn>$cnC#WQW7V`rajbYFesPhrBd6gMOiVg9rtlbVcerID!8a$g zC+pBCbaq_%(z755VyuYGEfk@0mf`65DYp(h0k6~x`OM+hsd9#(z%nUu^yeKHHH|G$ zK#vMYeKGJ5BR(SM!H+gQS!vhi?8e?Xc1}8XXNRrU9~d09sp%9$A+0!Ku3FG!BBNd0 zZIH|xiJVdOoB*LRp*wpe4W_XR>J|xow7U*j033%`}tn)r1X(UZg`rXOo=l9#(0wEwa zOshB&H&PSh*YUqNdbhdw4?Za?4HI7+56TlYaS=!bm6Uqfwfew%(dcNbiCxIznga+T zQ;gas2O|TIPT$y#H}xF4g+uIYuAr8wYSz$bl|5^iD=dwk`PRE)zAv)vOru1kr`FXr zouw9wpg8VOtrQL;8M?KA_lV&p$!{a(%R#|ioB_9{ihO1w5L8K&nrfdrI?gJrEQGP< zfPoC(1AyhCpH9m&!7T+P3j6q5y2?kPSz=SUjh* zb#&%MB)b~o2QZ7)A`rzhYPG1VybE=sM7XEkAE$r#c%vUqL8{lL6L2be#e47}H`?vn z(<@QxvMI#rIOdI0_!4<5&z1ytTh+#9t4KFXpQd1{0J-HYwl^wkK(PqRNox!Lxw9*b z!4_)}g!jV&;1oP_1+S&}jY#7*?;qwr&#NXbbWRCzd@=?Jo=AQwBXT`NILG33;Wh_^ zZ0EcQtJji5-2+odSnA94>GmlEn@SG^npUZ)EX+v|_H2{SWBhDq2)95ap1 z+nwJn0Dtk^i{_qt1Q%WByCp`Rp$BWt6=8Pkk=1ly6Ich|$Wj}98(KVsz(|0|jq@~I zhzWUUsS&b8&5Djpr6`ibnK({SH1EdwgDQXf1U@-*h&30lNm5qR*3nhznJXFn5JLLF mPrb!EzI*)gbndmHWkD#_qOspS`CK89f)W-#!ra)Lb^ZkcGe|A~ literal 0 HcmV?d00001 diff --git a/scripts/kconfig/loader.gif b/scripts/kconfig/loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..54c8bc76cc037ab04dc1d37ddc34384f9714284e From patchwork Mon Oct 28 03:49:49 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13852980 Received: from mail-ed1-f43.google.com (mail-ed1-f43.google.com [209.85.208.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B4FB018E038; Mon, 28 Oct 2024 03:50:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730087431; cv=none; b=WxmATiK/D+MzvGzIQkutXolCPDsmPAbqX7D5HPQojLEl07oF8ql72GjBHAEtTPq1DQ7nq0bbqZtIberGJs7nGX0j9NTz3tRU8eNbytMBTDRGrBNtA1/KA0PtLebkniEbmmJATeUVLQh0oWuwTS5kg+fx6MDxfLHdrxNgkWrTj/I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730087431; c=relaxed/simple; bh=xGwi4psJJOZC8oqFMX4FH7oNE+Fjw6HTeW5J9RMFR3c=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=CfsS4U9y4WhyJnetWVLJ8tv4+BTgRkY2Ups8Z1HzXCnHcB7TZnrCd8Y7mEbBMwp1R1RXEXuoaXycvlCqoSGCsRuNWRCMoMbjxrfdwcj9JQYJVAwkHdffcNE52J5FQWOOY9VtkYI3Wnkxlje0Lg+SO5kJu5Gpgz2ZKd9YCEPwnJQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Auo9mzGr; arc=none smtp.client-ip=209.85.208.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Auo9mzGr" Received: by mail-ed1-f43.google.com with SMTP id 4fb4d7f45d1cf-5cbb6166c06so3063047a12.0; Sun, 27 Oct 2024 20:50:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1730087427; x=1730692227; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=kGngZIwfO/jqpHla3vwHAdrO/yliWLs6xLpeLI2Xezk=; b=Auo9mzGrM4x5M8+FX0AL3SIsENh1vjDKFjy9CLTRPVBzBj2JZuw27KTApDJxaXKXcO JskGYT2xEfeh7q4xUhbjk2QhdDl/Gz2Fhw71oL+967xRNy2AAe6iXtO1TYGSSq5KUDR0 XFFa3mlEcv2B73/0vN60lXQ+ZgWCSeWZilzjIPaAIuuipwoc8g6F3LMMgZaziFQfWGIN eZwqL018VKLf5JHiuhVF87Fs++Koz7Ew6g2YHPl6vj0bPPBA8Vqczkt+lyMAJQIFSv5t qpU+/ehmVJn2Lt4e9rxH5UzOBEaD08b5HkklH0g/Qd2/xHmpLSDtsyXCpyqOaPZ1wv7H C5ag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1730087427; x=1730692227; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=kGngZIwfO/jqpHla3vwHAdrO/yliWLs6xLpeLI2Xezk=; b=LRdMpoKH8Eego6jiogbHQIyQytAu/7EaYdWwTTh1PNPa8QPjhk40zFf5Wz7RN8Jbee dKpSbD6x7IdIcNgFKVhcTBa+AdeXpbj7lhC0qDMN7A9TQq5CiuoqimG73YB+neB6FZNJ bVPUAfDy6Z6l7TYZTafgkG6i+cm2yuJfRExnjrno/6fbi2xQAS25OVMUnBxVzps4SCWV KQl7GXSexymHer2QRSSl+0xGmVnsXfXWL3pw+FdnduJ1SIPPk1fkNHxBaDaLb16FtgXj R2rUQjPaT8CXzhoJ5qpw/j2LWpvAuaxtrAfHOnpkc30MnVZ086CwBUxKhZh9II9Zshiv Spcg== X-Forwarded-Encrypted: i=1; AJvYcCV9aIo11UKNBkJYj7r5RschTus7VF/gz7PeYAkWbX4qDmpq1du4d372A7ZV0qXBhxxsFexnkAu6tp4VhYU=@vger.kernel.org X-Gm-Message-State: AOJu0YzP97Bht1ATUWF1nDqj/dsx1kax0/Rc7SogN8SLUdNfTzGIXwTS t82dnv1w9wBbWPnlMex4NVwoRcieK7lpl+6wpV7/9sMxtpu/bSGfeSr+ww== X-Google-Smtp-Source: AGHT+IHMClniOpjo8Gx0250GBhDj14kOzacMomo6b2Lbb6wULifLkWN21LVHSugZd/m+oD2cDO+l7w== X-Received: by 2002:a17:907:7d8a:b0:a9a:122b:e545 with SMTP id a640c23a62f3a-a9de5f26cd7mr577554066b.32.1730087426913; Sun, 27 Oct 2024 20:50:26 -0700 (PDT) Received: from localhost.localdomain ([2a02:908:e842:bf20:78b:e59b:2b0:d2e9]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9b30f5932fsm334599366b.168.2024.10.27.20.50.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 27 Oct 2024 20:50:26 -0700 (PDT) From: Ole Schuerks To: linux-kbuild@vger.kernel.org Cc: ole0811sch@gmail.com, jude.gyimah@rub.de, thorsten.berger@rub.de, deltaone@debian.org, jan.sollmann@rub.de, mcgrof@kernel.org, masahiroy@kernel.org, linux-kernel@vger.kernel.org, nathan@kernel.org, nicolas@fjasle.eu Subject: [PATCH v6 11/11] kconfig: Add documentation for the conflict resolver Date: Mon, 28 Oct 2024 04:49:49 +0100 Message-Id: <20241028034949.95322-12-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20241028034949.95322-1-ole0811sch@gmail.com> References: <20241028034949.95322-1-ole0811sch@gmail.com> Precedence: bulk X-Mailing-List: linux-kbuild@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add documentation for the interface of the conflict resolver and instructions for installing PicoSAT. The target audience is everyone using xconfig, in particular, kernel developers, end users, system administrators, and distributors. Signed-off-by: Ole Schuerks --- Documentation/kbuild/kconfig.rst | 56 ++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/Documentation/kbuild/kconfig.rst b/Documentation/kbuild/kconfig.rst index fc4e845bc249..d49ab303e757 100644 --- a/Documentation/kbuild/kconfig.rst +++ b/Documentation/kbuild/kconfig.rst @@ -285,6 +285,62 @@ Searching in xconfig: You can also enter a different search string without having to return to the main menu. +Conflict resolution +------------------- + + xconfig has support for conflict resolution. A conflict is in this case any + situation where you want to change the value of a symbol, but + unfulfilled dependencies prevent this. You can create a list of symbols + and their desired values, and the conflict resolver will calculate a series + of changes in xconfig, which allows setting the symbols to their desired + values. + +Requirements: + + To use the conflict resolver, PicoSAT needs to be installed as a library. + + Debian-based distributions:: + + sudo apt install picosat + + Fedora:: + + sudo dnf install picosat + + You can also build PicoSAT yourself from the `sources + `_. The conflict resolver + requires that PicoSAT is built with tracing enabled (e.g., using the + configure.sh script with the "--trace" option). It expects the shared + library to be named "libpicosat-trace.so", "libpicosat-trace.so.0" or + "libpicosat-trace.so.1". + +Usage: + + To add a symbol to the list of symbols whose values should be changed (that + is, the 'conflict'), you select the symbol in the main view of xconfig. With + the button "Add symbol" you add the symbol to the conflict, which makes it + appear in a table below the main view. You need to switch to "Show Prompt + Options" under the tab "Option" if the symbol is hidden in the main view. + You can set the desired value of a symbol by either clicking on the + corresponding cell in the column "Wanted Value," or by selecting the + symbol's row and using one of the buttons above the table. + + Once the 'conflict' is declared, the solutions can be calculated using the + button "Calculate Fixes". Once calculated, they appear in the menu on the + bottom right. You can select a solution from up to three candidates. The + solutions are presented in a table that shows which values the symbols need + to have to resolve the conflict. Using the button "Apply selected solution" + the indicated changes can automatically be applied. If you want to change + the values manually, the symbols are color-coded to indicate the order in + which they need to be set: Green means that a symbol is already set to the + calculated value. Gray means that a symbol cannot yet be set to the + calculated value and that other symbols' values need to be changed first. + Red means that a symbol is not yet set to the calculated value, but that you + can set it to the calculated value. + + Note that in rare cases the conflict resolver cannot resolve the conflict + even when a solution exists, it suggests unnecessary changes, or it suggests + changes that do not resolve the conflict. gconfig =======