From patchwork Fri Sep 20 08:56:18 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13808358 Received: from mail-ed1-f41.google.com (mail-ed1-f41.google.com [209.85.208.41]) (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 9D89F34CF5; Fri, 20 Sep 2024 08:56:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726822607; cv=none; b=eBHv+Q6LHx0xMYAP8j6J7eyMZJaHerATYiT3s9/R5CgApl3MWerP0ICvqV4rLWIGIlyNgWdL1LRGFLpqh8xye70Q6uf9ipjiqFATN1mwEPbS3RvN6I9l0THJwnc6KfCyyNIiYA3Z374LEiCsEfWvzjgsebGzPULxb8Gehjhbx58= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726822607; c=relaxed/simple; bh=Irg2GzBKQZUEkCk4UB0i5rJX3bypgVmO3Uz93YVGkOw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=k7G71cR19NrnJSikOQHvQGLmAPuiJxflRRqYSlaZc05rXl3yV/n9L97W1QuUuF2E0O4oIjDHmibPCc9UYAGXN3iDVbG623QgwZnBHEF3ss/Vf4hsEVxjaQNJBoqk0DHDY3awPLyq214FHIp8wzNfpChYJl3Pl8UOAvnG0y9LKR8= 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=EGAkg7VV; arc=none smtp.client-ip=209.85.208.41 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="EGAkg7VV" Received: by mail-ed1-f41.google.com with SMTP id 4fb4d7f45d1cf-5c46680a71bso437447a12.2; Fri, 20 Sep 2024 01:56:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1726822604; x=1727427404; 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=zuBVbFf1k61tFrHxuv3u3r5EVuVStgJFdmZAOMLjXg4=; b=EGAkg7VVfVwjYiUTLmRfvbsz5raDXSELcdlpqLmps1tfxvhUxK66qxwl8LCQFGuOUR xVSbHeNdg1x4jiwYEow04UiaZ1f/S90noxBkqGeOQ5zCl8TXtZr1NVEoUEYcZfxJeeyu VD01sxuRjtZPX994y2Poi4ImCcXiBXTY2S9A600AJr2r3CIa9+tjuAcuNx3fEGwB2OQp GeANDoEnogkm/AafWat5ZYbLHlV2u8FPwNBpbNEmKDU88hp2/hIGCOCGfkW9wHsZV0VK AjgZqe9IyutFZA74oNcV34DRt5XNZYxzybNhD+DvGHc91E+c+BKN8u1sKMzxL+KCNBxr cd/w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726822604; x=1727427404; 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=zuBVbFf1k61tFrHxuv3u3r5EVuVStgJFdmZAOMLjXg4=; b=sPe7UAfpe0m3Vd/w3JK+GFiwgDl46n5un6o+C0O+JH7gXrLHB6W37pdrNxMFAdXx+E 1xIKD1M0NZud2dcMxDDGhMdlRbYYZ+PcSqiDC8/dubNKGVaXn7kVg1l9SRv9u4VyDmB6 Q18zG3fxEJ7mFfCN8JuUATVfHGKzh5KPXbwzRH9J5/DUT6rQ9Cy+h6+5+o5GO3s/07/H wY584sy55R3W1fnHXp52YcG1TxX9poPr7kbakCjTzxp2CuIW7HqPJQUn5/Nx2YB1MT1X N8SehPLc0Y+vrGKqO7J4hD6mdqzfXWC9GejVVhqs3GHipSZtAGrUniMhRMleInv9Ud1N c9sA== X-Forwarded-Encrypted: i=1; AJvYcCXFCMqYY8YeyjnOepHUl0ZXA7aW4mer7JW0bSDUHCrrZhw3AAc6EQgcWa5UCCODRd2+bouQUUDLMLUfntA=@vger.kernel.org X-Gm-Message-State: AOJu0Yzu/6N35DFJwNzpVcKdGND68nzNxkqYSiIWEE+frisJjUJ4/0s3 PkoAeNdopibUJmA8x25cl9MpueRqwcMms6V2nMjZzGLAayYDklvsyNE7Ng== X-Google-Smtp-Source: AGHT+IEui0lGllXaB2nDV3EznWZIkawQfwzGCJbAWqlQMYGwpy+N24PXX+4D/QRvLLkzBiedPaU4/A== X-Received: by 2002:a17:907:e647:b0:a88:b93b:cdcb with SMTP id a640c23a62f3a-a90d510a264mr158134366b.47.1726822603671; Fri, 20 Sep 2024 01:56:43 -0700 (PDT) Received: from localhost.localdomain ([2a02:908:e842:bf20:422c:48db:9094:2fa9]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a906109637esm817861866b.40.2024.09.20.01.56.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Sep 2024 01:56:43 -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 v5 01/11] kconfig: Add PicoSAT interface Date: Fri, 20 Sep 2024 10:56:18 +0200 Message-Id: <20240920085628.51863-2-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240920085628.51863-1-ole0811sch@gmail.com> References: <20240920085628.51863-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/install-picosat.sh | 29 +++++++++++ scripts/kconfig/picosat_functions.c | 74 +++++++++++++++++++++++++++++ scripts/kconfig/picosat_functions.h | 35 ++++++++++++++ 3 files changed, 138 insertions(+) create mode 100755 scripts/kconfig/install-picosat.sh create mode 100644 scripts/kconfig/picosat_functions.c create mode 100644 scripts/kconfig/picosat_functions.h diff --git a/scripts/kconfig/install-picosat.sh b/scripts/kconfig/install-picosat.sh new file mode 100755 index 000000000000..aadfa9582ecb --- /dev/null +++ b/scripts/kconfig/install-picosat.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +psinstdir=$(mktemp -d) +if [ $? -ne 0 ]; then + echo "mktemp failed" + exit 1 +fi +cd $psinstdir +wget "https://fmv.jku.at/picosat/picosat-965.tar.gz" +tar -xf picosat-965.tar.gz +cd picosat-965 +cp makefile.in makefile.in2 +# change soname to conform with packages for Debian and Fedora +sed -e "s,-soname -Xlinker libpicosat.so,-soname -Xlinker \ + libpicosat-trace.so.0," makefile.in2 > makefile.in +./configure.sh -O -t --shared +make libpicosat.so +install -m 0755 -p libpicosat.so /usr/local/lib/libpicosat-trace.so.0.0.965 \ +&& ln -s -f libpicosat-trace.so.0.0.965 /usr/local/lib/libpicosat-trace.so.0 \ +&& ln -s -f libpicosat-trace.so.0 /usr/local/lib/libpicosat-trace.so \ +&& ldconfig +echo +if [ $? -ne 0 ]; then + echo "Installation of PicoSAT failed, make sure you are running with root privileges." + exit 1 +else + echo "Installation of PicoSAT succeeded." +fi 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 Fri Sep 20 08:56:19 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13808359 Received: from mail-lf1-f49.google.com (mail-lf1-f49.google.com [209.85.167.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 75FF913AA4E; Fri, 20 Sep 2024 08:56:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726822611; cv=none; b=BuVE/pkF8UgioXR5iF3PlTT8vsbStAnjpjIj2rK7W7yU1l2Btg+E7nXrlr+jqXt8x/or/ttrDk8jJpIAubDotYNFzdIMh9bhDzax2CQxA05GdYTDHgyM9XJuopybuGLb1qzz01fE3dbVU0Nk3AT2ez9efx5EXikDYQ/v8Twf0KM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726822611; c=relaxed/simple; bh=5BvIsrv/SYo12MNQp2fGXf9p61NAqzS/ebLIY4c714I=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=kGXE7bkDYO5+3l0xwKefOz4JPyhOP7gH7Ayr75yB57xD95YAGfbCu/T/tljix2O3X4MnUahiVAMVuafh6irHdurOoewb8Km5Ehg9APGHkNShxxU9wa2p42iYrcoblvtOQhqiYT7aw+cyUTx7jFjKq5WKgxUkmqF7R4tJOnCATSw= 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=f45deTzf; arc=none smtp.client-ip=209.85.167.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="f45deTzf" Received: by mail-lf1-f49.google.com with SMTP id 2adb3069b0e04-5356aa9a0afso3090609e87.2; Fri, 20 Sep 2024 01:56:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1726822607; x=1727427407; 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=nvBAAu8Benq3E7BygRhUhfDHzBHVGBUbyRnXkgCn8P8=; b=f45deTzfj7bagbZJ6qXUCoryMkTmrzA5/6ii1yOuH2VIRN88ZrVrh389NXP1X4d95M uMqTcjxfs+voJ267r3LgmCQxdkwTykSCLP8e1zrDjXmqkAjh8myEfouPBQiVjYs3T2yu HZPVjJLoBtAmIfMWOSqyHQvdaQUW/tRIOeQMDeZiO1vTe/NPXfJIXDPGR74mi3i+9YJX qI3b7LIEpQ4cKJTOOybv7BV1aUIjR/vtwRX2mw/XrAyRl2aq7KgW/+dSXvby3Y2PWU+p j8r3s7mfdm4u8E2Om20VgNqjYH3B5jv7OiNu/iC1SG/S5D1ACc1xdooBzwtwXXMs1k8w RAJg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726822607; x=1727427407; 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=nvBAAu8Benq3E7BygRhUhfDHzBHVGBUbyRnXkgCn8P8=; b=JtqI/uHVRjJ7NPTJs2nq4b9D5E2J/IrFImCo3ct68SpvQoyZdb309O4zxDeBLRgzpt QoG8JA6ZrXN8urNBEjL4XY4CXsFlbNhr8riuWruOvqRLWxcI7zxMeo3lQ3CtKSEUVi/i yIulbXnq13ibeWZ4RePQH+PuCNq2t83zO3sMOcfe8nbf0tWf+13q4TOcts4ccF1PQ7GK 0NtrwZ/L3IQHMk5ysxOpfai7mvpdF0v5A7Bo062zOnXS/aLJs6ZSlBR4a1lKQ/6DTfvS WRXNiHqyPje0f6ZjrKqTjAauBG3pBXdspxQR9yAhlUc43ZArUfOe/wIv6uxAc37/YEVT 5c/Q== X-Forwarded-Encrypted: i=1; AJvYcCV8XyDnh/8/6oAUdZAbQlRyGFrihDbV2+yKdSz4wxnlCwcGX9DwFl/qJHw03O0fdZG5sXzFUKuNTyCoVwo=@vger.kernel.org X-Gm-Message-State: AOJu0YzMmcWUL2EVdCsCnPAaaJquCwpJxcl+ZkUgtcRcBELa1yx1KfbV CbVNV70LLP/QcBubE2eF+dHTCaR0lcEz6Cce1j4v9J9hgVUM1cTxHKK/MA== X-Google-Smtp-Source: AGHT+IFnVKVosd4YHg+3YEFot/4uRKU9Gi186/8x6lrf4mVHCvvIZH8uzTapBg1xgXcsa4s1ZHXLdA== X-Received: by 2002:a05:6512:3d0e:b0:535:6baa:8c5d with SMTP id 2adb3069b0e04-536ac2e5a42mr1630964e87.20.1726822607146; Fri, 20 Sep 2024 01:56:47 -0700 (PDT) Received: from localhost.localdomain ([2a02:908:e842:bf20:422c:48db:9094:2fa9]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a906109637esm817861866b.40.2024.09.20.01.56.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Sep 2024 01:56:46 -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 v5 02/11] kbuild: Add list_is_{first,last}, list_size, list_at_index, list_for_each_from Date: Fri, 20 Sep 2024 10:56:19 +0200 Message-Id: <20240920085628.51863-3-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240920085628.51863-1-ole0811sch@gmail.com> References: <20240920085628.51863-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_is_first and list_is_last respectively check whether an entry is the first or last entry of the list. 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 | 71 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/scripts/include/list.h b/scripts/include/list.h index fea1e2b79063..f7aff1749a0b 100644 --- a/scripts/include/list.h +++ b/scripts/include/list.h @@ -169,6 +169,43 @@ static inline int list_empty(const struct list_head *head) return head->next == head; } +/** + * list_is_first - tests whether @list belongs to the first entry + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_first(const struct list_head *list, + const struct list_head *head) +{ + return list == head->next; +} + +/** + * list_is_last - tests whether @list belongs to the last entry + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_last(const struct list_head *list, + const struct list_head *head) +{ + return list == head->prev; +} + +/** + * 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. @@ -260,6 +297,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 Fri Sep 20 08:56:20 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13808360 Received: from mail-lf1-f47.google.com (mail-lf1-f47.google.com [209.85.167.47]) (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 6063213B5A9; Fri, 20 Sep 2024 08:56:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726822614; cv=none; b=NPnB45pmmrSgmt6iuKpuCrME55aOP8BOeth0UJ2D8gunVmVC3OA2KMXQQv9P23QUiOAR129dV9uQFHR+IMNH+m8pXCIZRx9j9EEXPtqIa2M7dq+T/R3czkDABtk2dyTo9oJY7LyU7MATk9PUFRXtWib+CAlQToeEsJ0035dAn3M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726822614; c=relaxed/simple; bh=bKENTHIl8F7xWMDrqvFFgSh71F7kKIZc1OUJgp8ZAm4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=dtz55I9+Fav/kcx70BJPpCdfz75kjn2EEaEv6L5C0rWJ4G+/4wXL6lkfoeR0sNXMnot7uGjRlclrfcOvU2GZxAQ4LR8OSyU4Lrh0PxNuRHWTdgx03HUqp7SqXddsz/Y5uNJzTVzpE4XA8tUSrdP7Akc+U5Wo/znLfATVGTQ13us= 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=HZK2ZBQb; arc=none smtp.client-ip=209.85.167.47 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="HZK2ZBQb" Received: by mail-lf1-f47.google.com with SMTP id 2adb3069b0e04-5356bb5522bso2218967e87.1; Fri, 20 Sep 2024 01:56:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1726822610; x=1727427410; 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=qji/5aly/dXIhGWxIxaA7IXcYFPGUv7Kw2QbfQSvLHs=; b=HZK2ZBQbUtx/qKzNbAk2pB/vhoG1HGduuEyo47uQ4ZS8Tj8L0T3Jtg9Gcx1VkKLLZb JMmcWqLvCUhU7npQTD11c64d+5mINQZVHcVP7r62F37KSJ5BxeF/Wm9yXmx3sVZQlAVq aTPr1zNazRDKc3uk7/cPfLvIgE2TJB2RSA8ntfMYP1szaeOhNQYeavlzWeLxCCZfhL+j jkYRRBUESWfMTSKfCoDkSqcRwLE1QL1Vcoct7aYDDEUm5oq0/eNADxP6ujV4P8Ma7gc1 T4sz/dVzyJiNX+bf4HRM0bM5aZfjKuftYoe+xnE2YQfXW7+y+ZlL2cCPGDxcySBCAY8K befQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726822610; x=1727427410; 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=qji/5aly/dXIhGWxIxaA7IXcYFPGUv7Kw2QbfQSvLHs=; b=Bl1oI4FrjR9rnTljdtN/thESv5TvrDbL0HLZQpMFUTWp3c5TmrMELMTwre3sfZAHyX GmMQ+se+FcI+iJ8kTkzGCiEv6hxv1rv9xKJnRY6cLyRknkbXXDvtfF1wNqhz9FSOz0HT JX0uuGJ/Hd9v28OHStyFd0NK9ULkR8D7mcc02K5zGGBHcBwaKeRisFm1Ema/yXf9ggGc tKEAh8PEY76VG1sy45Tm6wNNFGoSWMpTpRoN7S9Pu1HnDGazX8GCyqJYK9bSE2C3sieG 91oeGpe/A6gEtFwCHAVebuUSsc2upJSckwnReOJshwPA1W1Io0vJomqvCX/KwkF6igWO OvNA== X-Forwarded-Encrypted: i=1; AJvYcCXa6L4ixE/v9Lyfe2LMV8sYPaRXu5mj7da1UeaHSlIfz9D+d8jb0zGpJrettuzHuVcYzs8NgR/gWrxoEbo=@vger.kernel.org X-Gm-Message-State: AOJu0YyZZgiw9PcuOsJwzV4qIqATGD0c5WdH7kDfnjGS6XMeGK/eFc+i kAYVFrI71PtVX1G/GX4C63hhmyvdez5vUf7BqHs2SI7ASc73iVY3xEHgaA== X-Google-Smtp-Source: AGHT+IEsUFdvsHwT+2keF2/ldsBLzBV/KyjhDhsvLbJaqytKtOXs9UemMH0DWCEKRFIaZE5tncJfMw== X-Received: by 2002:a05:6512:3e07:b0:536:554a:24ba with SMTP id 2adb3069b0e04-536ac32e481mr1106971e87.39.1726822609972; Fri, 20 Sep 2024 01:56:49 -0700 (PDT) Received: from localhost.localdomain ([2a02:908:e842:bf20:422c:48db:9094:2fa9]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a906109637esm817861866b.40.2024.09.20.01.56.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Sep 2024 01:56:49 -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 v5 03/11] kconfig: Add definitions Date: Fri, 20 Sep 2024 10:56:20 +0200 Message-Id: <20240920085628.51863-4-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240920085628.51863-1-ole0811sch@gmail.com> References: <20240920085628.51863-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 c82d08bbd704..3b8be0e9b7dd 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -126,6 +126,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 */ @@ -187,6 +200,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 Fri Sep 20 08:56:21 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13808361 Received: from mail-ej1-f46.google.com (mail-ej1-f46.google.com [209.85.218.46]) (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 08FF113BC26; Fri, 20 Sep 2024 08:56:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726822617; cv=none; b=gvCAnMegDpkKljGUcmJZmEPUX8al+Hxb490l9mnimW6UdENCjREzasbqQFe9X5rF/DefMv/cGJPlJNf3GxiZBdjzkptWm48NXK8GxkvN/C2zrm70lyeGS2InjjsY2EDP8dTu5lOX1x70ppbLrSABjPV0gDKX0x+LGYsmC6UcREM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726822617; c=relaxed/simple; bh=DppSDWkTADuYC8A8QtyR0WLPME8rqZb2I8wilkJ7OBs=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=uJZMl3epINLxuKwsqSeYFCybRi6MjR9n5/PsHwHE52OIDRk9ySX2qR1J8Hw9yeMDdOGyWnSy6tvXZk1yTIzF/NhdWTYZ2YlxKORxrcv7B1ot/zIgfEKtV/VbgCiRVbzNYkOWvN46ES0N1SwECwlbno78M0PAMNiVyU0ceU/ASQc= 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=dkgp0udP; arc=none smtp.client-ip=209.85.218.46 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="dkgp0udP" Received: by mail-ej1-f46.google.com with SMTP id a640c23a62f3a-a7aa086b077so238431166b.0; Fri, 20 Sep 2024 01:56:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1726822612; x=1727427412; 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=w30kYie/I7kDYlUhxrK0Y05nk4BLpcF3m1+t/JjpMkA=; b=dkgp0udPUkjjW62WGaGC4uTWA+06ik8v+ZFMXdqYW41Z4/FqfWCnXMT/RaLaQTcqU6 xW0YhzR/4dfXe0NWVg42+qOBaKFnrQKc7Asf9+z1BSl+/PzPskfOC3NE2U8P9hT68RzR wDXFc/pucuFApO8uO2od2NHwwLV0MqeSNORZZ4qbMQjxg8bsa8V41kOCGFaecVb6cBLY KAzqN9DYf8s9WNIoCjCMGWl5JI9xaP/Jk4+1jEK8ss76xgBhr9RDVmuBXE9Gz7bmtXEB z5J299IPqQ8qsBePBdZ5JKS8aS14yjDPKP+p3U0UEZUyzMxLPwkwuP9BLAHGtDOR44F/ XGzA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726822612; x=1727427412; 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=w30kYie/I7kDYlUhxrK0Y05nk4BLpcF3m1+t/JjpMkA=; b=IcZAt3+aaRrkYid7NmrXlXGmUi06GiNvSX4vFuwaWEDx7aw+1HfA+qq+nloIQEMXzA r7/Fmzf2jAuJQLIOYH8zljhcLjzWgXHQjU1Eq0RHYH0XjRlETiIvAESW8jt6w63EYzyn 493U3K6NdHYqFv79UgeN4BY2FmQJfVfaiTla5RkJBGitseI4MgKez0aC9t963XiAzrij ax51yMRzD2T8YS6Fm7nZ0o8biUC9ytZZiO9QpFZEBXWzHdIekQMbHHwDPC1O8gZi3/Ya ypF5tf9Be0PajQTUu/sYVVRx8O06a1teOaH2pWR+fl53foBWnQ8ZpupZ+OwJhO7g5D1V jErQ== X-Forwarded-Encrypted: i=1; AJvYcCWpd+OqtC4DMLVIMK83pJIg8ksYCfQ9DyiTLfDs9LA7Yr/+7KcO049BD2d14+NLCyE4ssXRlgEmhd/kV8w=@vger.kernel.org X-Gm-Message-State: AOJu0Yw4v1w2upKw5BAWhGZpkej7WjSAUbp9VCq8KQh0R3rMQWh3Oz6I kRD8SUpybjsUdziWQ2BePDQ0IctyF/ctmcWIwbtCyaB75t9h5LMFTaDK6A== X-Google-Smtp-Source: AGHT+IG8tsvImSBOB1odJz/JAl7iElepOJ9K8zviMRM79l8m4CTgk7HhFZCN7FwYhsVQbXWZkofSmw== X-Received: by 2002:a17:907:3daa:b0:a86:7924:11bd with SMTP id a640c23a62f3a-a90d512700dmr195794466b.41.1726822611810; Fri, 20 Sep 2024 01:56:51 -0700 (PDT) Received: from localhost.localdomain ([2a02:908:e842:bf20:422c:48db:9094:2fa9]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a906109637esm817861866b.40.2024.09.20.01.56.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Sep 2024 01:56:51 -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 v5 04/11] kconfig: Add files for building constraints Date: Fri, 20 Sep 2024 10:56:21 +0200 Message-Id: <20240920085628.51863-5-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240920085628.51863-1-ole0811sch@gmail.com> References: <20240920085628.51863-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 | 1789 ++++++++++++++++++++++++++++++ scripts/kconfig/cf_constraints.h | 24 + 2 files changed, 1813 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..b84280ea5309 --- /dev/null +++ b/scripts/kconfig/cf_constraints.c @@ -0,0 +1,1789 @@ +// 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 = expr_copy(p->visible.expr); + else + expr = expr_alloc_and( + expr_copy(p->visible.expr), + expr_copy(ctx)); + } else { + if (ctx == NULL) + expr = expr_alloc_symbol(&symbol_yes); + else + expr = expr_alloc_and( + expr_alloc_symbol(&symbol_yes), + expr_copy(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(expr_copy(p->expr), + expr_copy(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); + expr_free(e_tmp); + } + /* 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(expr_copy(p->expr), + expr_copy(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); + expr_free(e_tmp); + } + expr_free(expr); + } +} + +/** + * 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 = expr_copy(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); + + expr_free(e); + } + + 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 Fri Sep 20 08:56:22 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13808362 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 B57ED136341; Fri, 20 Sep 2024 08:56:55 +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=1726822619; cv=none; b=TLM287R2gLAK5Gq/jrW3AwrrW7xCwbywrGC/ARqayj00BrdLMyL39P73+bVc0JX1NRsT1Daf5+ty8nJrpdUB4APvLs2HZ9VKWy3ENG1+eskl1TB0EZEAPpeM0gVInaRQ56GQyUe85V7JaI2ptyarJ1SeP/LCpjQ2ylTJHWgU0Qg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726822619; c=relaxed/simple; bh=/+igm9Vo0cuQx5SLUEUmZj0v06F1PcUK63RHWDBlERc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=s6fZbNO5r4JnjlISxiq0aB0R4w3H0n/VDdUHb3od5v5aX3ChRkZTYTHBXWwNIaaIOB93PRbTRG5dXrUBRWoMAdv1HlvGex8RLcK94M3lZEnWH7spec6HfhZIsHFdm/rbuBTM05sHUKSE+8rlizNIpsmJ7nBlwvJjlyrhiwvl+MQ= 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=J+CWYwed; 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="J+CWYwed" Received: by mail-ed1-f42.google.com with SMTP id 4fb4d7f45d1cf-5c3d209db94so2280264a12.3; Fri, 20 Sep 2024 01:56:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1726822614; x=1727427414; 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=Pbcvj02csotvo9yHIbxdDy7DD8wgjcYuNNoP3oaEwCI=; b=J+CWYwedNwiZLQK9UAI3ywlrsxC2ultMju6biX1dCsKHagx5p4aNYF31JZFGlS69Qv J0/fehkZDRHl1k0LFLwcLb2dmS64MgGuRJBXeJ+bygZa4EF0Nn2xkNObTud7LLP9ycNz oHsjg8c0Zys5AudjndeYqDzDzVk4zFYaMdjtNyIxCyPZ3qQ/LYhphEyXAB1V98TGTusN 42OfBEkwNO3D3A8F2qGlX29LojEH2eBp1KT5hpKoeA7mo15yPL2KvVcp7SFQF/ztm3s0 nk6cDHn+SGq3ycYN1LIeb0ciRT5DlnVmHNax3rABOdJ682LTy/0M3cLOJMw+8xTeOfWW +HMA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726822614; x=1727427414; 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=Pbcvj02csotvo9yHIbxdDy7DD8wgjcYuNNoP3oaEwCI=; b=Biz6G3nDcbfqLYLkmLgVs/8aBSkgc1b8c9c3BjTl/7S79hob1wHaXVIBTFVCxQy5ta VmoXCjAoz7PZsy+A19gQiPggWzEJLRa8Ln6FuSsMW+VnIlcGUwNT8X0leaNyvLDIPYHX SmDZVUj98tsCeCokw2oEQ0KV4HsD8AWA/L0qkztfzmlSvhqQguxGB9uUtY23zFCCxlkW xkiuXkrprQqwCFTQrV7mlPFW6FXO9B0U1INZFF7PWU7EblQtq1m/wpV5ZSQL0ryhAwBu KfxDv2RqwYwHAsDxJY2XxlmgiUGM4XwEnlkiRkbGTCqyLvqJF4PmGUoeI11NtfHiR8mM s/SQ== X-Forwarded-Encrypted: i=1; AJvYcCW9LH/jJgElsvwI1hdHkp3y7JaioLOT4X4eHcAH9r6dBVejGmqCOybsFuIh4ulArwYD34PakzeWEBAfi0E=@vger.kernel.org X-Gm-Message-State: AOJu0YwjiWs8WVvLniwemHK8YFv1Yj8H4sbQS1a555oirekXBb5nEyjP K06s7zACZE2QOHI6FvlQAJAZfziNafG9UHzbBJhWeeZFnL5I+6005B0a8g== X-Google-Smtp-Source: AGHT+IEHCKMdWuTjhiTCBkG1mFiIlzfavSMnKY1hcF0wQd8toraEu0y851suDMd5A6Sg9N+eh7X72w== X-Received: by 2002:a17:907:c88f:b0:a80:7ce0:8b2a with SMTP id a640c23a62f3a-a90d5610a7dmr162168266b.19.1726822613536; Fri, 20 Sep 2024 01:56:53 -0700 (PDT) Received: from localhost.localdomain ([2a02:908:e842:bf20:422c:48db:9094:2fa9]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a906109637esm817861866b.40.2024.09.20.01.56.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Sep 2024 01:56:53 -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 v5 05/11] kconfig: Add files for handling expressions Date: Fri, 20 Sep 2024 10:56:22 +0200 Message-Id: <20240920085628.51863-6-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240920085628.51863-1-ole0811sch@gmail.com> References: <20240920085628.51863-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 Fri Sep 20 08:56:23 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13808363 Received: from mail-ej1-f51.google.com (mail-ej1-f51.google.com [209.85.218.51]) (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 B1B7813C67E; Fri, 20 Sep 2024 08:56:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726822620; cv=none; b=k5e9zp6tT3YoS+hBwKj4zqYtiUQ2CxeLYT75nIFHj2bgoX4L36rJ5pzGYeYXzXlPOpiwMlJ8/pqFsWGxbszaSHg7z/Xr7rPIO87jXfKISBWTBVZnMK7Qx4H2TT6s/s3+n/VgEv/yjRFvzo91LFhBM2SSSy4NAlxSzdP1xTz/AbQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726822620; c=relaxed/simple; bh=xzj9jUA855fkRm4IgksvEN5/HnLXJWD79rWsY6qC9ZU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=I+sT6+ACxhLOU8WfBGFI1PmvcCmydX46wK9N0uoY6G2GRYETUk6SvHMbdubjJDHqLMzE8FN2Idqjrmbjb4UsfpU9/3hby5+PNF55YpDuZLIWq1Kjgq8UYUUCW1Ybw9b9/2mw00KCyEiE+wSbuFAdhWeTG6PyDVmqYqp1BlnETDI= 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=maxxaO90; arc=none smtp.client-ip=209.85.218.51 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="maxxaO90" Received: by mail-ej1-f51.google.com with SMTP id a640c23a62f3a-a8a7cdfdd80so232116566b.0; Fri, 20 Sep 2024 01:56:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1726822616; x=1727427416; 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=sWSAJgWdyoGXz8bShklr1oHdXkEfUmBJsG7oXiT9t3c=; b=maxxaO90ZHn/+CPxyYovvg+r/Yro3ckfes1sVev6XVKRSM4VRl5k/OIqsDxHtWHTj1 f8coEbSfP57Sp4pMWSwaifflOzptc/8oM9CAqjigijRgwqX7wcxl5gbFQ9mVvwbDfVO9 NE+OS9lfvKqiZraut9E0D5fn0/L7arwphkJgKQvnTBjz5jzIWY6JRmRQ10Rgi/w90jZa M9BX7OSJpddNgs9Sj2dJLf94Sfms0zB1sIKjiy2aWdzA2GX2PDwKN1zyHi3clGG1dqO9 WTZRvHAewMnuUIF2z3uG24e9jHrHgtM8txLx9P08KmoOYGnPQqg5WlQLEKa7IW2gyRcO Cjng== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726822616; x=1727427416; 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=sWSAJgWdyoGXz8bShklr1oHdXkEfUmBJsG7oXiT9t3c=; b=NEDC8rHIFn1gA4kxm9SvOwo+xhL3FRqFlDTnRTk8Zy2ahCow15SveFpZEy85RUeGao T8iwsH4u2RNl37eIqQlYMxBCMc4y84KXavNcwiZePid85p82FymxzpvynDRphOsrnjOe TgLuGcBDVGD5H/CcoHu6CchOaIIO2ob76OzPMT7ruKkg6imTUkWVcGS6AFtqmUrQUgu3 qGYKlYzwhYF7WNm6J+PDpmFXxOsLgkJxd5ECKrB1xL0ZhVnjS4ONpOFpPKw8NuADTRmv +6S9lAs+GvU1z29pfuzPgpliWjDFwvGIUVN0L00DgG3F7lSqRJ5MUFdFLgyALQGdG/My YDwQ== X-Forwarded-Encrypted: i=1; AJvYcCXDPDhlImsajeH6TDPzltVgJV89qR8ufaWD1IWhqFbQAu9YClyXOFZUXlZKMX22XWi1s1MYYYg/AA8Jgkk=@vger.kernel.org X-Gm-Message-State: AOJu0YwRXYkuS/dPi4Cy1boK/K7n1V8XCCPYbKtviwt1BxxSynJDiS41 hJ1vNWqrvfOa4/mwYk/qq4+7ipVpH4yi2yxLr/6WIlll7fI+6paJ2br4+Q== X-Google-Smtp-Source: AGHT+IEpCDoTigFh5ipJF51oaFhRT1HerEdjMbVeKOxEC03wiH/jW8oXr/R8NUmte4bd4H8iA6WCFQ== X-Received: by 2002:a17:906:794c:b0:a86:7199:af37 with SMTP id a640c23a62f3a-a90d51601d9mr188766066b.58.1726822615765; Fri, 20 Sep 2024 01:56:55 -0700 (PDT) Received: from localhost.localdomain ([2a02:908:e842:bf20:422c:48db:9094:2fa9]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a906109637esm817861866b.40.2024.09.20.01.56.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Sep 2024 01:56:55 -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 v5 06/11] kconfig: Add files for RangeFix Date: Fri, 20 Sep 2024 10:56:23 +0200 Message-Id: <20240920085628.51863-7-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240920085628.51863-1-ole0811sch@gmail.com> References: <20240920085628.51863-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 Fri Sep 20 08:56:24 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13808364 Received: from mail-lj1-f172.google.com (mail-lj1-f172.google.com [209.85.208.172]) (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 A119F13C9DE; Fri, 20 Sep 2024 08:56:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726822622; cv=none; b=IhVl2FBeUuk/hHYX1waaQ7/eMuO1V6AQWtpSJ2w155Qf80HZFyEKPLIxTbUoUjOBFu8lb02Em7Ox5lLp5fOknmywZqZvHXw4tSZzNpdDNCXFAPZXoBEK8d8IlEe8o7TxyW7Vwj5shtBMgj7lHqcgpMWEjJLzIj9oC0wSx3EE/Hw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726822622; c=relaxed/simple; bh=nkEmnjMMpF15Q31Jkp+eVrcSI8YBsCCYCnCu1AFqyUw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=gVvH9RpSvbjmDcpavtsQjTaQmWJcUGHn97v4VbHiTiuuHtmzihslQ3bb4Nh9AXcH0lkjhGT4AEtv98fC4me0Bp5h0uwgnmOK2fOLkYfaZRhlFRkCc01Zeo+T4UaubRKhGDRkkrxWj9w+W30VYvCmCvcyjm+2FREIJ4WWR1VVIx0= 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=l3OviIwG; arc=none smtp.client-ip=209.85.208.172 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="l3OviIwG" Received: by mail-lj1-f172.google.com with SMTP id 38308e7fff4ca-2f74e613a10so26922281fa.1; Fri, 20 Sep 2024 01:56:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1726822618; x=1727427418; 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=Bdjh/8Otl1xOq6LNNEKvyVcsENCNjE0HEhaAAB1PSyQ=; b=l3OviIwG5ktnZQaVDohGudRAyvRxiium8KQo3lKETuz9fqztbkRbyR5b1/Jtoz+wV9 DvD0tjrHLiRbIxhYFTVkkJHm07VxIlnr48Dv2DeDE1Aii2lYUi9Cjw3kpsIVsqbuG+Sq o4KqTMMTxN/P7gYQWVET6v548fd5xKoJtvCvXun7vArraEQyDWlaEqVZ26kDMueBw+Iz YVy+Ife+h7NBWdTs6XZg7NlOXevxwcgNt0ouEXBRFaA5Y1LeB7PD5rUF89xWP2XeAV66 ecM5wG2rr428P6r57oqqChA4HL/DWMJL850nrdXcfHBwi0JF82VPXq/H8P8wED6SkHoK xg9w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726822618; x=1727427418; 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=Bdjh/8Otl1xOq6LNNEKvyVcsENCNjE0HEhaAAB1PSyQ=; b=kzGkK4WkjnGbgB645boUf0O1EjeljOwX04WXHAoJOEGZVwScW3InIBAFT7dkGrQmDC 8o9yjQLg1piPBzhhdZkskRkaoDqlTQcwgQHsHb+JLq68hhaY6Aou/rOmXVfMzol8ZPI4 /04V6OLTzo3EVs+WtkpFIb5r7n159WVlwCO7dxMCMJI7rrqTqCTtfw0tq5eQx1wSQB4+ yEd4zZMJf4TNXQ/KZH1JXoQiAsGHDDXJyqYyfwSb2RQqwP0kOiWZOD4klU4aUqDagvGy G0eH33ztCGNKGyx2RoD9Zh5+ta3gSLYgt0iau9U/Fx9E48xzmNDdsZOP0mA2C0e6OvJ4 o9lQ== X-Forwarded-Encrypted: i=1; AJvYcCU7XBwuT8SI//+oEwvUpfh09htG7aJMWO3+pQ2I30DgN47vXJmhk53ZtGz1Ei/8Fy7gfw9kd2ASjEfSK00=@vger.kernel.org X-Gm-Message-State: AOJu0Yxu++hwAmaiD3yGt05efmYynkXL++5eQls6jvNI9mj9Vo5kb9lh w2B9Db/q7RbPdP99w1AXgpBwgdCKGsuxjrg3gf2cdbi70NEs//ve+j7gjA== X-Google-Smtp-Source: AGHT+IEHejuxQpSN39QCeRyYO1mv688YxeNka5UUyxE4glPVDlLoMuNJPbV3KO0XvJd2BOtuzyPWrQ== X-Received: by 2002:a2e:bea1:0:b0:2ee:52f4:266 with SMTP id 38308e7fff4ca-2f7cb2f9931mr16324711fa.3.1726822617375; Fri, 20 Sep 2024 01:56:57 -0700 (PDT) Received: from localhost.localdomain ([2a02:908:e842:bf20:422c:48db:9094:2fa9]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a906109637esm817861866b.40.2024.09.20.01.56.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Sep 2024 01:56:57 -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 v5 07/11] kconfig: Add files with utility functions Date: Fri, 20 Sep 2024 10:56:24 +0200 Message-Id: <20240920085628.51863-8-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240920085628.51863-1-ole0811sch@gmail.com> References: <20240920085628.51863-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 Fri Sep 20 08:56:25 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13808365 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 DD88613D297; Fri, 20 Sep 2024 08:57:00 +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=1726822623; cv=none; b=YyN8DbuouMqgaP4k2OTa4SMXY1rOVzuWSa6HWBg3MsRU5AKgaCYHNa/4x2VBoWC367/g+U8HMHUADL7DDrifLZDLdeYJ3b7+EQe9pRuAvPG2q4Bcajn7C88Hxf3Um+AdgzVhEnC/5RmuQsW4ajiJvADEj2Qw80CLPsFXpnJp258= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726822623; c=relaxed/simple; bh=/41txCYFQ7CyEmxhECWoiay9tnFyY6nmi1QqtARjsS4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=lPesapcByjGhuAXKLfAPVIKj17jvO/QZ+b48ptOtxREtdfR+w8446zlWuWDnC47YiBGpTWcdo0nhpZCfM6i+9IUvnqGdt9DVBTPJdUYsJmK/fCtvEmMU3ccAgf+Nz1DG2EEfJ3OCxTWPa6UsgSLwsY+6sx4wNU4OievJ+lJsKaw= 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=SofcArXZ; 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="SofcArXZ" Received: by mail-ej1-f53.google.com with SMTP id a640c23a62f3a-a8a6d1766a7so236389366b.3; Fri, 20 Sep 2024 01:57:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1726822619; x=1727427419; 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=5jarFNJyfsREmlAM8vygYe1Z8jZyMu0CWlih30VG7Cs=; b=SofcArXZu6OApv0QsPT1wxQ/RmAInCSV+zl6JVqRTEjAQhov4yXQ+m/5RT8W2uhM2B CAmCHohXBSu3GIzzM072dGXdI5v1EXba0qx3C36ppOlip2SqiyIX8sfhvHlIaOplRxtN 8qPmaBw3yVYML2+cu9c/dKHLR3k2j5DZdIwqeDSuejeX1D5aoXJpJ+oJ+xQuZozBSBCR ZBuloLPhhPrfu4vwuDnmaH4cmyYyl66u9RihryRXUhRyz9ff8XH4SvcvZZVMDUohtOPk WGDvIDf2zJYua4B/hwcZ1U/6qCdUwvm5AzWMhFNO3iuX/3v89Fhz9wkHLmbBqH/I1CLw ItMg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726822619; x=1727427419; 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=5jarFNJyfsREmlAM8vygYe1Z8jZyMu0CWlih30VG7Cs=; b=Xnlqlu7pKtz/linkKkrhG9zwL5uw+yFNsq2HY5nMJwFqaVTFpuXtca49FrbQHv+2dL VPT4RzY87xdUE78m30ya2jOsUl5kX2WgFvRG5vjKTCBvLWXa0O8mazFYRZUYvPJTyePw kkV2ewwky+F23YAkOUKJUa1qEOiq+bsDNLrTuixgHEhzyXM/wXcRoR5wf3GcBs2qIsvI 6YgNqGdPufTBe0LNdkNFoyfaAYSGePvBnItBPRLhu5tpNRXm636GCAlFEM6/qKbOMMJ4 1lZNgq89Ed2Vd9zwS3J8uFXnD4wvJFbZ8F2JX/F9fGmt7YWCUCXFmyCWZ+hE3LXa9HCW BeBw== X-Forwarded-Encrypted: i=1; AJvYcCUWlQZi+VGnXOjBxYGtlJ9qWe57p/d3MOhjVVclEXn6Ys+VdEzpB60BBp0YJH8RGEySrwdxU8NqkfU3gRo=@vger.kernel.org X-Gm-Message-State: AOJu0Ywg85GFGovK4RoL97xkZ90YaxBpgGTGQuNSFsz3ewoTBdAdqJzs vgL4YbdqM/yvoRwNJxjZ1wryJPFt1HE/1iJCKpCfgpMAzO2yFpATadJ0mw== X-Google-Smtp-Source: AGHT+IFCtCG6XBuKtsReQvrLTUfPntrs/mmRL2fpNXEnY22rllk5Vf1TNXFZk5X0LLLPs1nxTQ5itg== X-Received: by 2002:a17:906:730c:b0:a8d:55ce:fb86 with SMTP id a640c23a62f3a-a90d5122bd7mr144022366b.57.1726822618953; Fri, 20 Sep 2024 01:56:58 -0700 (PDT) Received: from localhost.localdomain ([2a02:908:e842:bf20:422c:48db:9094:2fa9]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a906109637esm817861866b.40.2024.09.20.01.56.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Sep 2024 01:56:58 -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 v5 08/11] kconfig: Add tools Date: Fri, 20 Sep 2024 10:56:25 +0200 Message-Id: <20240920085628.51863-9-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240920085628.51863-1-ole0811sch@gmail.com> References: <20240920085628.51863-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 Fri Sep 20 08:56:26 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13808366 Received: from mail-ej1-f46.google.com (mail-ej1-f46.google.com [209.85.218.46]) (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 B68A413A265; Fri, 20 Sep 2024 08:57:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726822626; cv=none; b=FwcB6j8w3ZWgIOHFJGLee0Orla1R0WdluAMeR6W14RnBF36NPOeZH0KgHyXu1lex3vdQsRfS2E0jRmOjx2Phx/ZdvLbjstilMqE2z4eFl+IBhoBAGi+6P0ukbuk+IvclD6s54zpHj80KBfFSHjgK7F70YWM5M6YjAMARdTGN6Yo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726822626; c=relaxed/simple; bh=U75QlSgyGntuv86kG/8KeMS+50KqwR7RECSHXuZVyds=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=NOeAZrs43iM8FGszKIKnJoORQng8m+1698Z5wlr6k0YAR/EbHq0nKDqwJE1xyhGIIxhVFQTzelnGV1cclBD9DtFukxgTxvLsSyebdw8MJ0IgNG1KL+0Ikcbbdhiu1qxB8+P4tgdaYnlGhxcex/QYtopIOsmSMg8qQIQR8pCyEcY= 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=aL3zw3F7; arc=none smtp.client-ip=209.85.218.46 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="aL3zw3F7" Received: by mail-ej1-f46.google.com with SMTP id a640c23a62f3a-a8a7596b7dfso310689966b.0; Fri, 20 Sep 2024 01:57:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1726822621; x=1727427421; 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=yJ5MNhQyZlGP8m7z/JeZxS+4BSBcL3zQHHrWNe5ey7o=; b=aL3zw3F71+D+D4pPClVR1ewCmq1IzqF7u6MUkJWcNTO9KNJjPxfxkA3Do+s+a6NhmG NDAKThehCR71H/ErEcMPaecyaWEaABQIUNgUfRv55vIzUzMM7JkafjMSY0Hjix7BXbqv 3Fw5gZofwYgJK0GOzBNr6TAAt4bUppT6pumwf+tUVuFIHiK8qlHt6Ggh+6L/w+mrqOki 2Cbl4HfGrIHR7vdOgfCnUncki7jO3Snzd5I8erdrcpHlSBSyBwcZJYlbHLbddtWhtCgX UlQ3Z3Mdjc8h3HSP53bLaQzO8ottNAFY0qxBJj1eukrZkS4m/79hJud6ZiAoHe+9k39z x1TA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726822621; x=1727427421; 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=yJ5MNhQyZlGP8m7z/JeZxS+4BSBcL3zQHHrWNe5ey7o=; b=oBJStgIgjjsrJpvE3zTRxikXUXfdooFLKVIJpGTHYFr3wWnDuu2zO+Doxl3BdE/30G /3nwcEihumaJmxCiTyfiBVmbePUFD2qRxeD9X26WtXzFYW62Kjb2WT5vSKhi+XXQj2QC Fnumhf19+Ej6csAuFTWctzSEkILCE4jGSJhUlIvQQgY832YY8oRy/hFT03jMhHD0GV1U nu4U9lrMG6Wc9fOtq6YR6/AIift/d/X6fIhPyuLOCIaberTMwJGl343Eji1kKlAIkKhx eDlMArsO2ibOdkovN7VF/aqs5BUHLg9CTIMhipSW2s2oHV+7jydQf+SVQ3eRvN93tUAa vdzg== X-Forwarded-Encrypted: i=1; AJvYcCW6FlEWKHnF3tLZQUJ4vpT77SDZuVHZIY4PqNstjQ+CYvM717lTudWCIxEcR/8Ewudiaq9LcATRgH2gKvI=@vger.kernel.org X-Gm-Message-State: AOJu0Yz0XA8ppxHt+eg9/BhMT9ki+E0dnKrzexX+1dRUAOEXueqlN0C0 PNDm6LzaEpd8ZseRV4bltN+CBsZEHTEUvqEXSyhmtNLW+kVMUjm76XCbBg== X-Google-Smtp-Source: AGHT+IEg0e1c19deP7yxHUkX6IHO86dl5EBmzC+fAzZzufE4g3axu+NcmCmvvn7WKKvuHNKmQ5FzzQ== X-Received: by 2002:a17:907:9482:b0:a8a:85c7:8755 with SMTP id a640c23a62f3a-a90c1c747a4mr631651366b.11.1726822620721; Fri, 20 Sep 2024 01:57:00 -0700 (PDT) Received: from localhost.localdomain ([2a02:908:e842:bf20:422c:48db:9094:2fa9]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a906109637esm817861866b.40.2024.09.20.01.56.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Sep 2024 01:57:00 -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 v5 09/11] kconfig: Add xconfig-modifications Date: Fri, 20 Sep 2024 10:56:26 +0200 Message-Id: <20240920085628.51863-10-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240920085628.51863-1-ole0811sch@gmail.com> References: <20240920085628.51863-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 | 623 ++++++++++++++++++++++++++++++++++++++- scripts/kconfig/qconf.h | 111 +++++++ 2 files changed, 732 insertions(+), 2 deletions(-) diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index 97fce13e551e..b71f35c1188c 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) { @@ -1249,7 +1271,10 @@ 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; @@ -1272,6 +1297,10 @@ ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow *parent) this, &ConfigSearchWindow::saveSettings); } +void ConfigSearchWindow::updateConflictsViewColorizationFowarder(void){ + emit updateConflictsViewColorization(); +} + void ConfigSearchWindow::saveSettings(void) { if (!objectName().isEmpty()) { @@ -1364,6 +1393,17 @@ ConfigMainWindow::ConfigMainWindow(void) split1->addWidget(configList); split1->addWidget(menuList); split2->addWidget(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,SIGNAL(conflictSelected(struct menu *)),SLOT(conflictSelected(struct menu *))); + connect(conflictsView,SIGNAL(refreshMenu()),SLOT(refreshMenu())); + connect(menuList,SIGNAL(updateConflictsViewColorization()),conflictsView,SLOT(updateConflictsViewColorization())); + connect(configList,SIGNAL(updateConflictsViewColorization()),conflictsView,SLOT(updateConflictsViewColorization())); setTabOrder(configList, helpText); configList->setFocus(); @@ -1430,6 +1470,8 @@ 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); @@ -1485,6 +1527,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, @@ -1493,6 +1537,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); @@ -1712,6 +1758,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); @@ -1847,6 +1900,504 @@ 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, SIGNAL(resultsReady()), SLOT(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, SIGNAL(cellClicked(int, int)), SLOT(cellClicked(int,int))); + 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, SIGNAL(clicked(bool)), SLOT(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; @@ -1896,6 +2447,8 @@ int main(int ac, char** av) fixup_rootmenu(&rootmenu); //zconfdump(stdout); + picosat_available = load_picosat(); + configApp = new QApplication(ac, av); configSettings = new ConfigSettings(); @@ -1918,3 +2471,69 @@ 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(); + layout.addWidget(&text); + text.setTextFormat(Qt::MarkdownText); + text.setTextInteractionFlags(Qt::TextSelectableByMouse); + text.setText(R""""( +To use the conflict resolver you need to install PicoSAT as a library. + +## Debian-based distributions + +```sh +sudo apt install picosat +``` + +## Fedora + +```sh +sudo dnf install picosat +``` + +## Other + +```sh +sudo scripts/kconfig/install-picosat.sh +``` + )""""); +} diff --git a/scripts/kconfig/qconf.h b/scripts/kconfig/qconf.h index 53373064d90a..8c818d3da536 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 Fri Sep 20 08:56:27 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13808367 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 7393C13D8A4; Fri, 20 Sep 2024 08:57:04 +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=1726822626; cv=none; b=M/pZt7nka47YzsFl7xa4U1H5/ACcWVXHVH8h6Vqj7qWnw401A+mKVnZ+ajTzGVZzpPn7WxAjSB9QriBCLDk3LzG1g8rJgBfhRp7udeQ39u0Pe96NGt9thcVP+NvuKRwmNT0gLGPtqNMG2nbqg7MhzLE6F7hDXJT0N5aiXzhF004= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726822626; c=relaxed/simple; bh=XhD3n1oKeM/YS/k/7zTXEhb/l9FjVtgQrMSbjSE/4w0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=pwO33qsibWsf9SLylJUwPM0UiD9NDA6AlWRlNlg83kGCq+EEWUtAZ5uKU6iiVJcdThyzBpAAvwrhqbozBhZuXHesJX4A9VDTlqxbl5lywjLb/zyAwrZc4FuTIhc2k5GnY/ieCJuBSrm7PJb7biOFs93AmnF9Tr7VbR0kUo61BmU= 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=aKDZ7A0S; 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="aKDZ7A0S" Received: by mail-ej1-f52.google.com with SMTP id a640c23a62f3a-a8d100e9ce0so213572666b.2; Fri, 20 Sep 2024 01:57:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1726822622; x=1727427422; 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=zYIGl18k/y2SUEw9ZkPuau+unS2SVmeP6xXXwBwy8Dg=; b=aKDZ7A0SrNZ/CowIPFK+54Yik5IjcGZAE76qCf1j4nW7hjdjt1lZPSU8z9oYB+nZFw zLgzgtsTbBSml/Pq2TDMcgdZmHeaq1oDFAY/dlaEGShlvSCA3CFybAxmIsZo6V81XbWu 5uuR3iB8EQdNNyZ4zLWsZ9xNoqCVlwktEXrj1QGGY8DsOwWsu3/eKz+Dqmk21opmdgKQ zfbxRuZTOMDmvRMNNIk2TrC+F3eS05oU2fpu3+Dh23kbSibQiL4/dbmrstSm04gd/F2g 70YMJz29INlV2Z4b0FX1GAKdm+Pw9N5dMbg4nMfAzhx/tNNyn612QjqPfeXcikaf7hOn ruoA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726822622; x=1727427422; 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=zYIGl18k/y2SUEw9ZkPuau+unS2SVmeP6xXXwBwy8Dg=; b=OAykodaoRq1A/uoK4hT24j3TkEnd+xR6Y17M0pkzk+f3frMYNLzvFTIA1fyaMBt676 Fma0xV2dJhYWY4uDvnqDM+BCTf8cgg/3pOBd9Dy+sO06igafIMfloGkO3cDf+ODEXCXJ v+o4M7wR0DWR96ApQYNtMNXIL1tU7IK0TnIi5rMcZvyYa03U32znb7C1yGKOu78E3HbK gE9gKeIH9sDb2S4TLRBTP8muUpG1SKMHixkndVNL60GpiOwbWw0+8+TWT7h6NzFvzZbH OA7I88ZRAva+RHsWe54zMUtuSB0MYzAoK9mBrr8UojfLM4h8dZ+kE4kxxQJXenDfJ+// PgBA== X-Forwarded-Encrypted: i=1; AJvYcCWic78p8eBGoO/UzBvNzioxQNnHXauLYVlSjNeAnPmxL5X5wdjoAFlsULCXdXsVg4y5uZezArHdwXuSUhs=@vger.kernel.org X-Gm-Message-State: AOJu0Yzyt/38K8wmZnwMFAXD5CCy6fV4L0aM7q0x2f32Hq6unqOHT+rF L+zrLDDGj2Gv0mOo0MzRjUAF6lUdUutUIeD6fWD4NtOpiQOP3ZUoEK79Ug== X-Google-Smtp-Source: AGHT+IHj5QW/ddTsT9z7mxHij5maUJpk6r8vqBtRRXGEvzGFUMa/Zum7lyQxoWrR52vbW6BeLOv0hg== X-Received: by 2002:a17:907:f757:b0:a86:bb90:93fa with SMTP id a640c23a62f3a-a90d577a219mr140874866b.44.1726822622371; Fri, 20 Sep 2024 01:57:02 -0700 (PDT) Received: from localhost.localdomain ([2a02:908:e842:bf20:422c:48db:9094:2fa9]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a906109637esm817861866b.40.2024.09.20.01.57.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Sep 2024 01:57:01 -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 v5 10/11] kconfig: Add loader.gif Date: Fri, 20 Sep 2024 10:56:27 +0200 Message-Id: <20240920085628.51863-11-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240920085628.51863-1-ole0811sch@gmail.com> References: <20240920085628.51863-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 Fri Sep 20 08:56:28 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13808368 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 9226014389F; Fri, 20 Sep 2024 08:57:07 +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=1726822629; cv=none; b=ovXxl3CU6rjdmwsAC/gFTEK/Vy8GPls8A2OEPVumxnI/J52uasZ3Om/mJAbD9R2gN60g/wTtdWutIFv11SdTPURxkhlU9VsJTcz9/LNf5l7Jkihw92acp5bTSJgrmKbgzWyvd5+t2gN0vW0J1vchviZZwLNh3dqmjFri9K8YNvM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1726822629; c=relaxed/simple; bh=61QQlySFa5pEM/vWncsXNN6TckBPFGjbzEUDyKZyDc0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ir+L5Aj+MwTvlnfBuI3vcF0mQab0Mi7mY7kgo0wjNUcRdpqDcOXjKck/2YXHtm35i9syBafjs5ViUqOJgzf1UryA7lSKn8+A6O+jAtKNfSV88LOxwCP5l2rc9PbATVpIiXl25X1Z/qNjyTgtTFzgEQByt4sNaQwZTEEBEDp0x6c= 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=kOHCaIaS; 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="kOHCaIaS" Received: by mail-ed1-f50.google.com with SMTP id 4fb4d7f45d1cf-5c241feb80dso5584626a12.0; Fri, 20 Sep 2024 01:57:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1726822626; x=1727427426; 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=tSINRI5oDb/ENez5X3KtVyvqwsHbg24jiAO3lEAztlo=; b=kOHCaIaSpgZWXiZ9+vgEvPmpepvaizZoPtxT9SIEXmvmAoXb+kABCNLN3AQUOtzYmZ fr+pSnFnoQqAF0x5XgeG5y+bBsc6KIn5tGmx1p2g8sxEORgw3e0de3JCl8WcXFLIsqSy yznSATMCy/j/SWwdqnRAar0IXXCNvydOroSF2n4zpjkf61YiU2S/7kRksw7yYC3ychBo YyROAEbcdU4OpvcwkwsoH/iVktPtrjdA3GiLO0sEarDAvCPk75/wHD+FLwnhoDBuXag5 NZxU0/oJxhsUTvLqOLkyqGAszf+vLaVPN28hgM7i+ksI9L+SmZQSZqeka9n/OEgHQWpl Jy7Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726822626; x=1727427426; 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=tSINRI5oDb/ENez5X3KtVyvqwsHbg24jiAO3lEAztlo=; b=kQuyaUu29dEYwGI89xTkL0ElOb4zcWsUIRUmICzEBIDJwoHXZ+N22QYYL8QRvcUnpo cwFP+6DjXgg5k3Pt/24yqnJEVg9jYEo2UDnQfR4IJ1U6TJEfcqweCILWTDeoS76mC+7O AVGDwUMbJf89I/TqpgyEbkg8bNAGXJLPpGzxjwmEs+QJ57+dEXG7t2hXNbD7lK7EBWIr ACk1MtEQ22WaeKVnXrksho8ST2haFkTDZ+2++8fEZ+sy73BSvz/Gv9H4XML0R66GdybZ DJeuq6xrYODZlKXveEFXq8w1LpZIItrUkbXcpiTwBuHkKVKX5+TytUGDxpxZgWcyBi/j /W3g== X-Forwarded-Encrypted: i=1; AJvYcCUFhcoe064oAtRc0dJx5qkO94eDeFMrrMpRHeaxyAGbsf9zC2Z0t7SjIdNA+h11KXGfUx88DhmquDPzCXg=@vger.kernel.org X-Gm-Message-State: AOJu0YwrnMKHYH2p0trb7fUg5gChXXSY8z28gn0hwlh7qVPWkLKk6dS7 WtPDxbicwXB57h7wz6W+wVN5rhzesbaymDYpqcRRjYAcjUTevDiCYrmiog== X-Google-Smtp-Source: AGHT+IGuwGV0NYWs6KD4S9AxG15tOLMKgs3Qp/ORAsqck2unCK7XvXylU8DwIZvSn3gjAMvKYfLS/g== X-Received: by 2002:a17:907:3f27:b0:a8d:59d7:f92b with SMTP id a640c23a62f3a-a90c1e545c7mr570196566b.30.1726822625560; Fri, 20 Sep 2024 01:57:05 -0700 (PDT) Received: from localhost.localdomain ([2a02:908:e842:bf20:422c:48db:9094:2fa9]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a906109637esm817861866b.40.2024.09.20.01.57.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Sep 2024 01:57: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 v5 11/11] kconfig: Add documentation for the conflict resolver Date: Fri, 20 Sep 2024 10:56:28 +0200 Message-Id: <20240920085628.51863-12-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240920085628.51863-1-ole0811sch@gmail.com> References: <20240920085628.51863-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 | 53 ++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/Documentation/kbuild/kconfig.rst b/Documentation/kbuild/kconfig.rst index fc4e845bc249..6a606c83d8e9 100644 --- a/Documentation/kbuild/kconfig.rst +++ b/Documentation/kbuild/kconfig.rst @@ -285,6 +285,59 @@ 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 + + Other:: + + sudo scripts/kconfig/install-picosat.sh + +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 =======