From patchwork Sat Feb 8 16:39:49 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13966439 Received: from mail-ej1-f44.google.com (mail-ej1-f44.google.com [209.85.218.44]) (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 92CE21F3B86; Sat, 8 Feb 2025 16:40:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739032840; cv=none; b=r9B19FLhb7goTSZaQLMrQy8zzcWlU41rRiP3d3TW9IhwRjMXnuIorD2062Ij6Iq3w19ZBlpG7rcuEydBkiOFlD+S31vn5nOhUSQJ90O0EYGqCRS9Hlh2Nyl/p8C6ILiW1C4szhBTc5mlnFaTCP6W0vjh5oBZ3ssfbs9oMzoY1QU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739032840; c=relaxed/simple; bh=Fgxku5RPW8FWV3C8caf/gIzbndc0xmCblOC0ySNF51A=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=IZIPSVL8E8v+EB3LvzvRVIlYQZ00aRU2Qo93BC1q+vpQQ/wBoQuM21FqFb7QlA3gwxKtVYVkbDi7Z+tse6PoHKnfRWvFUZvnyvO5UYMHtNllmQ8KFOeXjGDl2MbG64UW5x85fyRMRoVXoYOk7FYOVIc1JbiDjlxXCrDvN06PstE= 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=UygrmsIr; arc=none smtp.client-ip=209.85.218.44 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="UygrmsIr" Received: by mail-ej1-f44.google.com with SMTP id a640c23a62f3a-ab7a4b40cc1so100659166b.0; Sat, 08 Feb 2025 08:40:38 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1739032837; x=1739637637; 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=gDYe9in7ppLsTtVsenOBRVJi5GL6J9SBuShnxnSy6NE=; b=UygrmsIrn1jnryqg0IDxwSDCKmT8nGBNGqKOUTZSLmnFBk3rQRiwpqJ/Ex59RE5uFM asJ58rZOM8ZJUU9iJcrTRux7yJ4Zx2WCd2KxIXfeThPwPu5PWGQ0kxVGU3f4ULbYd3Ga 6gfEBylzqSZzS+/dlx6VssUynv6pfGp5rQD1zEBsGkocLYtg7l8QsSBGBhryIgLU9dIt RN5Kyo3/DbSLVTsvujt+C9SM8z02Xw6h/AVY8EPWwaU+hQeRbd+yohX01LVIhpMaVofx BWolTiqkdd3JgCtSxrAEbC4Hdp6czEmen//Ah5ztkudVhvvzIaygfFQyESZzxOXfsAfo W2KA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1739032837; x=1739637637; 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=gDYe9in7ppLsTtVsenOBRVJi5GL6J9SBuShnxnSy6NE=; b=VtiB2XQkdcbtozr1oj76kxtNmSEtYTKrn4egFEEOXU3OAv1bemoSaeyVIVWmKwUUOq G/xHJ9tilU7VJhRHJTUDw4JF9EUmCoLtV1mFcMo/pUKcTcp0+iJ9fDyXUHpnVx3mY5l4 59Yz0XFiva8P+/CPrRWT5Mwpwus6LiBFm9DvDLxfXUUc0zEFhx2Rcd+OhR/tGu2RqbBo SMAYtb2VG6P9QfIQoZWntwr7N91xqx9VI0ck0j4DpAaUNpiKrYLLEqBtVrX84Xt/cdTk HOQRfWgIuwA1HT15sd9XJhSd59NHZAao/rGp26ye+KtuaFlBVgSsYioCFxt/4P3ujnez TMmA== X-Forwarded-Encrypted: i=1; AJvYcCW59YgQlYKf7dUENfxWFd8M3WE4bTqYcnf7Nqk98ncIsKoRUz8CPPkH4PXpE6/LTdSdU9HDIjGTgmeJud0=@vger.kernel.org X-Gm-Message-State: AOJu0YwOAy0Fx8CId5hcUGCnYDl/dDyfmXQyIlCgmacHnzvO4xtlmRcw lX+N/b1bRax9hBRVbfPLRuVkl7v5XuA/v21wfazzd/U0LkaJ4+8jDhxMtw== X-Gm-Gg: ASbGncuLwYrWc8LbWozVBWDh7G7ozmccY9iaHFoql0uJfbu8EYqsDkSLegRqGw7z5an 9obbZgKMP++N81UKqLBHkfUmwLiBXsRziW18oD3AHV0aoGr7MgHzYd6GDjXDiY6XFoVVyo/7D/E T0spXX3VUuK7Gcip1e1dk6gK89s8QqAJ3pO6tFay4jUZi33E5BW7YupwzladVIYIc5Dm0gGvvZ+ 6Ekhr+XIqKBPMioDAjVb6um8WfyZwas4ZZOtx7jDWhPo0rytcozZWOnP6QI2NRHn30vfEPc55iN BBR6wwOu9kaSR2JVPPktEExG7zgmPA== X-Google-Smtp-Source: AGHT+IH03k6Ap+U8QUh7GB24E2m/Ci6bNLoyNBjkMmx/L2TYMgr43Tr3Vj8Xc1tlA0G4lHGJU08skw== X-Received: by 2002:a17:907:72d0:b0:ab6:d4d0:2be9 with SMTP id a640c23a62f3a-ab789c544c0mr752546366b.56.1739032836658; Sat, 08 Feb 2025 08:40:36 -0800 (PST) Received: from localhost.localdomain ([2a02:908:e842:bf20:e115:64e7:5d6:176]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ab773337f3dsm501539166b.139.2025.02.08.08.40.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Feb 2025 08:40:36 -0800 (PST) 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 v7 01/11] kconfig: Add PicoSAT interface Date: Sat, 8 Feb 2025 17:39:49 +0100 Message-Id: <20250208163959.3973163-2-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250208163959.3973163-1-ole0811sch@gmail.com> References: <20250208163959.3973163-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 [0] is the SAT solver used in this project. It is used as a dynamically loaded library. Add PicoSAT dynamic library support to kconfig, which will be used in the subsequent patches. Link: https://fmv.jku.at/picosat/ # [0] Signed-off-by: Patrick Franz Signed-off-by: Ibrahim Fayaz Signed-off-by: Thorsten Berger Signed-off-by: Ole Schuerks Reviewed-by: Luis Chamberlain --- scripts/kconfig/picosat_functions.c | 79 +++++++++++++++++++++++++++++ scripts/kconfig/picosat_functions.h | 35 +++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 scripts/kconfig/picosat_functions.c create mode 100644 scripts/kconfig/picosat_functions.h diff --git a/scripts/kconfig/picosat_functions.c b/scripts/kconfig/picosat_functions.c new file mode 100644 index 000000000000..dcfc80d418c2 --- /dev/null +++ b/scripts/kconfig/picosat_functions.c @@ -0,0 +1,79 @@ +// 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) +{ + const char *error_str; + + if (*failed) + return; + + dlerror(); // clear error + *ptr = dlsym(handle, name); + error_str = dlerror(); + if (error_str) { + printd("While loading %s: %s\n", name, error_str); + *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 Sat Feb 8 16:39:50 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13966440 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 75A251F3BAA; Sat, 8 Feb 2025 16:40:42 +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=1739032844; cv=none; b=GGVU6hfy8X5X4GDZrzmd617Ebvl/ago+YiD/Q7E/XbsURIaOUdboTm+ftXr/c1m57+8qpqLf6MauosOn2/IMI+VLfk4sZZQVmU2CEEIaWK0DNChgDJzTYVqwK7pb61CAdGueFoWnq0CWJ3G1SCtxuAR8bT0T4NwUpfAmX5ybWL4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739032844; c=relaxed/simple; bh=H2KX6Em0lGPKJzxWFEHziMiC/SiUTPwppprNvpBnbuo=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=aLPU/oeRYs1iEyJeBKzsr66bqM0/3h8CzX79aN5qKb5g2ph06pxbkUMn4L5o6472+fmfhcod9iXIWYKCYvn3qgN3EY5HXAhz8Uza7TA5CriIUnImKTjsijkGeUpD+Qk6mFCYutXeXPTOD/jnAwQi2AaLEvEcULZ8egdno13edQo= 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=U2Gru89f; 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="U2Gru89f" Received: by mail-ej1-f52.google.com with SMTP id a640c23a62f3a-ab7a4b40cc1so100667666b.0; Sat, 08 Feb 2025 08:40:42 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1739032841; x=1739637641; 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=PFVg0d0ABZwGR+n77r4ytnStBZWeFm/afUQWqFUjHPo=; b=U2Gru89fnjPJo1R/InKRG/eDG8iFR8kNhtmf6dzeOtAU+kV/XXNe2LMAbj8gQpAij5 AbCUHRTpVs0vjCICTCGCz3vnInTxT2qycIYCtmkEYLG5M8z4G4SVRS02oC5dIeN0bkpI Vmk+XIDaEu3cm16RP0G31vepUCXJ5h5O5wVFahmKqvqTSuWq6xgpQfQje9gJdW0kmKd3 9IDzNAWSUiaO5lzX9frCDkwQd6DHIYunc6Ov2c0ojuFLNEtVmYgxhKU4rqf1Kn3kiRtZ L34JQKOy3064vYlfARU0AQE5JUaZfq5Os6OVYHHzRxi7B+E3/5NitTKZYZm/q7xYS+SB rccw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1739032841; x=1739637641; 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=PFVg0d0ABZwGR+n77r4ytnStBZWeFm/afUQWqFUjHPo=; b=LijeuSsrxYIStdSFnILFdifRf+KoLwq8hljW+4DAlEsrAidz6920yeVA3GSYhE8WV0 12fYghRHWGX37ZlqXyQ4q6j1KnecRS9zvIAfDW+c5u2Lb0Bx8gb31PDr4M6LM64IrlVA 1oX1vOzW+ookRq2DwoBiCP+QTB40N52s7LxKmk4M3QOi6K+2wMsAjALMkcHpaCSP4ghy vBFt6rskBk+bdDIek8OZ4iasdFHX23O3TZ9BSz/Pn+dGTNG1tD6hDaN3PEOyACjk56qN X6C64wTW0BCDeGcUhAM/oi3pHyCD0crtCJN/dT+43NHleOVcLk/DuhVlzOiCtlaJ8iRM 6jnw== X-Forwarded-Encrypted: i=1; AJvYcCXPpjspyLpxLEkUawzO4TCE7G2B1UUVaEiR5jKTmvBdIbd8aYn1960LRDwQXM/Ixftg/40AZlFgICwdwRs=@vger.kernel.org X-Gm-Message-State: AOJu0Yz1JJKKD3BfaA797nve4mi/MjER1FEY503lpq005oVHvyVEt8Mo dSyJROgmQsQW6ZqPrtd0Gw1oxK0u3Xgi9xMTr19iwjelzr6oUvRcSgYKwg== X-Gm-Gg: ASbGnctT9NzMfH46Osjg+btQ+jfIXjgpizG6ygtma8dWUkCBi0aZA4LEXr9G72nu52j wjqQnFlu6W7q1jLsfO7TOuo9rKS9uHufKDwN9Vo54G7mfSfYFZnpXPu027aYJJxhpFm33LLP86W I1SdIlx0qrfywUgogDbnp6MT88apQ/3DDv2G+o2BH3TzR1JeEcmdEWmK1aAb4muhFCBZT/oGHBh ntokf8N5HxijxtTKGyh1eRJu2m6YDhZFertTNwxQHcoT1E4m9hXbikotH8qWAUyJn/mrg+Hek2n 418pkRzBifSZfcMBExVkM/iVngdKWg== X-Google-Smtp-Source: AGHT+IFopXvS3YWFXMi0y9TbqOmtx26Khed7zURFzfhXM2BWPQvpju1KMLsT0otbH1jMr1+B8O3tYA== X-Received: by 2002:a17:906:5acb:b0:ab7:b072:8481 with SMTP id a640c23a62f3a-ab7b07285f8mr26676666b.45.1739032840355; Sat, 08 Feb 2025 08:40:40 -0800 (PST) Received: from localhost.localdomain ([2a02:908:e842:bf20:e115:64e7:5d6:176]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ab773337f3dsm501539166b.139.2025.02.08.08.40.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Feb 2025 08:40:40 -0800 (PST) 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 v7 02/11] kconfig: Add definitions Date: Sat, 8 Feb 2025 17:39:50 +0100 Message-Id: <20250208163959.3973163-3-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250208163959.3973163-1-ole0811sch@gmail.com> References: <20250208163959.3973163-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 structs that are used to store constraints of symbols and 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 fb50bd4f4103..db3069c13064 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 @@ -154,6 +155,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), \ @@ -198,10 +200,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_fixgen.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..ab2bd5a5b882 --- /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_fixgen; + +#define printd(fmt...) do { \ + if (CFDEBUG) \ + printf(fmt); \ +} while (0) + +/* + * Helper macros for use of list.h with type safety. + * Lists of type X can then be defined as + * `struct X_list { + * struct list_head list; + * }`, + * which contains the head of the list, and the nodes with the actual elements + * are contained in `struct X_node { + * struct X *elem; + * struct list_head node; + * }` + */ + +/* macros for internal usage */ +#define __NODE_T(prefix) struct prefix##_node +#define __LIST_T(prefix) struct prefix##_list +#define __CF_DEFS_TO_STR2(x) #x +#define __CF_DEFS_TO_STR(x) __CF_DEFS_TO_STR2(x) +#define __ASSERT_LIST_PREF(list, prefix) \ + _Static_assert(__builtin_types_compatible_p(typeof(*list), \ + __LIST_T(prefix)), \ + "Incorrect type of list, should be `" __CF_DEFS_TO_STR( \ + __LIST_T(prefix)) " *`") +#define __ASSERT_NODE_PREF(node, prefix) \ + _Static_assert(__builtin_types_compatible_p(typeof(*node), \ + __NODE_T(prefix)), \ + "Incorrect type of node, should be `" __CF_DEFS_TO_STR( \ + __LIST_T(prefix)) " *`") + +/* + * CF_ALLOC_NODE - Utility macro for allocating, initializing and returning an + * object of a type like struct fexpr_node + * + * @node_type: type of the object to create a pointer to (e.g. struct fexpr_node) + * @el: the value to set field .element to + */ +#define CF_ALLOC_NODE(el, prefix) \ + ({ \ + __NODE_T(prefix) *__node_cf_alloc = \ + xmalloc(sizeof(*__node_cf_alloc)); \ + __node_cf_alloc->elem = el; \ + INIT_LIST_HEAD(&__node_cf_alloc->node); \ + __node_cf_alloc; \ + }) + +/* + * constructs an object using CF_ALLOC_NODE(node_type, el) and then adds to the + * end of list->list + */ +#define CF_PUSH_BACK(list_, el, prefix) \ + do { \ + __ASSERT_LIST_PREF(list_, prefix); \ + __NODE_T(prefix) *__cf_emplace_back_node = \ + CF_ALLOC_NODE(el, prefix); \ + list_add_tail(&__cf_emplace_back_node->node, &(list_)->list); \ + } while (0) + +/* + * frees all nodes and then list_ + */ +#define CF_LIST_FREE(list_, prefix) \ + do { \ + __NODE_T(prefix) * __node, *__next; \ + __ASSERT_LIST_PREF(list_, prefix); \ + list_for_each_entry_safe(__node, __next, &(list_)->list, \ + node) { \ + list_del(&__node->node); \ + free(__node); \ + } \ + free(list_); \ + } while (0) + +#define __CF_LIST_INIT(full_list_type) \ + ({ \ + full_list_type *__cf_list = xmalloc(sizeof(*__cf_list)); \ + INIT_LIST_HEAD(&__cf_list->list); \ + __cf_list; \ + }) + +#define __CF_DEF_LIST(name, full_list_type) \ + full_list_type *name = __CF_LIST_INIT(full_list_type) + +/* + * declares and initializes a list + */ +#define CF_DEF_LIST(name, prefix) __CF_DEF_LIST(name, __LIST_T(prefix)) + +/* + * returns initialized a list + */ +#define CF_LIST_INIT(prefix) __CF_LIST_INIT(__LIST_T(prefix)) + +#define CF_LIST_FOR_EACH(node_, list_, prefix) \ + list_for_each_entry(node_, ({ \ + __ASSERT_LIST_PREF(list_, prefix); \ + __ASSERT_NODE_PREF(node_, prefix); \ + &(list_)->list; \ + }), \ + node) + +#define CF_LIST_COPY(orig, prefix) \ + ({ \ + __CF_DEF_LIST(__ret, typeof(*orig)); \ + __NODE_T(prefix) * __node; \ + CF_LIST_FOR_EACH(__node, orig, prefix) \ + CF_PUSH_BACK(__ret, __node->elem, prefix); \ + __ret; \ + }) + +/* + * For functions that construct nested pexpr expressions. + */ +enum pexpr_move { + PEXPR_ARG1, /* put reference to first pexpr */ + PEXPR_ARG2, /* put reference to second pexpr */ + PEXPR_ARGX /* put all references to pexpr's */ +}; + + +/* different types for f_expr */ +enum fexpr_type { + FE_SYMBOL, + FE_NPC, /* no prompt condition */ + FE_TRUE, /* constant of value True */ + FE_FALSE, /* constant of value False */ + FE_NONBOOL, /* for all non-(boolean/tristate) known values */ + FE_CHOICE, /* symbols of type choice */ + FE_SELECT, /* auxiliary variable for selected symbols */ + FE_TMPSATVAR /* temporary sat-variable (Tseytin) */ +}; + +/* struct for a propositional logic formula */ +struct fexpr { + /* name of the feature expr */ + struct gstr name; + + /* associated symbol */ + struct symbol *sym; + + /* integer value for the SAT solver */ + int satval; + + /* assumption in the last call to PicoSAT */ + bool assumption; + + /* type of the fexpr */ + enum fexpr_type type; + + union { + /* symbol */ + struct { + tristate tri; + }; + /* EQUALS */ + struct { + struct symbol *eqsym; + struct symbol *eqvalue; + }; + /* HEX, INTEGER, STRING */ + struct { + struct gstr nb_val; + }; + }; +}; + +/* struct definitions for lists */ +struct fexpr_node { + struct fexpr *elem; + struct list_head node; +}; + +struct fexpr_list { + struct list_head list; +}; + +struct fexl_list { + struct list_head list; +}; + +struct fexl_node { + struct fexpr_list *elem; + struct list_head node; +}; + +struct pexpr_list { + struct list_head list; +}; + +struct pexpr_node { + struct pexpr *elem; + struct list_head node; +}; + +/** + * struct defm_list - Map from values of default properties of a symbol to their + * (accumulated) conditions + */ +struct defm_list { + struct list_head list; +}; + +struct defm_node { + struct default_map *elem; + struct list_head node; +}; + +struct sfix_list { + struct list_head list; +}; + +struct sfix_node { + struct symbol_fix *elem; + struct list_head node; +}; + +struct sfl_list { + struct list_head list; +}; + +struct sfl_node { + struct sfix_list *elem; + struct list_head node; +}; + +struct sym_list { + struct list_head list; +}; + +struct sym_node { + struct symbol *elem; + struct list_head node; +}; + +struct prop_list { + struct list_head list; +}; + +struct prop_node { + struct property *elem; + struct list_head node; +}; + +struct sdv_list { + struct list_head list; +}; + +struct sdv_node { + struct symbol_dvalue *elem; + struct list_head node; +}; + + +enum pexpr_type { + PE_SYMBOL, + PE_AND, + PE_OR, + PE_NOT +}; + +union pexpr_data { + struct pexpr *pexpr; + struct fexpr *fexpr; +}; + +/** + * struct pexpr - a node in a tree representing a propositional formula + * @type: Type of the node + * @left: left-hand-side for AND and OR, the unique operand for NOT, and for + * SYMBOL it contains the fpexpr. + * @right: right-hand-side for AND and OR + * @ref_count: Number of calls to pexpr_put() that need to effectuated with this + * pexpr for it to get free'd. + * + * Functions that return new struct pexpr instances (like pexpr_or(), + * pexpr_or_share(), pexf(), ...) set @ref_count in a way that accounts for the + * new reference that they return (e.g. pexf() will always set it to 1). + * Functions with arguments of type ``struct pexpr *`` will generally keep the + * reference intact, so that for example + * ``e = pexf(sym); not_e = pexpr_not_share(e)`` would require + * ``pexpr_put(not_e)`` before not_e can be free'd and additionally + * ``pexpr_put(e)`` for e to get free'd. Some functions take an argument of type + * ``enum pexpr_move`` which function as a wrapper of sorts that first executes + * a function and then pexpr_put's the argument(s) specified by the + * ``enum pexpr_move`` argument (e.g. the normal function for OR is + * pexpr_or_share() and the wrapper is pexpr_or()). + */ +struct pexpr { + enum pexpr_type type; + union pexpr_data left, right; + unsigned int ref_count; +}; + +enum symboldv_type { + SDV_BOOLEAN, /* boolean/tristate */ + SDV_NONBOOLEAN /* string/int/hex */ +}; + +/** + * struct default_map - Map entry from default values to their condition + * @val: value of the default property. Not 'owned' by this struct and + * therefore shouldn't be free'd. + * @e: condition that implies that the symbol assumes the @val. Needs to be + * pexpr_put when free'ing. + */ +struct default_map { + struct fexpr *val; + struct pexpr *e; +}; + +struct symbol_dvalue { + struct symbol *sym; + + enum symboldv_type type; + + union { + /* boolean/tristate */ + tristate tri; + + /* string/int/hex */ + struct gstr nb_val; + }; +}; + +enum symbolfix_type { + SF_BOOLEAN, /* boolean/tristate */ + SF_NONBOOLEAN, /* string/int/hex */ + SF_DISALLOWED /* disallowed non-boolean values */ +}; + +struct symbol_fix { + struct symbol *sym; + + enum symbolfix_type type; + + union { + /* boolean/tristate */ + tristate tri; + + /* string/int/hex */ + struct gstr nb_val; + + /* disallowed non-boolean values */ + struct gstr disallowed; + }; +}; + +struct constants { + struct fexpr *const_false; + struct fexpr *const_true; + struct fexpr *symbol_yes_fexpr; + struct fexpr *symbol_mod_fexpr; + struct fexpr *symbol_no_fexpr; +}; + +struct cfdata { + unsigned int sat_variable_nr; + unsigned int tmp_variable_nr; + struct fexpr **satmap; // map SAT variables to fexpr + size_t satmap_size; + struct constants *constants; + struct sdv_list *sdv_symbols; // array with conflict-symbols +}; + +#endif diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h index 21578dcd4292..1b5427fe90e5 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -140,6 +140,19 @@ struct symbol { * "Weak" reverse dependencies through being implied by other symbols */ struct expr_value implied; + + /* + * ConfigFix + */ + struct fexpr *fexpr_y; + struct fexpr *fexpr_m; + struct fexpr *fexpr_sel_y; + struct fexpr *fexpr_sel_m; + struct pexpr *list_sel_y; + struct pexpr *list_sel_m; + struct fexpr *noPromptCond; + struct fexpr_list *nb_vals; /* list of struct fexpr_node's; used for non-booleans */ + struct pexpr_list *constraints; /* list of constraints for symbol */ }; #define SYMBOL_CONST 0x0001 /* symbol is const */ @@ -201,6 +214,10 @@ struct property { for (st = sym->prop; st; st = st->next) \ if (st->type == (tok)) #define for_all_defaults(sym, st) for_all_properties(sym, st, P_DEFAULT) +#define for_all_choices(symbol, child, menu_ptr) \ + list_for_each_entry(menu_ptr, &(symbol)->menus, link) \ + for (child = (menu_ptr)->list; child; child = (child)->next) \ + if ((child)->sym && sym_is_choice_value((child)->sym)) #define for_all_prompts(sym, st) \ for (st = sym->prop; st; st = st->next) \ if (st->text) From patchwork Sat Feb 8 16:39:51 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13966441 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 857111F3B97; Sat, 8 Feb 2025 16:40:52 +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=1739032854; cv=none; b=i5DmNDf06gqKXiJn9IIUeyNwrB0S0bAffh7Iu8TAq1fsCjM/UDDJhGJYNYB//K0XCFrD8BZDmXurMO/K15GCVQY71Dhp4itHDStsZG25D9UajeCsXiEZkL0vMlB/WfQCKkKYlgHshh+BjOkOMbctD5anqpneOf1C4AxsB3gk3Ec= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739032854; c=relaxed/simple; bh=NZQWB/8O/pABSLvkDGFliMDk/CDPZjx7dlzGJFJOsVU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=k7UVRvzo7/rFN7ZL4C6HyH26Tcr3d3AYjaDVpjvfWgnxeK4rexi909CMB1JHrke0O44TCQvnrDAS13AHwwlRY9kXWpjPsResXI/O6VcloW4zGaEXH74RJIv5fuZ+7Oh5kpOgfRqsLzW3+mTlqNtHi4q4aceEmyo3G/5cGi2vGHQ= 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=AXY3HQOi; 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="AXY3HQOi" Received: by mail-ej1-f46.google.com with SMTP id a640c23a62f3a-ab794ae3cf2so147251266b.2; Sat, 08 Feb 2025 08:40:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1739032851; x=1739637651; 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=eQ5zjv0KRGetMajFc3IkL6hUkHKCs6bY73Rhz2RkOd4=; b=AXY3HQOiFhJloS27bsVb+ZuCdjD7Barr0jAOBfpJncSjWFXSyQK8W3bT4Mdg+VZECE 1Nk5NNnX/Cy/qOMJzY3yZ+IjdM2kz/uOEm6IoogRsz7mLpSh0l5AIv6AlPcAhtR1DvVs mcrupBoMpDEvlGh5PFFcVBagM3OV0xeHCCY4qBBUSnw0y2Wex1T1Eyqgd4DQV2U/P/31 Tg4Wq9B5XHDT2pFg52/GBtXxyHyBqSNKsBmcZ2Y+wsIiqAKLFUBq810dnR4GuK0f5yHN Y6pZPJk7cJ87ekOm4iSUA2RTQmyZe9ZxQbDp9mPP+Di38uRwuZ3ttEQ9cZxrh+Ag5m/o OFzg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1739032851; x=1739637651; 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=eQ5zjv0KRGetMajFc3IkL6hUkHKCs6bY73Rhz2RkOd4=; b=NgkPwLsBMTHJ3+1TyuJ09zgG4AuwJZbS9rjy1sWtx4TAud5SJwwDEv67uDagotrhb9 Qr3y+g0Lv1C/3H0jfMNCCBozf7+twQfbiSuNmYUClK0cJ8PSeucCHweFsWCGjlGIWwVj Hewom4MjkbdizXmA/Yd/DlKaGSLx53JkEXQE28Olw1QdN/fq1tpDtajV6qcVXa3zqLa3 BooPYeWW5tWGW7lfoyEhQRCP4M9irMDtXrzR+4B5y9GX7s7KEZS0MZCw+G+1SyZGqVrd 4AVHDuHWo9NMSYBBr6LAW6JRsTAQv3FvYLJAscADOZ7OZ5xjkUwDV/6HDVgehSMnD/GN Zbvg== X-Forwarded-Encrypted: i=1; AJvYcCXn2E3JLd17iUaOBIWn/HNr03afMqpG5gcC4TDyDg9y9hMCOtqWN8zpqBAX4taUseS2hS91n1/gsIpVKIw=@vger.kernel.org X-Gm-Message-State: AOJu0Ywfvbb2F1vYiq7Eejc8DU2RWHrcvxBQqgvXDZYRTMzRu/OBfpuC dRVS9RtYQf0sRYeIv9ZxJLJf6chArTnHBKd2erOEp7dYPydfiKO3I/nFhw== X-Gm-Gg: ASbGncub8Ug5IUA+59W7lZI50MqLHOmuu0oQvrJNmFV4n+UYZF9QuhKsc/69AqbKkjt yzO9/aBrvs7/BdsYC4FkEBtyDopR5l8zcirN5mpkTgrR7KUFbXjFolwGZV3AscI7el16OWoTple CWAmVplMyTNn8cWo5kXUS4wFh9aVqNwgqsf/29v9Jt2OL8fTBa2rW6hml6P0jd88QvWIatgfiAm LqIxJIpRHSZFBQmtNzIAs+MpeuSvq4MbMpBbCe7oL+oYBMyFaCCv6nxiRAPz7l9OROp3V4vZCOq PtnLhGuaZXSxJfRLjXVze1odsl7k0w== X-Google-Smtp-Source: AGHT+IE7iRvnLbtQOBBohb/TEeQCCMkIB9F32YfC+a1kcNPSJRKNStKrmEGpMgr74uC1ZuiEePW9Jg== X-Received: by 2002:a17:907:97c6:b0:ab7:10e1:8779 with SMTP id a640c23a62f3a-ab789aef873mr878828766b.27.1739032850563; Sat, 08 Feb 2025 08:40:50 -0800 (PST) Received: from localhost.localdomain ([2a02:908:e842:bf20:e115:64e7:5d6:176]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ab773337f3dsm501539166b.139.2025.02.08.08.40.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Feb 2025 08:40:50 -0800 (PST) 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 v7 03/11] kbuild: Add list_count_nodes and list_for_each_entry_from Date: Sat, 8 Feb 2025 17:39:51 +0100 Message-Id: <20250208163959.3973163-4-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250208163959.3973163-1-ole0811sch@gmail.com> References: <20250208163959.3973163-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_count_nodes and list_for_each_entry_from exist in include/linux/list.h. Add them to scripts/include/list.h. Signed-off-by: Ole Schuerks Reviewed-by: Luis Chamberlain --- scripts/include/list.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/scripts/include/list.h b/scripts/include/list.h index 8bdcaadca709..b60f1cc878f1 100644 --- a/scripts/include/list.h +++ b/scripts/include/list.h @@ -219,6 +219,20 @@ static inline int list_empty(const struct list_head *head) return head->next == head; } +/** + * list_count_nodes - count nodes in the list + * @head: the head for your list. + */ +static inline size_t list_count_nodes(struct list_head *head) +{ + size_t count = 0; + + for (struct list_head *pos = head->next; pos != head; pos = pos->next) + ++count; + + return count; +} + /** * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. @@ -310,6 +324,18 @@ 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 from the current point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Iterate over list of given type, continuing from current position. + */ +#define list_for_each_entry_from(pos, head, member) \ + for (; !list_entry_is_head(pos, head, member); \ + pos = list_next_entry(pos, member)) + /* * Double linked lists with a single pointer list head. * Mostly useful for hash tables where the two pointer list head is From patchwork Sat Feb 8 16:39:52 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13966442 Received: from mail-ej1-f50.google.com (mail-ej1-f50.google.com [209.85.218.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 A927022E019; Sat, 8 Feb 2025 16:40:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739032860; cv=none; b=j5S4NzoO6zRv0zK3vvQcnFqFkFDk2+MvEZcfITSVcgjgpUDIQ166w8P91yrIjpCXDK4JBemtAAsmmqCrfSjIcdjg5M1tXGygMT4D49RbjA50X4jgruRVByZ3DLYhaid6Zr856fUzxNYaZJaZmKyaExBrl63CT9DBlJZ/cipHsvA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739032860; c=relaxed/simple; bh=LyCmYDTzyaESOY6EKVCFSSnVLUReqVrznPmy74qPEXc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ZoYKbrceL/6MdCc6lw8jaksEy8Pn0mGg9mJF8//WbEB2tJlTyRIh6r66uKEem0iPMNefOzlWpHaDPxJCXySh8M/c2J8lehnEzC0iBe8mFFvnIWsOQE44bZu3QOtJs8njrxn/0w17Aiqg1x6JQHyfa9csHxGNhZpHi31/0wNS8sU= 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=hpTC5Ewy; arc=none smtp.client-ip=209.85.218.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="hpTC5Ewy" Received: by mail-ej1-f50.google.com with SMTP id a640c23a62f3a-aaec111762bso742799066b.2; Sat, 08 Feb 2025 08:40:56 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1739032855; x=1739637655; 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=hYfX1D5bf9yYQNdoGMsNgIzpcCfrXukpAMHbFLNM1Z0=; b=hpTC5EwyKfpTCpoq/hmMXSqKK14/QtpgdwaWyyHspZeQNpd8S9V7d8Aoffa5TTmIaj Dsocz6V9+akDByl4uBfVyoXRBnudiJ0yP1lIR+A6afYPC7bcQNEiHCPf42RxeJF0lQx8 SM9X7BYSB2lb3xxZHjcmJ6a0MVSzJj5+VKAhAm4Oz3nI96TxQ1klXcT2Jd9NMoS+dgF9 9TOQg+Equx2B2YaO3QVcZTNgiI+bnsADvK9hz+H9d9uJxSSrTOwZ1dgPa69073u4qV4Z tzBNzEDlidVXn63CPlnrW5Vb48ba/3Do5InFwi0bywsffogOmjafi+QfIN0MDKi2prGD 4Lsg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1739032855; x=1739637655; 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=hYfX1D5bf9yYQNdoGMsNgIzpcCfrXukpAMHbFLNM1Z0=; b=C+UjX0qczuJPpf8+nd8AlJAg69E6O55Xx10XPXFxfiE8aFa8lLOx2HErHgLsGNWbVp kzFJAsY0QsFLcLnUcpa9mWNefXWEOc6Yr1t9AH+UuOzuYDaFvj81iXo+svAbbPmFHPqB FlPwambfKbmtj2TmXdC0cR3EDYd2NMALxe3HFBSzoXmiVFASjD4cl7lME+xorM8TFm7I 8NnvMcLOUvY/cvkXpi2bUJ+OGb+k6AKuzpgYc09z4jfQD2CDGRRtIahHRQu6umI0rxDh PzOfJxgBGoGSNrYCQw6Nzt2TVbvEc/yQ0Krt7NJj0ybCJFKSXlOkHM5OxAMLcA0MZv/6 ff+A== X-Forwarded-Encrypted: i=1; AJvYcCVhJRYcI8AM3WbXHvhFcQOJXLB6IzTINlik0ogdBMmE59snxCrG0kiNw6z9oH2hskGsk+E0QT/puF9DFN0=@vger.kernel.org X-Gm-Message-State: AOJu0YykmB72wbiQ8WZU+FNL9GFlBz1oZBKleUD9B7aU9t2OTBYE2mlP 8HEGHLb7GJwkEtgQr3vqDpnJZLcc1TeKWM6s6f8Oh4SfCgp0lZ683yDXgA== X-Gm-Gg: ASbGncvvDsireX4ABpHqCxLODWt8s+TWCmdNWURPcgTYRzFTg3u3yeheKuFdrJbPEtq Zo8KsbX1AXyCqTFr6MZxpyRnmVMJBRJLL+aUq2CJRAplS0o1sgvhstq0Iy1kAbYIpXyn8MSIXNX XHcFTTy388SV7xnnSwYHxmFaPuUdeyt3cLgbdzCpr2JMRQcqzAt3lz2Cb5AsIBJDFEcI3WHmq+u 2QV6nfPNL2UAJNl2RP1P8JfM2uATDp72WfHbk3WRNnrKZPasJr8Ep4sytHawezppGGMwrCS0HWQ 370IAdwHJNnqr+5FcnENCMWmz52PFA== X-Google-Smtp-Source: AGHT+IFRFVSvkG0m+rcyJek7tpW7gR0UwrBGtS7/LVrjiJEL1NnunBZCTk6/i/yZMd4N6mywR6xmJQ== X-Received: by 2002:a17:907:6e88:b0:ab6:330c:7af0 with SMTP id a640c23a62f3a-ab789b3744bmr903328166b.20.1739032854622; Sat, 08 Feb 2025 08:40:54 -0800 (PST) Received: from localhost.localdomain ([2a02:908:e842:bf20:e115:64e7:5d6:176]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ab773337f3dsm501539166b.139.2025.02.08.08.40.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Feb 2025 08:40:54 -0800 (PST) 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 v7 04/11] kconfig: Add files for building constraints Date: Sat, 8 Feb 2025 17:39:52 +0100 Message-Id: <20250208163959.3973163-5-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250208163959.3973163-1-ole0811sch@gmail.com> References: <20250208163959.3973163-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 files for the translation of the Kconfig-model into propositional logic. The translation associates constraints in propositional logic with the individual symbols. 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 | 1777 ++++++++++++++++++++++++++++++ scripts/kconfig/cf_constraints.h | 24 + 2 files changed, 1801 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..886cf12868ad --- /dev/null +++ b/scripts/kconfig/cf_constraints.c @@ -0,0 +1,1777 @@ +// 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" +#include "../include/list.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 = list_next_entry(node, node); + + choice = node->elem; + list_for_each_entry_from(node2, &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 = list_next_entry(node, node); + + choice = node->elem; + + tmp = CF_LIST_INIT(sym); + list_for_each_entry_from(node2, &promptItems->list, + node) { + choice2 = node2->elem; + if (choice2->type == S_TRISTATE) + CF_PUSH_BACK(tmp, choice2, sym); + } + if (list_empty(&tmp->list)) + continue; + + CF_LIST_FOR_EACH(node2, tmp, sym) { + struct pexpr *choice2_mod = + pexpr_alloc_symbol(choice2->fexpr_m); + + choice2 = node2->elem; + if (list_is_first(&node2->node, &tmp->list)) + c1 = pexpr_not_share(choice2_mod, data); + else + c1 = pexpr_and( + c1, + pexpr_not_share(choice2_mod, + data), + data, PEXPR_ARGX); + + PEXPR_PUT(choice2_mod); + } + c1 = pexpr_implies(pexpr_alloc_symbol(choice->fexpr_y), + c1, data, PEXPR_ARGX); + sym_add_constraint(sym, c1, data); + pexpr_put(c1); + } + } + CF_LIST_FREE(promptItems, sym); + CF_LIST_FREE(items, sym); +} + +/* + * build the constraints for invisible options such as defaults + */ +static void add_invisible_constraints(struct symbol *sym, struct cfdata *data) +{ + struct property *prompt = sym_get_prompt(sym); + struct pexpr *promptCondition_both, *promptCondition_yes, *noPromptCond; + struct pexpr *npc; + struct defm_list *defaults; + struct pexpr *default_y, *default_m, *default_both; + + /* no constraints for the prompt, nothing to do here */ + if (prompt != NULL && !prompt->visible.expr) + return; + + if (prompt == NULL) { + promptCondition_both = + pexpr_alloc_symbol(data->constants->const_false); + promptCondition_yes = + pexpr_alloc_symbol(data->constants->const_false); + noPromptCond = pexpr_alloc_symbol(data->constants->const_true); + } else { + struct property *p; + + promptCondition_both = + pexpr_alloc_symbol(data->constants->const_false); + promptCondition_yes = + pexpr_alloc_symbol(data->constants->const_false); + + /* some symbols have multiple prompts */ + for_all_prompts(sym, p) { + promptCondition_both = + pexpr_or(promptCondition_both, + expr_calculate_pexpr_both( + p->visible.expr, data), + data, PEXPR_ARGX); + promptCondition_yes = pexpr_or( + promptCondition_yes, + expr_calculate_pexpr_y(p->visible.expr, data), + data, PEXPR_ARGX); + } + noPromptCond = pexpr_not_share(promptCondition_both, data); + } + + if (NPC_OPTIMISATION) { + struct fexpr *npc_fe = + fexpr_create(data->sat_variable_nr++, FE_NPC, ""); + + if (sym_is_choice(sym)) + str_append(&npc_fe->name, "Choice_"); + + str_append(&npc_fe->name, sym_get_name(sym)); + str_append(&npc_fe->name, "_NPC"); + sym->noPromptCond = npc_fe; + fexpr_add_to_satmap(npc_fe, data); + + npc = pexpr_alloc_symbol(npc_fe); + + if (!sym_is_choice_value(sym) && !sym_is_choice(sym)) { + struct pexpr *c = + pexpr_implies_share(noPromptCond, npc, data); + sym_add_constraint(sym, c, data); + pexpr_put(c); + } + } else { + npc = pexpr_get(noPromptCond); + } + + defaults = calc_default_conditions(sym, data); + default_y = get_default_y(defaults, data); + default_m = get_default_m(defaults, data); + default_both = pexpr_or_share(default_y, default_m, data); + + /* + * tristate elements are only selectable as yes, if they are visible as + * yes + */ + if (sym->type == S_TRISTATE) { + struct pexpr *e1 = pexpr_implies( + promptCondition_both, + pexpr_implies(pexpr_alloc_symbol(sym->fexpr_y), + promptCondition_yes, data, + PEXPR_ARG1), + data, PEXPR_ARG2); + + sym_add_constraint(sym, e1, data); + pexpr_put(e1); + } + + /* if invisible and off by default, then a symbol can only be + * deactivated by its reverse dependencies + */ + if (sym->type == S_TRISTATE) { + struct pexpr *sel_y, *sel_m, *sel_both; + struct pexpr *c1, *c2, *c3; + struct pexpr *d1, *d2, *d3; + struct pexpr *e1, *e2, *e3; + + if (sym->fexpr_sel_y != NULL) { + sel_y = pexpr_implies( + pexpr_alloc_symbol(sym->fexpr_y), + pexpr_alloc_symbol(sym->fexpr_sel_y), data, + PEXPR_ARGX); + sel_m = pexpr_implies( + pexpr_alloc_symbol(sym->fexpr_m), + pexpr_alloc_symbol(sym->fexpr_sel_m), data, + PEXPR_ARGX); + sel_both = pexpr_implies( + pexpr_alloc_symbol(sym->fexpr_y), + pexpr_or(pexpr_alloc_symbol(sym->fexpr_sel_m), + pexpr_alloc_symbol(sym->fexpr_sel_y), + data, PEXPR_ARGX), + data, PEXPR_ARGX); + } else { + sel_y = pexpr_not(pexpr_alloc_symbol(sym->fexpr_y), + data); + sel_m = pexpr_not(pexpr_alloc_symbol(sym->fexpr_m), + data); + sel_both = pexpr_get(sel_y); + } + + c1 = pexpr_implies(pexpr_not_share(default_y, data), sel_y, + data, PEXPR_ARG1); + c2 = pexpr_implies(pexpr_alloc_symbol(modules_sym->fexpr_y), c1, + data, PEXPR_ARG1); + c3 = pexpr_implies_share(npc, c2, data); + sym_add_constraint(sym, c3, data); + + d1 = pexpr_implies(pexpr_not_share(default_m, data), sel_m, + data, PEXPR_ARG1); + d2 = pexpr_implies(pexpr_alloc_symbol(modules_sym->fexpr_y), d1, + data, PEXPR_ARG1); + d3 = pexpr_implies_share(npc, d2, data); + sym_add_constraint(sym, d3, data); + + e1 = pexpr_implies(pexpr_not_share(default_both, data), + sel_both, data, PEXPR_ARG1); + e2 = pexpr_implies( + pexpr_not(pexpr_alloc_symbol(modules_sym->fexpr_y), + data), + e1, data, PEXPR_ARG1); + e3 = pexpr_implies_share(npc, e2, data); + sym_add_constraint(sym, e3, data); + PEXPR_PUT(sel_y, sel_m, sel_both, c1, c2, c3, d1, d2, d3, e1, + e2, e3); + } else if (sym->type == S_BOOLEAN) { + struct pexpr *sel_y; + struct pexpr *e1, *e2; + + if (sym->fexpr_sel_y != NULL) + sel_y = pexpr_implies( + pexpr_alloc_symbol(sym->fexpr_y), + pexpr_alloc_symbol(sym->fexpr_sel_y), data, + PEXPR_ARGX); + else + sel_y = pexpr_not(pexpr_alloc_symbol(sym->fexpr_y), + data); + + e1 = pexpr_implies(pexpr_not_share(default_both, data), + sel_y, data, PEXPR_ARG1); + e2 = pexpr_implies_share(npc, e1, data); + + sym_add_constraint_unique(sym, e2, data); + PEXPR_PUT(sel_y, e1, e2); + } else { + /* if non-boolean is invisible and no default's condition is + * fulfilled, then the symbol is not set + */ + struct pexpr *default_any = get_default_any(sym, data); + struct pexpr *e1 = + pexpr_alloc_symbol(data->constants->const_true); + struct pexpr *e2, *e3; + struct fexpr_node *node; + bool first = true; + + /* e1 = "sym is not set" */ + CF_LIST_FOR_EACH(node, sym->nb_vals, fexpr) { + if (first) { + first = false; + continue; + } + e1 = pexpr_and(e1, + pexpr_not(pexpr_alloc_symbol(node->elem), + data), + data, PEXPR_ARGX); + } + + e2 = pexpr_implies(pexpr_not_share(default_any, data), e1, + data, PEXPR_ARG1); + e3 = pexpr_implies_share(npc, e2, data); + + sym_add_constraint(sym, e3, data); + PEXPR_PUT(default_any, e1, e2, e3); + } + + /* if invisible and on by default, then a symbol can only be deactivated + * by its dependencies + */ + if (list_empty(&defaults->list)) { + // nothing to do + } else if (sym->type == S_TRISTATE) { + struct pexpr *e1; + struct pexpr *e2; + + e1 = pexpr_implies( + npc, + pexpr_implies(default_y, + pexpr_alloc_symbol(sym->fexpr_y), data, + PEXPR_ARG2), + data, PEXPR_ARG2); + sym_add_constraint(sym, e1, data); + + e2 = pexpr_implies( + npc, + pexpr_implies(default_m, + sym_get_fexpr_both(sym, data), + data, PEXPR_ARG2), + data, PEXPR_ARG2); + sym_add_constraint(sym, e2, data); + PEXPR_PUT(e1, e2); + } else if (sym->type == S_BOOLEAN) { + struct pexpr *c; + struct pexpr *c2; + + c = pexpr_implies(default_both, + pexpr_alloc_symbol(sym->fexpr_y), data, + PEXPR_ARG2); + + // TODO tristate choice hack + + c2 = pexpr_implies_share(npc, c, data); + sym_add_constraint(sym, c2, data); + PEXPR_PUT(c, c2); + } else { + /* if non-boolean invisible, then it assumes the correct + * default (if any). + */ + struct defm_node *node; + struct pexpr *cond, *c; + struct fexpr *f; + + CF_LIST_FOR_EACH(node, defaults, defm) { + f = node->elem->val; + cond = node->elem->e; + c = pexpr_implies(npc, + pexpr_implies(cond, + pexpr_alloc_symbol(f), + data, PEXPR_ARG2), + data, PEXPR_ARG2); + sym_add_constraint(sym, c, data); + pexpr_put(c); + } + } + + PEXPR_PUT(promptCondition_yes, promptCondition_both, noPromptCond, npc, + default_y, default_m, default_both); + defm_list_destruct(defaults); +} + +/* + * add the known values from the default and range properties + */ +static void sym_add_nonbool_values_from_default_range(struct symbol *sym, + struct cfdata *data) +{ + struct property *p; + + for_all_defaults(sym, p) { + if (p == NULL) + continue; + + /* add the value to known values, if it doesn't exist yet */ + sym_create_nonbool_fexpr(sym, p->expr->left.sym->name, data); + } + + for_all_properties(sym, p, P_RANGE) { + if (p == NULL) + continue; + + /* add the values to known values, if they don't exist yet */ + sym_create_nonbool_fexpr(sym, p->expr->left.sym->name, data); + sym_create_nonbool_fexpr(sym, p->expr->right.sym->name, data); + } +} + +/* + * build the range constraints for int/hex: + * For each range and each value in `sym->nb_vals` that's not in the range: + * If the range's condition is fulfilled, then sym can't have this value. + */ +static void sym_add_range_constraints(struct symbol *sym, struct cfdata *data) +{ + struct property *prop; + struct pexpr *prevs; + struct pexpr *propCond; + struct pexpr_list *prevCond; // list of all conditions of the ranges + // from the previous iterations + + prevCond = CF_LIST_INIT(pexpr); + + for_all_properties(sym, prop, P_RANGE) { + int base; + long long range_min, range_max, tmp; + struct fexpr_node *node; + bool first; + + if (prop == NULL) + continue; + + prevs = pexpr_alloc_symbol(data->constants->const_true); + propCond = prop_get_condition(prop, data); + + // construct prevs as "none of the previous ranges' conditions + // were fulfilled but this range's condition is" + if (list_empty(&prevCond->list)) { + pexpr_put(prevs); + prevs = pexpr_get(propCond); +; + } else { + struct pexpr_node *node; + + CF_LIST_FOR_EACH(node, prevCond, pexpr) + prevs = pexpr_and(pexpr_not_share(node->elem, + data), + prevs, data, PEXPR_ARGX); + + prevs = pexpr_and(propCond, prevs, data, + PEXPR_ARG2); + } + CF_PUSH_BACK(prevCond, pexpr_get(propCond), pexpr); + + switch (sym->type) { + case S_INT: + base = 10; + break; + case S_HEX: + base = 16; + break; + default: + return; + } + + range_min = sym_get_range_val(prop->expr->left.sym, base); + range_max = sym_get_range_val(prop->expr->right.sym, base); + + first = true; + /* can skip the first non-boolean value, since this is 'n' */ + CF_LIST_FOR_EACH(node, sym->nb_vals, fexpr) { + struct pexpr *not_nb_val; + struct pexpr *c; + + if (first) { + first = false; + continue; + } + + tmp = strtoll(str_get(&node->elem->nb_val), NULL, base); + + /* known value is in range, nothing to do here */ + if (tmp >= range_min && tmp <= range_max) + continue; + + not_nb_val = + pexpr_not(pexpr_alloc_symbol(node->elem), data); + c = pexpr_implies_share(prevs, not_nb_val, data); + sym_add_constraint(sym, c, data); + PEXPR_PUT(not_nb_val, c); + } + PEXPR_PUT(prevs, propCond); + } + + pexpr_list_free_put(prevCond); + +} + +/* + * at least 1 of the known values for a non-boolean symbol must be true + */ +static void sym_nonbool_at_least_1(struct symbol *sym, struct cfdata *data) +{ + struct pexpr *e; + struct fexpr_node *node; + + if (!sym_is_nonboolean(sym)) + return; + + e = pexpr_alloc_symbol(data->constants->const_false); + CF_LIST_FOR_EACH(node, sym->nb_vals, fexpr) + e = pexpr_or(e, pexpr_alloc_symbol(node->elem), data, + PEXPR_ARGX); + + sym_add_constraint(sym, e, data); + pexpr_put(e); +} + +/* + * at most 1 of the known values for a non-boolean symbol can be true + */ +static void sym_nonbool_at_most_1(struct symbol *sym, struct cfdata *data) +{ + struct fexpr_node *node1; + + if (!sym_is_nonboolean(sym)) + return; + + /* iterate over all subsets of sym->nb_vals of size 2 */ + CF_LIST_FOR_EACH(node1, sym->nb_vals, fexpr) { + struct pexpr *e1 = pexpr_alloc_symbol(node1->elem); + struct fexpr_node *node2; + + list_for_each_entry_reverse(node2, &sym->nb_vals->list, node) { + struct pexpr *e2, *e; + + if (node2 == node1) + break; + e2 = pexpr_alloc_symbol(node2->elem); + e = pexpr_or(pexpr_not_share(e1, data), + pexpr_not_share(e2, data), + data, PEXPR_ARGX); + + sym_add_constraint(sym, e, data); + PEXPR_PUT(e, e2); + } + pexpr_put(e1); + } +} + +/* + * a visible prompt for a non-boolean implies a value for the symbol + */ +static void sym_add_nonbool_prompt_constraint(struct symbol *sym, + struct cfdata *data) +{ + struct property *prompt; + struct pexpr *promptCondition; + struct pexpr *n; + struct pexpr *c = NULL; + + prompt = sym_get_prompt(sym); + if (prompt == NULL) + return; + + promptCondition = prop_get_condition(prompt, data); + n = pexpr_alloc_symbol(sym_get_nonbool_fexpr(sym, "n")); + + if (n->type != PE_SYMBOL || n->left.fexpr == NULL) + goto cleanup; + + c = pexpr_implies(promptCondition, pexpr_not_share(n, data), data, + PEXPR_ARG2); + + sym_add_constraint(sym, c, data); + +cleanup: + PEXPR_PUT(n, promptCondition, c); +} + +static struct default_map *create_default_map_entry(struct fexpr *val, + struct pexpr *e) +{ + struct default_map *map = malloc(sizeof(struct default_map)); + + pexpr_get(e); + map->val = val; + map->e = e; + + return map; +} + +/** + * findDefaultEntry() + * @val: Value that the entry must have + * @defaults: List of defaults to search in + * @constants: To get ``constants->const_false`` from + * + * Finds an entry in @defaults whose &default_map.val attribute is the same + * pointer as the @val argument. + * + * Return: The condition &default_map.e of the found entry, or + * ``pexf(constants->const_false)`` if none was found. To be pexpr_put() by the + * caller. + */ +static struct pexpr *findDefaultEntry(struct fexpr *val, + struct defm_list *defaults, + struct constants *constants) +{ + struct defm_node *node; + + CF_LIST_FOR_EACH(node, defaults, defm) { + if (val == node->elem->val) { + pexpr_get(node->elem->e); + return node->elem->e; + } + } + + return pexpr_alloc_symbol(constants->const_false); +} + +/* + * accumulated during execution of add_defaults(), a disjunction of the + * conditions for all default props of a symbol + */ +static struct pexpr *covered; + +static bool is_tri_as_num(struct symbol *sym) +{ + if (!sym->name) + return false; + + return !strcmp(sym->name, "0") + || !strcmp(sym->name, "1") + || !strcmp(sym->name, "2"); +} + +/** + * add_to_default_map() - Add to or update an entry in a default list + * @entry: Will be consumed by this function, i.e. the caller should and need + * only access @entry via @defaults. + */ +static void add_to_default_map(struct defm_list *defaults, + struct default_map *entry, struct symbol *sym) +{ + /* as this is a map, the entry must be replaced if it already exists */ + if (sym_is_boolean(sym)) { + struct default_map *map; + struct defm_node *node; + + CF_LIST_FOR_EACH(node, defaults, defm) { + map = node->elem; + if (map->val->sym == entry->val->sym) { + pexpr_put(map->e); + map->e = entry->e; + free(entry); + return; + } + } + CF_PUSH_BACK(defaults, entry, defm); + } else { + struct default_map *map; + struct defm_node *node; + + CF_LIST_FOR_EACH(node, defaults, defm) { + map = node->elem; + if (map->val->satval == entry->val->satval) { + pexpr_put(map->e); + map->e = entry->e; + free(entry); + return; + } + } + CF_PUSH_BACK(defaults, entry, defm); + } +} + +/** + * updateDefaultList() - Update a default list with a new value-condition pair + * @val: The value whose condition will be updated + * @newCond: The condition of the default prop. Does not include the condition + * that the earlier default's conditions are not fulfilled. + * @result: the default list + * @sym: the symbol that the defaults belong to + * + * Update the condition that @val will be used for @sym by considering the next + * default property, whose condition is given by @newCond. + */ +static void updateDefaultList(struct fexpr *val, struct pexpr *newCond, + struct defm_list *result, struct symbol *sym, + struct cfdata *data) +{ + // The current condition of @val deduced from the previous default props + struct pexpr *prevCond = findDefaultEntry(val, result, data->constants); + // New combined condition for @val + struct pexpr *condUseVal = + pexpr_or(prevCond, + pexpr_and(newCond, pexpr_not_share(covered, data), + data, PEXPR_ARG2), + data, PEXPR_ARG2); + add_to_default_map(result, create_default_map_entry(val, condUseVal), + sym); + covered = pexpr_or(covered, newCond, data, PEXPR_ARG1); + PEXPR_PUT(prevCond, condUseVal); +} + +/** + * add_defaults() - Generate list of default values and their conditions + * @defaults: List of the default properties + * @ctx: Additional condition that needs to be fulfilled for any default. May be + * NULL. + * @result: List that will be filled + * @sym: Symbol that the defaults belong to + * + * Creates a map from values that @sym can assume to the conditions under which + * they will be assumed. Without @ctx, this will only consider the conditions + * directly associated with the defaults, e.g. sym->dir_dep would not be + * considered. + * + * As a side effect, the &symbol->nb_vals of @sym will be added for + * all default values (as well as the @symbol->nb_vals of other symbols @sym has + * as default (recursively)). + */ +static void add_defaults(struct prop_list *defaults, struct expr *ctx, + struct defm_list *result, struct symbol *sym, + struct cfdata *data) +{ + struct prop_node *node; + struct property *p; + struct expr *expr; + + CF_LIST_FOR_EACH(node, defaults, prop) { + p = node->elem; + /* calculate expr as whether the default's condition (and the + * one inherited from ctx) is fulfilled + */ + if (p->visible.expr) { + if (ctx == NULL) + expr = p->visible.expr; + else + expr = expr_alloc_and(p->visible.expr, ctx); + } else { + if (ctx == NULL) + expr = expr_alloc_symbol(&symbol_yes); + else + expr = expr_alloc_and( + expr_alloc_symbol(&symbol_yes), ctx); + } + + /* if tristate and def.value = y */ + if (p->expr->type == E_SYMBOL && sym->type == S_TRISTATE && + p->expr->left.sym == &symbol_yes) { + struct pexpr *expr_y = + expr_calculate_pexpr_y(expr, data); + struct pexpr *expr_m = + expr_calculate_pexpr_m(expr, data); + + updateDefaultList(data->constants->symbol_yes_fexpr, + expr_y, result, sym, data); + updateDefaultList(data->constants->symbol_mod_fexpr, + expr_m, result, sym, data); + PEXPR_PUT(expr_y, expr_m); + } + /* if def.value = n/m/y */ + else if (p->expr->type == E_SYMBOL && + sym_is_tristate_constant(p->expr->left.sym) && + sym_is_boolean(sym)) { + struct fexpr *s; + struct pexpr *expr_both = + expr_calculate_pexpr_both(expr, data); + + if (p->expr->left.sym == &symbol_yes) + s = data->constants->symbol_yes_fexpr; + else if (p->expr->left.sym == &symbol_mod) + s = data->constants->symbol_mod_fexpr; + else + s = data->constants->symbol_no_fexpr; + + updateDefaultList(s, expr_both, result, sym, data); + pexpr_put(expr_both); + } + /* if def.value = n/m/y, but written as 0/1/2 for a boolean */ + else if (sym_is_boolean(sym) && p->expr->type == E_SYMBOL && + p->expr->left.sym->type == S_UNKNOWN && + is_tri_as_num(p->expr->left.sym)) { + struct fexpr *s; + struct pexpr *expr_both = + expr_calculate_pexpr_both(expr, data); + + if (!strcmp(p->expr->left.sym->name, "0")) + s = data->constants->symbol_no_fexpr; + else if (!strcmp(p->expr->left.sym->name, "1")) + s = data->constants->symbol_mod_fexpr; + else + s = data->constants->symbol_yes_fexpr; + + updateDefaultList(s, expr_both, result, sym, data); + pexpr_put(expr_both); + } + /* if def.value = non-boolean constant */ + else if (expr_is_nonbool_constant(p->expr)) { + struct fexpr *s = sym_get_or_create_nonbool_fexpr( + sym, p->expr->left.sym->name, data); + struct pexpr *expr_both = + expr_calculate_pexpr_both(expr, data); + + updateDefaultList(s, expr_both, result, sym, data); + pexpr_put(expr_both); + } + /* any expression which evaluates to n/m/y for a tristate */ + else if (sym->type == S_TRISTATE) { + struct expr *e_tmp = expr_alloc_and(p->expr, expr); + struct pexpr *expr_y = + expr_calculate_pexpr_y(e_tmp, data); + struct pexpr *expr_m = + expr_calculate_pexpr_m(e_tmp, data); + + updateDefaultList(data->constants->symbol_yes_fexpr, + expr_y, result, sym, data); + updateDefaultList(data->constants->symbol_mod_fexpr, + expr_m, result, sym, data); + PEXPR_PUT(expr_y, expr_m); + } + /* if non-boolean && def.value = non-boolean symbol */ + else if (p->expr->type == E_SYMBOL && sym_is_nonboolean(sym) && + sym_is_nonboolean(p->expr->left.sym)) { + CF_DEF_LIST(nb_sym_defaults, prop); + struct property *p_tmp; + + /* Add defaults of other symbol as possible defaults for + * this symbol + */ + for_all_defaults(p->expr->left.sym, p_tmp) + CF_PUSH_BACK(nb_sym_defaults, p_tmp, prop); + + add_defaults(nb_sym_defaults, expr, result, sym, data); + CF_LIST_FREE(nb_sym_defaults, prop); + } + /* any expression which evaluates to n/m/y */ + else { + struct expr *e_tmp = expr_alloc_and(p->expr, expr); + struct pexpr *expr_both = + expr_calculate_pexpr_both(e_tmp, data); + + updateDefaultList(data->constants->symbol_yes_fexpr, + expr_both, result, sym, data); + + pexpr_put(expr_both); + } + } +} + +/** + * get_defaults() - Generate list of default values and their conditions + * @sym: Symbol whose defaults we want to look at + * + * Creates a map from values that @sym can assume to the conditions under which + * they will be assumed. This will only consider the conditions + * directly associated with the defaults, e.g. sym->dir_dep would not be + * considered. + * + * As a side effect, the &symbol->nb_vals of @sym will be added for + * all default values (as well as the @symbol->nb_vals of other symbols @sym has + * as default (recursively)). + */ +static struct defm_list *calc_default_conditions(struct symbol *sym, + struct cfdata *data) +{ + CF_DEF_LIST(result, defm); + struct prop_list *defaults; /* list of default props of sym */ + struct property *p; + + covered = pexpr_alloc_symbol(data->constants->const_false); + + defaults = CF_LIST_INIT(prop); + for_all_defaults(sym, p) + CF_PUSH_BACK(defaults, p, prop); + + add_defaults(defaults, NULL, result, sym, data); + CF_LIST_FREE(defaults, prop); + pexpr_put(covered); + + return result; +} + +/* + * return the condition for "y", False if it doesn't exist + */ +static struct pexpr *get_default_y(struct defm_list *list, struct cfdata *data) +{ + struct default_map *entry; + struct defm_node *node; + + CF_LIST_FOR_EACH(node, list, defm) { + entry = node->elem; + if (entry->val->type == FE_SYMBOL && + entry->val->sym == &symbol_yes) { + pexpr_get(entry->e); + return entry->e; + } + } + + return pexpr_alloc_symbol(data->constants->const_false); +} + +/* + * return the condition for "m", False if it doesn't exist + */ +static struct pexpr *get_default_m(struct defm_list *list, struct cfdata *data) +{ + struct default_map *entry; + struct defm_node *node; + + CF_LIST_FOR_EACH(node, list, defm) { + entry = node->elem; + if (entry->val->type == FE_SYMBOL && + entry->val->sym == &symbol_mod) { + pexpr_get(entry->e); + return entry->e; + } + } + + return pexpr_alloc_symbol(data->constants->const_false); +} + +/* + * return the constraint when _some_ default value will be applied + */ +static struct pexpr *get_default_any(struct symbol *sym, struct cfdata *data) +{ + struct property *prop; + struct expr *e; + struct pexpr *p; + + if (!sym_is_nonboolean(sym)) + return NULL; + + p = pexpr_alloc_symbol(data->constants->const_false); + for_all_defaults(sym, prop) { + if (prop->visible.expr) + e = prop->visible.expr; + else + e = expr_alloc_symbol(&symbol_yes); + + if (expr_can_evaluate_to_mod(e)) + p = pexpr_or(p, expr_calculate_pexpr_both(e, data), + data, PEXPR_ARGX); + + p = pexpr_or(p, expr_calculate_pexpr_y(e, data), data, + PEXPR_ARGX); + } + + return p; +} + +/* + * get the value for the range + */ +static long sym_get_range_val(struct symbol *sym, int base) +{ + sym_calc_value(sym); + switch (sym->type) { + case S_INT: + base = 10; + break; + case S_HEX: + base = 16; + break; + default: + break; + } + return strtol(sym->curr.val, NULL, base); +} + +/* + * count the number of all constraints + */ +unsigned int count_constraints(void) +{ + unsigned int c = 0; + struct symbol *sym; + + for_all_symbols(sym) { + if (sym->type == S_UNKNOWN) + continue; + + c += list_count_nodes(&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 Sat Feb 8 16:39:53 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13966443 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 A9E0F1F3B80; Sat, 8 Feb 2025 16:40:58 +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=1739032862; cv=none; b=hluSo6HkEhzV3FQgN6K9pvlNQ6yluUtZ1etHxiTfXs5z4R7g/f1HzyA7TtkODZgvOLUX/nBFQS7x4/Dtk9ijRl7JeZf1m0gz5tikeaJVgTjFg3oOkJVv3Ve5rlvhRUHldh83Qi2eI/HtTKVyFxQ7q2AvaXJZUig8FTjo6FqTzDI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739032862; c=relaxed/simple; bh=FqmSbtlMMWUBDLXGc/SBAEOIpjazmJiBgFrfgV/vWjo=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=kgp8z9hAHNLyiBouyjeSSp7l0bkRG5JiUm7qQppL9XW0vwfu2gA5EMF8berLRsg7kAoanBidn4oNL2Usxvr0nhYZIbdemx9mJFw6SjktcugF/noDP7Fm8TmxsiQnxrBERS8dY+Sg37XmfBckCo4VhyUkCcNQoU20AcbRvPpaMPI= 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=hv2y6ni/; 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="hv2y6ni/" Received: by mail-ej1-f46.google.com with SMTP id a640c23a62f3a-aaec61d0f65so681816066b.1; Sat, 08 Feb 2025 08:40:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1739032857; x=1739637657; 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=zhgTgfAqljXipPSLrUJVbcqzCrto8sA1S4DgHu12cwY=; b=hv2y6ni/ClJAtQQDo0T14BgfxhdAJJLzYm9DT5JsyEqGSylsWWckGgy/kFdWZAxGc9 irjF9lQ23hhw0PirlxrxL7+8pL250+y9kLBvDkvNTan++CYnD0lyRbFk5X9kSRyR1+8Z ki2Q15BlV+HjLJly8Lgk7raYcx5Hw8fice3OS191L5tXwyG6HWXQBUXl/yppJZ7hJnLT kggWPMfh/hV/hCU4XZZYRImTHXEx4OR+VUqgrtqj+xzKwJKxefroPfiLt75ipmszRNl1 Qawj/Ucvk9hAlo1nOn0EM8tgeDkM0jVfhGJ77jxybgcafg4B/gYkKPta88BLriA6dMat 5O8g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1739032857; x=1739637657; 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=zhgTgfAqljXipPSLrUJVbcqzCrto8sA1S4DgHu12cwY=; b=l2eesYIDKWNoB7nUYBqGsTGMVEqIKMAZlKltAfwMAevz1fOJffjPCSOE5VlCgFX7ZY 84pM0pOj9adISUnJxRLMee5WvqY0BGnBp96AeCwK1liPxGR9zMDfexyPWgl88qoB6ezP D5kkJWkUBvIuf3i7N/X6k4HCeNN4x/WmfOC+QCNbe/umwfX14kzuETfWk6LHjDQztcm4 aLFGTQ5/jndAbC0v7os++QGenHsyGk5XAgSTqa9x9kKckz1/SY0NhnUShCE3/zfvGLtk WfjFZyN5RvafyUBFF8vl1DFR/S6BSTWZfeiY3PyPohTTwaMUxanlTnREXUIsUjmX/c/6 vV8g== X-Forwarded-Encrypted: i=1; AJvYcCUDouEx+/g4XBok4O4xsScSucXdG1Ejh/m5R6U8pms3yLs0iVUdQC/m5Ft6bQ4ynsvmUvhm5cllQ/AvLQY=@vger.kernel.org X-Gm-Message-State: AOJu0YwEyhOxixUvv6Jn992ngN72M1QuDoVSLyoiYgScp7F5cD9ldkni H9wJOExKqTNRU9xjT0/E009ZmRw9LvxlDtFegBrx9qUMyyL7tzPsCUj6Vw== X-Gm-Gg: ASbGncugQLawPfEoxSTUJ9mHSxpl5cdZ/AJSBqNlSj6BXUxk9d27c5Kvh5g+KHRc7x8 SPCy6DA/10JzaLHMJaSqSjde9jbU7vbGRWhliKERvllvg6FlhoyS4qPmXjM6Pk8J+06jO9V/2oC ek4Q1q9k774ylz7QK5dKdvu0uEMs7UosEC1idqz0WLLfgfXuc7Ok6bdchgUhfFdcDhyVNLe4O+b RTQjiu2jmleRbSA6uP1mqeiyUkMkt0929YnDD6HuCkYUM9LX8etmq8sCRCPDoqfRVRID8gWuSsh fhvnOYVe6CzCdzn19Y4h3l6OGnkBMw== X-Google-Smtp-Source: AGHT+IHvdQmsQfURh4dOywnqlo1BPlmD5O4VBcAHTP/Rvh/VtO335bWnmmhqwowIjAM8+1g2Ska1/g== X-Received: by 2002:a17:907:3e8d:b0:ab7:63fa:e49c with SMTP id a640c23a62f3a-ab789cbe110mr870295066b.36.1739032856538; Sat, 08 Feb 2025 08:40:56 -0800 (PST) Received: from localhost.localdomain ([2a02:908:e842:bf20:e115:64e7:5d6:176]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ab773337f3dsm501539166b.139.2025.02.08.08.40.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Feb 2025 08:40:56 -0800 (PST) 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 v7 05/11] kconfig: Add files for handling expressions Date: Sat, 8 Feb 2025 17:39:53 +0100 Message-Id: <20250208163959.3973163-6-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250208163959.3973163-1-ole0811sch@gmail.com> References: <20250208163959.3973163-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 files containing functions and macros to deal with propositional formulas, which are used for the translation of the Kconfig-model into propositional logic and in the conflict resolution 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_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 Sat Feb 8 16:39:54 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13966444 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 7843E1F3BAB; Sat, 8 Feb 2025 16:41:01 +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=1739032864; cv=none; b=dmIoCap48hnhDcmNEgYfyyDJKxV7Nt54Q1zbHrPY0u1RHcZF29sL7dl9ftV3q+0Q1HDctoP9uhHRwvppczIczeWSyXLhlC+nb2cyChDDqJB8BdBb7UKdvm5UgvC5TZImXhnaJwiXSOkauD91pE3uZFgIo/FLMIOsGblZpDjfhvg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739032864; c=relaxed/simple; bh=zoO28aFVkP79VmHa6smDzpp65M5mfMHpJuokDSAizrY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=sX1YTOBxh1PPNcaWPU8i8oDazd3I12oAzE+1iu2LsfVzfMPYXv30D1MrcZ/2JNk/+gHQqm0kdJ9ZGP8yHz5peWi91F87fn0GDOjN9+it+VNjd65O2c/oTCkNvJRqSvgbQl310o31KN06sbVImysOIeF6myvWKZE9CFA3J3nTj3I= 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=PXdIn+K0; 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="PXdIn+K0" Received: by mail-ej1-f52.google.com with SMTP id a640c23a62f3a-ab7814aa2adso287919866b.0; Sat, 08 Feb 2025 08:41:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1739032860; x=1739637660; 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=XhSKulL/jcQkkOeYmaX9J3roT9oQLXT9cnUOl8BYF8U=; b=PXdIn+K0Cr8TvtKonZwvfBbi4eTVlONLu7qcmORxAw5SGOH1hXlRAyOc6E+I9cKl0W Q8VMEU229PFyqKtUxRTrnTiUMZMSE1uPQlk+lfeAkZGBKbknDkd7XHjCMBGyzY3F5S/G fwLP5CUf7UOaqw5XkowFproKOu37c+ozSCc13uFpWEeTdvc4+11JEZl1Hceru/Doojhg zMALVTgwmjL4z7843VTok+t/WvossQCS/UxX1S1lxrXqBkApyUOtbKN7V3IbbOq1843K MvylN4GjmwnX08WFjlnZ2d2361XXB7j9PIZdTcoSWyku8IIPi08MyYFFOvOCeLecYSo8 +wcg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1739032860; x=1739637660; 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=XhSKulL/jcQkkOeYmaX9J3roT9oQLXT9cnUOl8BYF8U=; b=mahgt1DxVmF2+ItRdHYfL0opSZuqrbjW2oY1UaX4nKbxaZVpy0pjzjjwCZDNT2qB/3 99kfdD4qhZzp+hLz4kJUbKLvYJxl3XU2AQfWwmnxFLYbGrpqh/Rh9MLXHE5C4KiKj9Oy HnYlp1dJUnwMlethzgLQ1ZlXOh/VEiGh5d4NGpezVHL5Cnwob6a/rxmNrBuu3rmkVhol xFplcL9+ZzJcgG67U40DgNCmZyP2IBaTog/WLy+AuFPPteHSOYz7peU4Mcr5JBlzyQc7 yHKSbRU5eXzXBb5l8furNySWQZbiYIKBHOgOcfqTKk7N+uzl63cjYqR3OcV+K/AGM7JO t7qQ== X-Forwarded-Encrypted: i=1; AJvYcCVk9YFLuS5lBFIRkKZPDwuTpTFTbI+dbeSobPgzUUl7uX5vmdVtz2uJ2rkiCD9/ILlMELwz5jbfDinuHCE=@vger.kernel.org X-Gm-Message-State: AOJu0Ywzo+Qrt4hNR8hmO/K9eh3Eycry0M9sN7klp2KZ3NOFD9ID1rwT Z2Yi0kzqiwhcho/sbiyhV7AOPlGGp1ek0ThKGkypkJI6CSLB2urtINtcVA== X-Gm-Gg: ASbGnctCL6yByAUZvBuqFPzikFL0bGIm90l0M4iB7zT3Wd5L31SX8JSeHruGduldiYz 025nezdDY1bD82M3s6uUXW/RqApGJDSgSEmQOlWhZOED0bPkns5acyANlGF+KIQXmCRZQNwLw8H QRyNwTYdb8ejs5Rgl+GxO0g7HIuUfD4y+r20FKZo4oIPUz2BhnZTFXpq5AvicvmO/3Ih/v8bnY8 OwGG23krwel2PtA3/uhSc8mmUgSmuoSvG4j1o5d1hkF+iOWP2AOOHr+8kIn+spILgcKn+5PFC4k 0OcnMgWE17rRCZ6QgblYCUFKrmWaGw== X-Google-Smtp-Source: AGHT+IFsQ39bt8oftFu0JDjY8z/1XuzzOIe+bfLKYLufDjjXqzZL/XT0qYqyNalizygJnR/1AK7W4A== X-Received: by 2002:a05:6402:40cb:b0:5d0:bf5e:eb8 with SMTP id 4fb4d7f45d1cf-5de45070884mr16647761a12.23.1739032859422; Sat, 08 Feb 2025 08:40:59 -0800 (PST) Received: from localhost.localdomain ([2a02:908:e842:bf20:e115:64e7:5d6:176]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ab773337f3dsm501539166b.139.2025.02.08.08.40.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Feb 2025 08:40:59 -0800 (PST) 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 v7 06/11] kconfig: Add files for fix generation Date: Sat, 8 Feb 2025 17:39:54 +0100 Message-Id: <20250208163959.3973163-7-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250208163959.3973163-1-ole0811sch@gmail.com> References: <20250208163959.3973163-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 files to implement the algorithm that is used to resolve conflicts, i.e., find sets of symbols whose values need to be changed in order to be able to make the desired changes of symbol values. 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_fixgen.c | 1136 +++++++++++++++++++++++++++++++++++ scripts/kconfig/cf_fixgen.h | 21 + 2 files changed, 1157 insertions(+) create mode 100644 scripts/kconfig/cf_fixgen.c create mode 100644 scripts/kconfig/cf_fixgen.h diff --git a/scripts/kconfig/cf_fixgen.c b/scripts/kconfig/cf_fixgen.c new file mode 100644 index 000000000000..88a6a464cccb --- /dev/null +++ b/scripts/kconfig/cf_fixgen.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_fixgen.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 *fixgen_run(PicoSAT *pico, struct cfdata *data) +{ + clock_t start, end; + double time; + struct fexl_list *diagnoses; + struct fexl_node *node; + + printd("Starting fix generation...\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_fixgen) { + stop_fixgen = 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 = cflist_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_fixgen.h b/scripts/kconfig/cf_fixgen.h new file mode 100644 index 000000000000..4526b5e4db06 --- /dev/null +++ b/scripts/kconfig/cf_fixgen.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Patrick Franz + */ + +#ifndef CF_FIXGEN_H +#define CF_FIXGEN_H + +#include "picosat_functions.h" +#include "cf_defs.h" + +/* initialize fixgen and return the diagnoses */ +struct sfl_list *fixgen_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 Sat Feb 8 16:39:55 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13966445 Received: from mail-ej1-f44.google.com (mail-ej1-f44.google.com [209.85.218.44]) (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 B10503DAC16; Sat, 8 Feb 2025 16:41:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739032870; cv=none; b=BmmwV35znZAQ05ZxDP9DhjkTIAPTbZvY1eNv6e1J4eDs/3Ts0vfHT4aCxV3yo7EfsfDAfF04QIwhlCZnN2q2ihb00nOWQVbLtodlM8OsCuRsrGhoe7HzQk3sP/7DPRRswrbWr51JHPu5+5T+teDLD1aTJZkscEk9IP9PF26PM6Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739032870; c=relaxed/simple; bh=pkvNrYUNC1dXrXHeGx43F+q9bWKmjgpPryymP1jnHUU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=sIKzYwlezbiksRZF/yxxsLVwQsb9JD3E8e7rj+oT+HKY1/KUJ2a51OthYIFJI9AErOZzO4JsIfj9brq8OYtJVr7UI/eC3CSLT1r4Q5ElTRzLXVhwkB3MC09yn1P/L7eBuuvLMjzI+myCe9O2YrNNvLQLzpJ67VRdgz29CDkAtFw= 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=MHNviXpO; arc=none smtp.client-ip=209.85.218.44 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="MHNviXpO" Received: by mail-ej1-f44.google.com with SMTP id a640c23a62f3a-aa67ac42819so480930666b.0; Sat, 08 Feb 2025 08:41:05 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1739032864; x=1739637664; 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=EQ0kUaUS3PHiloOOyIcVRYupmf7yXzMkKyQKclEF5Cc=; b=MHNviXpOHC2uZPb9m9s4XYg7W/ur2MGtAiPlRMLh2GwGj3clbLYroxHv+VWgV0jTfV eqJqaDtPMoK3cPt81cC4WEpy9kpdpQyAyZ5jEQd8JHt33EzqPDs0QWrW2/yjXtWrt2NI 0K+B59y6rANLGmv5YZcJ1chAtwn1nDWs3/VkV/Ar7fgB+RO72jDgaONtHtPNn6d3kr7L YshpbNM7/fo2MbkBXQRaSeeFXhcr3tTjxe1Lr6kkU+Z7eti2vks5WiY78aU2/Ecpk1O2 aOw/A/hRAySkFRnfVCl+uKONASoiz3a+5lRjt7+0wb/j68SnxjdIuP8RCSX8sfu+srVL pk+Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1739032864; x=1739637664; 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=EQ0kUaUS3PHiloOOyIcVRYupmf7yXzMkKyQKclEF5Cc=; b=vlBxKjVs32mEf688a9xgAHyY7SUOpZZh2XVwglD5csZv+/gmYSr6UfjlEiMBThnb4u Um4pmBelgG736qDvuDC7HU/2jrxEqzJ5B6B8gZdlWfYAS5d5BQi5BbdNjT/qytECkQd1 ig/f62qf5iEPDtAuINWiJ3sJ4o8pWwcbif26XYexVe6CDLW52TeacsgV/crBiVskdoeR Z0PXGnCEdxoeaAYC0JBzWoPXHI7Ef45WvWPxWQEH5kMXi4+KG0skjGrIWJ5EXYtdDQui /fuTIBQcbuwSMM/Jo0HJvUghQkNpRPaHGac32I8Ew0+lmZHk2uV4EcNav9tcsQzFaF85 Hz8g== X-Forwarded-Encrypted: i=1; AJvYcCV8iGXr1eD/G3UGWjYM4+SaeoDor/iLE4OHiCHatNBph61ZBThPeZuWfoBEjSCN6m9lwC+F658/kh/zU8g=@vger.kernel.org X-Gm-Message-State: AOJu0Yz4Z3st2HeC7ff4KuM+GfSOufXNnVWY08Gd1cgJhULLUv0iO2Xa hOEvj1qncHaGYiLiLtF0aOzs/EkkQakTFvcBSiH+cGDSsTW3cohLpAFaQg== X-Gm-Gg: ASbGncvSmHlOsqPFGgWP3sUleaiD+6TZYAecZ7fnLmK7htnC6SX4L5zGWcvpXixpXUE spCDlCqAorWiYcNTj1BRds3q31gickqasXmNDxne0pfFvA9pM2om9VMS36jitdFAXRqzxhMPlSQ 2P8ZaxUK1jKvImlL2guUgwX0PdxMtv5PywKKNNE1Fp1QHuq1mLuh+OmOIZKdvTMcsh5aVnJSQTZ dJlciEm+JlxiPskiUtb2QaZtY8UZHalXPyrs23oW0h2vQcSea8LOul/AIiLZaP0srM/aeQ1947h nJjezsutOSvuObw5IRfFJy3bl+xFOA== X-Google-Smtp-Source: AGHT+IH3ZAsVUX1yMnXQIjn21269frmjWJrHGDuPdi2Y+gSiSm5mHVCgP22wZqxnsVvBgmSguTQu7A== X-Received: by 2002:a17:907:9628:b0:ab3:76fb:96ab with SMTP id a640c23a62f3a-ab789c36549mr828938966b.57.1739032863917; Sat, 08 Feb 2025 08:41:03 -0800 (PST) Received: from localhost.localdomain ([2a02:908:e842:bf20:e115:64e7:5d6:176]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ab773337f3dsm501539166b.139.2025.02.08.08.41.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Feb 2025 08:41:03 -0800 (PST) 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 v7 07/11] kconfig: Add files with utility functions Date: Sat, 8 Feb 2025 17:39:55 +0100 Message-Id: <20250208163959.3973163-8-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250208163959.3973163-1-ole0811sch@gmail.com> References: <20250208163959.3973163-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 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 | 136 +++++ 2 files changed, 1116 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..1cbdcd8b7257 --- /dev/null +++ b/scripts/kconfig/cf_utils.h @@ -0,0 +1,136 @@ +/* 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" +#include "../include/list.h" + +/** + * cflist_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 cflist_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; \ + }) + + +/* 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 Sat Feb 8 16:39:56 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13966446 Received: from mail-ej1-f50.google.com (mail-ej1-f50.google.com [209.85.218.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 6C8741F3BBD; Sat, 8 Feb 2025 16:41:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739032885; cv=none; b=nE0ux4/l3EFNDTJkrlW4o1jc97A/X8py2QMADYeV2q6ATr9+YHGrrCRE/9KFV7yyzwej0TjziekKNX0Gin+Wh6ghasS5DCB15s9wGHXc23dV+09y3agnJ79Xa960Ny40ZHHXCILkeYkD7kr/CFU/KBWBJbIobqCWHLLb+UDgO4E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739032885; c=relaxed/simple; bh=jGrFWzTihzlAQh18ukiuzZlgaKLeUDANnBxJ4PM2B9I=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=qRk6lXOyOGEA85XQBl9gMx/1mJ3XlSR8jYMJewhy1DyVtATayjMz6Uo91KVjoVBZwXzAQdFFpCTgENPaP9GpzecOalKKjWRGiATsZu0uyhr2s64FHpOPTqrGftEYkZ+pY8CFVHiHe/PMJ5B5dVbEeCR0rfNGgy8Qqidv+sRWKu8= 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=QSUXmBcL; arc=none smtp.client-ip=209.85.218.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="QSUXmBcL" Received: by mail-ej1-f50.google.com with SMTP id a640c23a62f3a-ab78d9c5542so287089866b.1; Sat, 08 Feb 2025 08:41:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1739032882; x=1739637682; 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=eYonhZzT/PmMHatRrfMF0S0W7UXu2WTis1ZFw8zaUSg=; b=QSUXmBcLghK696UJJSx3PnxNQxIIWnnrYMTLUgguEMYmstasum0Nd1GKiwGs0xS/Oi rve+yUIpTGbslYA/VF4gliYT9BrP2d4OfftI3abJwt7ZkZw001iZTfijp/b1H3mcu2vC mTaU3nnWiMZ8MGSCw1jFa5c0Hl45GG6qkD8l1eT9JLIjdccntGZ+NZNVRY3AAZMEOiVO /iUeK5bclTxi8ERhPmxrM+5YROi+LiGtG+bVj80SL5zeyZbIqZCdhzDxqcSgAM1RPHJr tzKr5C+Ms65q9WwMvTmSSC2ck4rZKK1cgtyOvNMFISXMa6x5Ms9OxslnBf82WHzVQU1l VdKA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1739032882; x=1739637682; 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=eYonhZzT/PmMHatRrfMF0S0W7UXu2WTis1ZFw8zaUSg=; b=dORuHjAsuGmPEbXjJGKPXk7qwl0u0XrBsOTKOuKbSgQsFTMoXW61p/2lICiZ1ZnljR 4ajk013RIKG2vEY1t7TAzjncsoRz3ZTuVulHh93RLo+Tx1HHrxpOKvgOhR6mIYv3QNmB 9TL7+yR7GBeRwt8ECYhUGE86HhuootLVnYiFRWxnkaOC6HiuGt4LQ5ikO3H/9rnqJMXx Bnl7fG/piI6v0gkEvu3CfJsZq9Mgb4msWq9NjeszPg24TWIixLzd1GRLxJAGsgvD7iOd zyMVFAIJLwHmuEHmAFsGJQBb7PujhX7vtvYYbP4zdQ0CxNboJcnnAIc1Q8v4DSPRobbP /1Gg== X-Forwarded-Encrypted: i=1; AJvYcCVQGtszlhUvOwhyg8BNAOY29AvzpSlrFJBSzfkmkVMPd8jyc8aW8XQfdlhyBrgCVeUzXGkJ25JPJQKrpYg=@vger.kernel.org X-Gm-Message-State: AOJu0Yyrv2g666XQrRQQhUhFvUwg+pSsUCvRs8xE7Qqjgm7xheoo5dE6 IW3eH1wtVtzs6nHwhgzTMUU5cRviQ48O+mTIxNN8OcACz/EszhF4oqkNRQ== X-Gm-Gg: ASbGncuipELYkbNA79Y4cqz7hP0y/VgLaKjzr+7Rd5rFghP3hzTk4mtuOHD7DA+hEMW Vgr0z+k5SSCXn/3PuOS9o6b63O3HMttKJIuk4YotkRSsZ0aceDR6mLF1XqYABBSls+ZmLJvZS5N fSUJklSqjkMKLQDtouNPNKxQdkNSpPxAucChyYlHp9FYoOZQI7Z+dGs36mJ1II0uedelGgrMaLK W+iFYyjBUXGps5SXgZUiZE8dlknEY0CYLZlrx6RuWCAsKXcSeM8PvqBCxe49BGtHjHZFGDriPnV KVoUIqCt89mXlWa9llRUd+ERvkyqhg== X-Google-Smtp-Source: AGHT+IGQFXNGacQJzkbyUHREaPu8mgF21jupN9KjAT5MQydblH/SJ2MO5jFMaRYTec8t1Fx9tvpl/Q== X-Received: by 2002:a17:907:d1a:b0:aa6:3f93:fb99 with SMTP id a640c23a62f3a-ab789bf9658mr876457566b.36.1739032881348; Sat, 08 Feb 2025 08:41:21 -0800 (PST) Received: from localhost.localdomain ([2a02:908:e842:bf20:e115:64e7:5d6:176]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ab773337f3dsm501539166b.139.2025.02.08.08.41.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Feb 2025 08:41:20 -0800 (PST) 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 v7 08/11] kconfig: Add tools Date: Sat, 8 Feb 2025 17:39:56 +0100 Message-Id: <20250208163959.3973163-9-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250208163959.3973163-1-ole0811sch@gmail.com> References: <20250208163959.3973163-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 actual API to be used from a configurator as well as a tool to export the translation of the kconfig-model into propositional logic to 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 | 352 ++++++++++++++++++++++++++++++++++ scripts/kconfig/configfix.h | 31 +++ 4 files changed, 533 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..10e934979c8a --- /dev/null +++ b/scripts/kconfig/configfix.c @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Patrick Franz + * ConfigFix documentation and contributors: http://github.com/isselab/configfix + */ + +#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_fixgen.h" +#include "cf_defs.h" +#include "expr.h" +#include "list.h" +#include "lkc.h" + +bool CFDEBUG; +bool stop_fixgen; + +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_count_nodes(&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 = fixgen_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_count_nodes(&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 fix generation after the next iteration + */ +void interrupt_fix_generation(void) +{ + stop_fixgen = 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 cflist_at_index(index, &solutions->list, struct sfl_node, node) + ->elem; +} + +struct symbol_fix *select_symbol(struct sfix_list *solution, int index) +{ + return cflist_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..717f12ec9a2c --- /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_fix_generation(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 Sat Feb 8 16:39:57 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13966447 Received: from mail-ej1-f43.google.com (mail-ej1-f43.google.com [209.85.218.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4E25C244ACD; Sat, 8 Feb 2025 16:41:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739032891; cv=none; b=D+2xje6HGWTbYe278LqSNipgqNDjenXdqDZ3wbxbUIVV0xhCJTPlFqnc1HAnRZhWAsj1vOWPBqsVS2FJp7ceQaLBsRTg3o865KKLjC8bV0gb2zsKTwsmGnofCTgTGygmqboOJf/xrnhsdpsbJUwH6wgDUM3jKIfJzWJyrgB3sbo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739032891; c=relaxed/simple; bh=iaCDp15+0iKPhU89YLQbxtiVUsPrWKHjQPz7OiTHLqQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=dGYCsug23Lpt92j8vmbBrtCCtajTtRDpkyGMlS4oQR2D4GVZwTMsDWX1/FFavZUK+lZ0wHIslMTfcz5uMvtsyw3CzbmO6uL+sJZCRcc83Q/hN/RfS4cB5Ux6OiiIAQ0pl1YJDY1A4TA9J3/1JNf/a3NwO8pydI9w3aCp/HchuwI= 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=UBpGhgB2; arc=none smtp.client-ip=209.85.218.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="UBpGhgB2" Received: by mail-ej1-f43.google.com with SMTP id a640c23a62f3a-aaee2c5ee6eso504554366b.1; Sat, 08 Feb 2025 08:41:28 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1739032887; x=1739637687; 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=PBp9yKqAQZTAYYlZpZ0Y5IV0ui2vSt5N/xUMNVZgTOU=; b=UBpGhgB2EbeYP7qyPGmoT50SCTEJuykvelSIam7KwPyi13TLJp+02D9G3MBNzF8prx fTeEcq/wlRwqccyioZHQQDm6fOfJG/aTbVeQwvE785noW2cIrsD77LlQHnR/8vGyjRmE D5XOnHd2IVQsLjwh2YnjsmGMu6WOU5mfCuARtU1K3EGqYsbAlw3d2vYPkQNIDdIcFiv9 4JQUegXNAz/rWQnhbwBkzycd66cFOKmQpOOy0Cu2PsWpMMfBryy1KZviDiGJzJ7Uq0JI wQzGbYFlADpNBPRWxX9UCwMr6HxVeOlAVE9QMC14Rgm2dawuaoOqsBdS0TN/jMIZ2KiZ uZYA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1739032887; x=1739637687; 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=PBp9yKqAQZTAYYlZpZ0Y5IV0ui2vSt5N/xUMNVZgTOU=; b=Z6/HdBN8YbShCKZDNAGpWsED2QQHgZ2JTb9cnRqL/uX9POKOpWmiFvc8lnhm2Qm/J1 hBzKOgqj562ywH9ev+qeoxVZlAX/w4wNaRDXE0NFgrOwjKTHUKxbqNqlqLlTdSbqGZVC gPER6zZMfNhrbG+bSPwU0jmI6dfa+4CXXgR2oCq1GGTjBkpQhnfhyj9Rp1zkDUeu4vXz UOW9XTyGLRZdnzf4tnKjjmbIYSbkp1qIPkO5Msqu0WfRuTAwAKIbEn+PKDNL3y3CZzp/ FEIsssCCbrWR5nMHG13BxFKQeaT+Zh+q0ZZar6dAs0yGu/wmj1hghUlQzkUVCgHWMpet aG3Q== X-Forwarded-Encrypted: i=1; AJvYcCXWginiHBZbGn7NhjjrfbTSUJpd+M8VmPtBwKuwwE9pLjUM4q83x5Di6IJJ+AuofuGY9OeQ5aVV3iPYWYo=@vger.kernel.org X-Gm-Message-State: AOJu0Ywx32U35e0oBowl2hiwfljL/Dfx/YKZwatG2+9Q3HTwWla+JXVt RVmKHxeL2ReDCdcPTWzKOp0kNFjhmJkVKIS2JYw1DS30wiwMBUGTcBsMhg== X-Gm-Gg: ASbGncthXlS4RAjGF3nORIZ6Vy4xe5FcI+6bQ0ybsz2r58JBUMiGCKL7dHY4xf+gDGd myXzvhLSTR0uHpbYTMNpgAVb3W885vTvdZ0fJXeEu4kZLSRGxnEfmqqiflywJZmV/U+nC3xYd2h kYsNIQw+HywlQB9lwnnJalzIDZlQSGOpdypRIMPB2dVyiPCw6FvkXvYeeWi5p4CUEgm0LplQCYG 7k40cHuUO4e86hpP9x4v7HkcEMY8hv91U9n4BLC7yOU0P0b84S8Fnb45yyELaUGafJpB6bOBABm w0ObwZvIykeAIbdle+ppimg/MPf2Rw== X-Google-Smtp-Source: AGHT+IHYvKuJWYwD1QPeGoTSTNEY+neDM9v4I8/J55nVLAXybCUr+9EPGvnrR7g+oIxlSfJx4A5luQ== X-Received: by 2002:a17:907:9726:b0:aa6:af66:7c89 with SMTP id a640c23a62f3a-ab789a6b4d8mr922573666b.5.1739032886277; Sat, 08 Feb 2025 08:41:26 -0800 (PST) Received: from localhost.localdomain ([2a02:908:e842:bf20:e115:64e7:5d6:176]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ab773337f3dsm501539166b.139.2025.02.08.08.41.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Feb 2025 08:41:25 -0800 (PST) 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 v7 09/11] kconfig: Add xconfig-modifications Date: Sat, 8 Feb 2025 17:39:57 +0100 Message-Id: <20250208163959.3973163-10-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250208163959.3973163-1-ole0811sch@gmail.com> References: <20250208163959.3973163-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 modifications to xconfig to integrate the conflict resolver. Co-developed-by: Patrick Franz Signed-off-by: Patrick Franz Co-developed-by: Ibrahim Fayaz Signed-off-by: Ibrahim Fayaz Reviewed-by: Luis Chamberlain Tested-by: Evgeny Groshev Suggested-by: Sarah Nadi Suggested-by: Thorsten Berger Signed-off-by: Thorsten Berger Signed-off-by: Ole Schuerks --- scripts/kconfig/qconf.cc | 676 ++++++++++++++++++++++++++++++++++++++- scripts/kconfig/qconf.h | 111 +++++++ 2 files changed, 785 insertions(+), 2 deletions(-) diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index eaa465b0ccf9..d798177a8c26 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") { @@ -404,7 +423,7 @@ void ConfigList::updateSelection(void) ConfigItem* item = (ConfigItem*)selectedItems().first(); if (!item) return; - + emit selectionChanged(selectedItems()); menu = item->menu; emit menuChanged(menu); if (!menu) @@ -537,6 +556,7 @@ void ConfigList::changeValue(ConfigItem* item) } if (oldexpr != newexpr) ConfigList::updateListForAll(); + emit updateConflictsViewColorization(); break; default: break; @@ -878,6 +898,7 @@ void ConfigList::contextMenuEvent(QContextMenuEvent *e) action, &QAction::setChecked); action->setChecked(showName); headerPopup->addAction(action); + headerPopup->addAction(addSymbolFromContextMenu); } headerPopup->exec(e->globalPos()); @@ -898,6 +919,7 @@ QList ConfigList::allLists; QAction *ConfigList::showNormalAction; QAction *ConfigList::showAllAction; QAction *ConfigList::showPromptAction; +QAction *ConfigList::addSymbolFromContextMenu; void ConfigList::setAllOpen(bool open) { @@ -1187,7 +1209,11 @@ ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow *parent) info, &ConfigInfoView::setInfo); connect(list, &ConfigList::menuChanged, parent, &ConfigMainWindow::setMenuLink); + connect(list, &ConfigList::menuChanged, + parent, &ConfigMainWindow::conflictSelected); + connect(list, &ConfigList::updateConflictsViewColorization, this, + &ConfigSearchWindow::updateConflictsViewColorizationFowarder); layout1->addWidget(split); QVariant x, y; @@ -1210,6 +1236,11 @@ ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow *parent) this, &ConfigSearchWindow::saveSettings); } +void ConfigSearchWindow::updateConflictsViewColorizationFowarder(void) +{ + emit updateConflictsViewColorization(); +} + void ConfigSearchWindow::saveSettings(void) { if (!objectName().isEmpty()) { @@ -1296,6 +1327,24 @@ ConfigMainWindow::ConfigMainWindow(void) helpText = new ConfigInfoView(split2, "help"); setTabOrder(configList, helpText); + split3 = new QSplitter(split2); + split3->setOrientation(Qt::Vertical); + conflictsView = new ConflictsView(split3, "help"); + /* + * conflictsSelected signal in conflictsview triggers when a conflict is + * selected in the view. this line connects that event to conflictselected + * event in mainwindow which updates the selection to match (in the + * configlist) the symbol that was selected. + */ + connect(conflictsView, &ConflictsView::conflictSelected, this, + &ConfigMainWindow::conflictSelected); + connect(conflictsView, &ConflictsView::refreshMenu, this, + &ConfigMainWindow::refreshMenu); + connect(menuList, &ConfigList::updateConflictsViewColorization, + conflictsView, &ConflictsView::updateConflictsViewColorization); + connect(configList, &ConfigList::updateConflictsViewColorization, + conflictsView, &ConflictsView::updateConflictsViewColorization); + configList->setFocus(); backAction = new QAction(QPixmap(xpm_back), "Back", this); @@ -1362,6 +1411,10 @@ ConfigMainWindow::ConfigMainWindow(void) ConfigList::showAllAction->setCheckable(true); ConfigList::showPromptAction = new QAction("Show Prompt Options", optGroup); ConfigList::showPromptAction->setCheckable(true); + ConfigList::addSymbolFromContextMenu = + new QAction("Add symbol from context menu"); + connect(ConfigList::addSymbolFromContextMenu, &QAction::triggered, + conflictsView, &ConflictsView::addSymbol); QAction *showDebugAction = new QAction("Show Debug Info", this); showDebugAction->setCheckable(true); @@ -1417,6 +1470,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, @@ -1425,6 +1480,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); @@ -1634,6 +1691,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); @@ -1769,6 +1833,536 @@ void ConfigMainWindow::conf_changed(bool dirty) saveAction->setEnabled(dirty); } +void ConfigMainWindow::refreshMenu(void) +{ + configList->updateListAll(); +} + +void QTableWidget::dropEvent(QDropEvent *event) +{ +} + +void ConflictsView::addPicoSatNote(QToolBar &toolbar) +{ + QLabel &label = *new QLabel; + auto &iconLabel = *new QLabel(); + iconLabel.setPixmap( + style()->standardIcon( + QStyle::StandardPixmap::SP_MessageBoxInformation) + .pixmap(20, 20)); + label.setText( + "The conflict resolver requires that PicoSAT is available as a library."); + QAction &showDialog = *new QAction(); + showDialog.setIconText("Install PicoSAT..."); + toolbar.addWidget(&iconLabel); + toolbar.addWidget(&label); + toolbar.addAction(&showDialog); + connect(&showDialog, &QAction::triggered, + [this]() { (new PicoSATInstallInfoWindow(this))->show(); }); +} + +ConflictsView::ConflictsView(QWidget *parent, const char *name) + : Parent(parent) +{ + /* + * - topLevelLayout + * - picoSatContainer + * - picoSatLayout + * - ... + * - conflictsViewContainer + * - horizontalLayout + * - verticalLayout + * - solutionLayout + */ + currentSelectedMenu = nullptr; + setObjectName(name); + QVBoxLayout *topLevelLayout = new QVBoxLayout(this); + QWidget *conflictsViewContainer = new QWidget; + if (!picosat_available) { + conflictsViewContainer->setDisabled(true); + QWidget *picoSatContainer = new QWidget; + topLevelLayout->addWidget(picoSatContainer); + QHBoxLayout *picoSatLayout = new QHBoxLayout(picoSatContainer); + QToolBar *picoToolbar = new QToolBar(picoSatContainer); + picoSatLayout->addWidget(picoToolbar); + picoSatLayout->addStretch(); + addPicoSatNote(*picoToolbar); + } + topLevelLayout->addWidget(conflictsViewContainer); + + QHBoxLayout *horizontalLayout = new QHBoxLayout(conflictsViewContainer); + QVBoxLayout *verticalLayout = new QVBoxLayout; + verticalLayout->setContentsMargins(0, 0, 0, 0); + conflictsToolBar = + new QToolBar("ConflictTools", conflictsViewContainer); + // toolbar buttons [n] [m] [y] [calculate fixes] [remove] + QAction *addSymbol = new QAction("Add Symbol"); + QAction *setConfigSymbolAsNo = new QAction("N"); + QAction *setConfigSymbolAsModule = new QAction("M"); + QAction *setConfigSymbolAsYes = new QAction("Y"); + fixConflictsAction_ = new QAction("Calculate Fixes"); + QAction *removeSymbol = new QAction("Remove Symbol"); + QMovie *loadingGif = new QMovie("scripts/kconfig/loader.gif"); + auto loadingLabel = new QLabel; + + if (loadingGif->isValid()) { + loadingGif->setScaledSize(loadingGif->scaledSize().scaled( + 20, 20, Qt::KeepAspectRatioByExpanding)); + loadingGif->start(); + loadingLabel->setMovie(loadingGif); + } else { + loadingLabel->setText("Calculating..."); + } + + //if you change the order of buttons here, change the code where + //module button was disabled if symbol is boolean, selecting module button + //depends on a specific index in list of action + fixConflictsAction_->setCheckable(false); + conflictsToolBar->addAction(addSymbol); + conflictsToolBar->addAction(setConfigSymbolAsNo); + conflictsToolBar->addAction(setConfigSymbolAsModule); + conflictsToolBar->addAction(setConfigSymbolAsYes); + conflictsToolBar->addAction(fixConflictsAction_); + conflictsToolBar->addAction(removeSymbol); + // loadingLabel->setMargin(5); + loadingLabel->setContentsMargins(5, 5, 5, 5); + loadingAction = conflictsToolBar->addWidget(loadingLabel); + loadingAction->setVisible(false); + + verticalLayout->addWidget(conflictsToolBar); + + connect(addSymbol, &QAction::triggered, this, + &ConflictsView::addSymbol); + connect(setConfigSymbolAsNo, &QAction::triggered, this, + &ConflictsView::changeToNo); + connect(setConfigSymbolAsModule, &QAction::triggered, this, + &ConflictsView::changeToModule); + connect(setConfigSymbolAsYes, &QAction::triggered, this, + &ConflictsView::changeToYes); + connect(removeSymbol, &QAction::triggered, this, + &ConflictsView::removeSymbol); + connect(this, &ConflictsView::resultsReady, this, + &ConflictsView::updateResults); + //connect clicking 'calculate fixes' to 'change all symbol values to fix all conflicts' + // no longer used anymore for now. + connect(fixConflictsAction_, &QAction::triggered, this, + &ConflictsView::calculateFixes); + + conflictsTable = (QTableWidget *)new dropAbleView(this); + conflictsTable->setRowCount(0); + conflictsTable->setColumnCount(3); + conflictsTable->setSelectionBehavior(QAbstractItemView::SelectRows); + conflictsTable->setEditTriggers(QAbstractItemView::NoEditTriggers); + + conflictsTable->setHorizontalHeaderLabels( + QStringList() << "Name" << "Wanted value" << "Current value"); + verticalLayout->addWidget(conflictsTable); + + conflictsTable->setDragDropMode(QAbstractItemView::DropOnly); + setAcceptDrops(true); + + connect(conflictsTable, &QTableWidget::cellClicked, this, + &ConflictsView::cellClicked); + horizontalLayout->addLayout(verticalLayout); + + // populate the solution view on the right hand side: + QVBoxLayout *solutionLayout = new QVBoxLayout(); + solutionLayout->setContentsMargins(0, 0, 0, 0); + solutionSelector = new QComboBox(); + connect(solutionSelector, + QOverload::of(&QComboBox::currentIndexChanged), + [=](int index) { changeSolutionTable(index); }); + solutionTable = new QTableWidget(); + solutionTable->setRowCount(0); + solutionTable->setColumnCount(2); + solutionTable->setHorizontalHeaderLabels(QStringList() + << "Name" << "New Value"); + solutionTable->setEditTriggers(QAbstractItemView::NoEditTriggers); + + applyFixButton = new QPushButton("Apply Selected solution"); + connect(applyFixButton, &QPushButton::clicked, this, + &ConflictsView::applyFixButtonClick); + + numSolutionLabel = new QLabel("Solutions:"); + solutionLayout->addWidget(numSolutionLabel); + solutionLayout->addWidget(solutionSelector); + solutionLayout->addWidget(solutionTable); + solutionLayout->addWidget(applyFixButton); + + horizontalLayout->addLayout(solutionLayout); +} + +void ConflictsView::applyFixButtonClick() +{ + signed int solution_number = solutionSelector->currentIndex(); + + if (solution_number == -1 || solution_output == NULL) { + return; + } + + apply_fix(solution_output[solution_number]); + + ConfigList::updateListForAll(); + for (int i = 0; i < conflictsTable->rowCount(); i++) { + conflictsTable->item(i, 2)->setText( + conflictsTable->item(i, 1)->text()); + } + updateConflictsViewColorization(); + QMessageBox msgBox; + msgBox.setText("The solution has been applied."); + msgBox.exec(); +} + +void ConflictsView::changeToYes() +{ + QItemSelectionModel *select = conflictsTable->selectionModel(); + if (select->hasSelection()) { + QModelIndexList rows = select->selectedRows(); + for (int i = 0; i < rows.count(); i++) { + conflictsTable->item(rows[i].row(), 1) + ->setText(tristate_value_to_string(yes)); + } + } +} + +void ConflictsView::changeToModule() +{ + QItemSelectionModel *select = conflictsTable->selectionModel(); + if (select->hasSelection()) { + QModelIndexList rows = select->selectedRows(); + for (int i = 0; i < rows.count(); i++) { + conflictsTable->item(rows[i].row(), 1) + ->setText(tristate_value_to_string(mod)); + } + } +} + +void ConflictsView::changeToNo() +{ + QItemSelectionModel *select = conflictsTable->selectionModel(); + if (select->hasSelection()) { + QModelIndexList rows = select->selectedRows(); + for (int i = 0; i < rows.count(); i++) { + conflictsTable->item(rows[i].row(), 1) + ->setText(tristate_value_to_string(no)); + } + } +} + +void ConflictsView::menuChanged(struct menu *m) +{ + currentSelectedMenu = m; +} + +void ConflictsView::addSymbol() +{ + addSymbolFromMenu(currentSelectedMenu); +} + +void ConflictsView::selectionChanged(QList selection) +{ + currentSelection = selection; +} + +void ConflictsView::addSymbolFromMenu(struct menu *m) +{ + // adds a symbol to the conflict resolver list + if (m != nullptr) { + if (m->sym != nullptr) { + struct symbol *sym = m->sym; + tristate currentval = sym_get_tristate_value(sym); + //if symbol is not added yet: + QAbstractItemModel *tableModel = + conflictsTable->model(); + QModelIndexList matches = tableModel->match( + tableModel->index(0, 0), Qt::DisplayRole, + QString(sym->name)); + if (matches.isEmpty()) { + conflictsTable->insertRow( + conflictsTable->rowCount()); + conflictsTable->setItem( + conflictsTable->rowCount() - 1, 0, + new QTableWidgetItem(sym->name)); + conflictsTable->setItem( + conflictsTable->rowCount() - 1, 1, + new QTableWidgetItem( + tristate_value_to_string( + currentval))); + conflictsTable->setItem( + conflictsTable->rowCount() - 1, 2, + new QTableWidgetItem( + tristate_value_to_string( + currentval))); + } else { + conflictsTable->item(matches[0].row(), 2) + ->setText(tristate_value_to_string( + currentval)); + } + } + } +} + +void ConflictsView::addSymbolFromContextMenu() +{ + struct menu *menu; + + if (currentSelection.count() < 0) { + return; + } + for (auto el : currentSelection) { + ConfigItem *item = (ConfigItem *)el; + if (!item) { + continue; + } + menu = item->menu; + addSymbolFromMenu(menu); + } +} + +void ConflictsView::removeSymbol() +{ + QItemSelectionModel *select = conflictsTable->selectionModel(); + QAbstractItemModel *itemModel = select->model(); + if (select->hasSelection()) { + QModelIndexList rows = select->selectedRows(); + itemModel->removeRows(rows[0].row(), rows.size()); + } +} + +void ConflictsView::cellClicked(int row, int column) +{ + auto itemText = conflictsTable->item(row, 0)->text().toUtf8().data(); + struct property *prop; + struct menu *men; + struct symbol *sym = sym_find(itemText); + + if (sym == NULL) + return; + prop = sym->prop; + men = prop->menu; + // uncommenting following like somehow disables click signal of 'apply selected solution' + if (sym->type == symbol_type::S_BOOLEAN) { + //disable module button + conflictsToolBar->actions()[2]->setDisabled(true); + } else { + //enable module button + conflictsToolBar->actions()[2]->setDisabled(false); + } + if (column == 1) { + // cycle to new value + tristate old_val = string_value_to_tristate( + conflictsTable->item(row, 1)->text()); + tristate new_val = old_val; + switch (old_val) { + case no: + new_val = mod; + break; + case mod: + new_val = yes; + break; + case yes: + new_val = no; + break; + } + if (sym->type == S_BOOLEAN && new_val == mod) + new_val = yes; + conflictsTable->item(row, 1)->setText( + tristate_value_to_string(new_val)); + } + emit(conflictSelected(men)); +} + +void ConflictsView::changeSolutionTable(int solution_number) +{ + size_t i; + + if (solution_output == nullptr || solution_number < 0) { + return; + } + struct sfix_list *selected_solution = solution_output[solution_number]; + current_solution_number = solution_number; + solutionTable->setRowCount(0); + i = 0; + for (struct list_head *curr = selected_solution->list.next; + curr != &selected_solution->list; curr = curr->next, ++i) { + solutionTable->insertRow(solutionTable->rowCount()); + struct symbol_fix *cur_symbol = + select_symbol(selected_solution, i); + + QTableWidgetItem *symbol_name = + new QTableWidgetItem(cur_symbol->sym->name); + + solutionTable->setItem(solutionTable->rowCount() - 1, 0, + symbol_name); + + if (cur_symbol->type == symbolfix_type::SF_BOOLEAN) { + QTableWidgetItem *symbol_value = new QTableWidgetItem( + tristate_value_to_string(cur_symbol->tri)); + solutionTable->setItem(solutionTable->rowCount() - 1, 1, + symbol_value); + } else if (cur_symbol->type == symbolfix_type::SF_NONBOOLEAN) { + QTableWidgetItem *symbol_value = + new QTableWidgetItem(cur_symbol->nb_val.s); + solutionTable->setItem(solutionTable->rowCount() - 1, 1, + symbol_value); + } else { + QTableWidgetItem *symbol_value = + new QTableWidgetItem(cur_symbol->disallowed.s); + solutionTable->setItem(solutionTable->rowCount() - 1, 1, + symbol_value); + } + } + updateConflictsViewColorization(); +} + +void ConflictsView::updateConflictsViewColorization(void) +{ + auto green = QColor(0, 170, 0); + auto red = QColor(255, 0, 0); + auto grey = QColor(180, 180, 180); + + if (solutionTable->rowCount() == 0 || current_solution_number < 0) + return; + + for (int i = 0; i < solutionTable->rowCount(); i++) { + QTableWidgetItem *symbol = solutionTable->item(i, 0); + //symbol from solution list + struct symbol_fix *cur_symbol = select_symbol( + solution_output[current_solution_number], i); + + // if symbol is editable but the value is not the target value from solution we got, the color is red + // if symbol is editable but the value is the target value from solution we got, the color is green + // if symbol is not editable , the value is not the target value, the color is grey + // if symbol is not editable , the value is the target value, the color is green + auto editable = sym_string_within_range( + cur_symbol->sym, + tristate_value_to_string(cur_symbol->tri) + .toStdString() + .c_str()); + auto _symbol = + solutionTable->item(i, 0)->text().toUtf8().data(); + struct symbol *sym_ = sym_find(_symbol); + + tristate current_value_of_symbol = sym_get_tristate_value(sym_); + tristate target_value_of_symbol = string_value_to_tristate( + solutionTable->item(i, 1)->text()); + bool symbol_value_same_as_target = current_value_of_symbol == + target_value_of_symbol; + + if (editable && !symbol_value_same_as_target) { + symbol->setForeground(red); + } else if (editable && symbol_value_same_as_target) { + symbol->setForeground(green); + } else if (!editable && !symbol_value_same_as_target) { + symbol->setForeground(grey); + } else if (!editable && symbol_value_same_as_target) { + symbol->setForeground(green); + } + } +} + +void ConflictsView::runSatConfAsync() +{ + //loop through the rows in conflicts table adding each row into the array: + struct symbol_dvalue *p = nullptr; + std::vector wanted_symbols; + + p = static_cast(calloc( + conflictsTable->rowCount(), sizeof(struct symbol_dvalue))); + if (!p) { + printf("memory allocation error\n"); + return; + } + + for (int i = 0; i < conflictsTable->rowCount(); i++) { + struct symbol_dvalue *tmp = (p + i); + auto _symbol = + conflictsTable->item(i, 0)->text().toUtf8().data(); + struct symbol *sym = sym_find(_symbol); + + tmp->sym = sym; + tmp->type = static_cast( + sym->type == symbol_type::S_BOOLEAN ? 0 : 1); + tmp->tri = string_value_to_tristate( + conflictsTable->item(i, 1)->text()); + wanted_symbols.push_back(tmp); + } + fixConflictsAction_->setText("Cancel"); + loadingAction->setVisible(true); + + solution_output = run_satconf(wanted_symbols.data(), + wanted_symbols.size(), &num_solutions); + + free(p); + emit resultsReady(); + { + std::lock_guard lk{ satconf_mutex }; + satconf_cancelled = true; + } + satconf_cancellation_cv.notify_one(); +} + +void ConflictsView::updateResults(void) +{ + fixConflictsAction_->setText("Calculate Fixes"); + loadingAction->setVisible(false); + if (!(solution_output == nullptr || num_solutions == 0)) { + solutionSelector->clear(); + for (unsigned int i = 0; i < num_solutions; i++) + solutionSelector->addItem(QString::number(i + 1)); + // populate the solution table from the first solution gotten + numSolutionLabel->setText( + QString("Solutions: (%1) found").arg(num_solutions)); + changeSolutionTable(0); + } else { + QMessageBox msgBox; + msgBox.setText("All symbols are already within range."); + msgBox.exec(); + } + if (runSatConfAsyncThread->joinable()) { + runSatConfAsyncThread->join(); + delete runSatConfAsyncThread; + runSatConfAsyncThread = nullptr; + } +} + +void ConflictsView::calculateFixes() +{ + if (conflictsTable->rowCount() == 0) { + printd("table is empty\n"); + return; + } + + if (runSatConfAsyncThread == nullptr) { + // fire away asynchronous call + std::unique_lock lk{ satconf_mutex }; + + numSolutionLabel->setText(QString("Solutions: ")); + solutionSelector->clear(); + solutionTable->setRowCount(0); + satconf_cancelled = false; + runSatConfAsyncThread = + new std::thread(&ConflictsView::runSatConfAsync, this); + } else { + printd("Interrupting fix generation\n"); + interrupt_fix_generation(); + 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; @@ -1818,6 +2412,8 @@ int main(int ac, char** av) fixup_rootmenu(&rootmenu); //zconfdump(stdout); + picosat_available = load_picosat(); + configApp = new QApplication(ac, av); configSettings = new ConfigSettings(); @@ -1836,3 +2432,79 @@ int main(int ac, char** av) return 0; } + +dropAbleView::dropAbleView(QWidget *parent) + : QTableWidget(parent) +{ +} + +dropAbleView::~dropAbleView() +{ +} +void dropAbleView::dropEvent(QDropEvent *event) +{ + event->acceptProposedAction(); +} + +static QString tristate_value_to_string(tristate val) +{ + switch (val) { + case yes: + return QString::fromStdString("Y"); + case mod: + return QString::fromStdString("M"); + case no: + return QString::fromStdString("N"); + default: + return QString::fromStdString(""); + } +} + +static tristate string_value_to_tristate(QString s) +{ + if (s == "Y") + return tristate::yes; + else if (s == "M") + return tristate::mod; + else if (s == "N") + return tristate::no; + else + return tristate::no; +} + +PicoSATInstallInfoWindow::PicoSATInstallInfoWindow(QWidget *parent) + : QDialog(parent) +{ + QVBoxLayout &layout = *new QVBoxLayout(this); + QLabel &text = *new QLabel(); + text.setWordWrap(true); + layout.addWidget(&text); + text.setTextFormat(Qt::MarkdownText); + text.setTextInteractionFlags(Qt::TextSelectableByMouse); + text.setTextInteractionFlags(Qt::TextBrowserInteraction); + text.setOpenExternalLinks(true); + text.setText(R""""( +Install the picosat package or build the library yourself: + +## Debian-based distributions + +```sh +sudo apt install picosat +``` + +## Fedora + +```sh +sudo dnf install picosat +``` + +## Other + +You can also build PicoSAT yourself from the +[sources](https://fmv.jku.at/picosat/picosat-965.tar.gz). You need to compile +PicoSAT with tracing enabled as a shared library under the name of +"libpicosat-trace.so", "libpicosat-trace.so.0" or "libpicosat-trace.so.1". +Tracing can be enabled using the `configure.sh` script with the `--trace` +option. + )""""); +} diff --git a/scripts/kconfig/qconf.h b/scripts/kconfig/qconf.h index 62ab3286d04f..b305574550ee 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; @@ -78,6 +87,8 @@ public slots: void parentSelected(void); void gotFocus(struct menu *); void showNameChanged(bool on); + void selectionChanged(QList selection); + void updateConflictsViewColorization(); public: void updateListAll(void) @@ -109,6 +120,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 { @@ -211,6 +298,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; @@ -220,6 +313,9 @@ class ConfigSearchWindow : public QDialog { public slots: void saveSettings(void); void search(void); + void updateConflictsViewColorizationFowarder(); +signals: + void updateConflictsViewColorization(); protected: QLineEdit* editField; @@ -255,6 +351,8 @@ public slots: void showIntro(void); void showAbout(void); void saveSettings(void); + void conflictSelected(struct menu *); + void refreshMenu(); protected: void closeEvent(QCloseEvent *e); @@ -263,10 +361,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 Sat Feb 8 16:39:58 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13966448 Received: from mail-ej1-f50.google.com (mail-ej1-f50.google.com [209.85.218.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 94F43244ADF; Sat, 8 Feb 2025 16:41:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739032894; cv=none; b=FWIakXCWPmeUBm01pK7ul6UgPrDTs+jBF8Lvy2zgs50jg3M+vcqnBmBV67FQfXliPI/j+rH2jH+q8RCc3hlOPKaRg6nLczYgmOE4WewIG34B5hwaJwnwzotwIlEitdoDusFwQNL44Bsr6LjD46+z2XqTPYl3O9usU0Ll5ehZTnU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739032894; c=relaxed/simple; bh=S/2EAtWp6JPLCvIE0+ZuLlyNzRcL0LTLF2JmlRHtFNQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=gZInekpgHf4A7nX3qedZaXZOh7SVOIXL9FCgG4YHNSdeLTNeDIIK0qnj9kJuvRWjUrOdJuehbiYCoYymP6GrsKEZ0kf9It/WzV0wuOrisvwibIxOKIsrhh7hq39cDNaEmcnKJXOQUhRoPozvSU22ZtlqGAd8grmgeODwTTer4Fk= 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=SgYSYej4; arc=none smtp.client-ip=209.85.218.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="SgYSYej4" Received: by mail-ej1-f50.google.com with SMTP id a640c23a62f3a-ab794ae3cf2so147289666b.2; Sat, 08 Feb 2025 08:41:32 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1739032891; x=1739637691; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=FQwdxRW+PlqWzgd2iVA2/71Jy8/QEccppk3FUkvYKjs=; b=SgYSYej4Jesh6mqgmz+5jeBWXjyVGIEoZ6yLEZzKIsbiWRtKPaIzk6JC2aLET7PtLQ A1nep6dMNRbu9AhIdP2nzrM9HESdSxkNzQPI4E2a8dI/+sgP2Ier+Enyb7Cp2ub5zrIt Jjkgvsuc4BzLMCxnMyTZG0OnzMvo9FEGNX94Rztc6EQUUIhw2b9V9zB6d3fO0c6ZeHf8 VnqgNHUXRfNYrnBhFJitArgES/Iac9sigpkZacbprGkERZeelqkKFlksrmvi23aD1KaB rDoxRFSWXLKA/DTJwaE++0lxaaR/dnHxj18ciYVXMHgxCYi8CKkNOnhxvhpELJS2aB3/ UGhg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1739032891; x=1739637691; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=FQwdxRW+PlqWzgd2iVA2/71Jy8/QEccppk3FUkvYKjs=; b=H7Y6e7hVUyTabnDpuWV6e8okQti03cLUSN8X16I/e0MZn+PSd2fltNqT+fvQMYuIGs 1+WkJLEc4SexKK0zTadndtnswm6lS8g1wJYaTMN43vCQucyIeLQOGy/AnnRKcAg6NW39 w7iyktVAGsQee6Siq20vtjkBPvaEfwThu4HbDMvxozN7/FzwwfqGEQhQ/ainLON4A4F5 Ojk/ubPK9ep8AmsT0g/xm4pDf40twQVQZsgGK+eRSQLvjqhrIzgU5B6epr4tRYV3/MrU Qka3JtN9B18SmW36JYc+t+3S1tI+o7L+lFiMFUVuSOyDw6A3pajtr0uYSfCt4z2bxbiX GvTw== X-Forwarded-Encrypted: i=1; AJvYcCWmrT//H4Xkd44JHczvpDhEWcprN27DIeItcKV6yyhHI7wkupXJNew9EsabbF0hCI4E8sN87TB8QQAEmLI=@vger.kernel.org X-Gm-Message-State: AOJu0Yw3mx+JtDwS0G58hqfgdcDwq1Hfgj7JH72qRdKNHgeT0o2wLUMg xD/7IRVGw4AhPOXKdxYCfEIiwKYMno9TzQiMjgPzvDtQAa+wjVoklDObuA== X-Gm-Gg: ASbGncsHYJS5ZufBTMJ35yqgf6qilsquHuHXc3OBUHqs6KvCWpZ7ap/y3Wm/VQKXUgr b6qrpBwtYNDB2aQOfskbLI3EI92w3zmhWWTJ6awKAmE1dVpWIFx/CEOOfH9/GXHLrl8sCbpgmqO WBGQ9+/m3bfzARpELJEY27RGUfcL3VEgO5OklDLFqff8V0KUcxyKLnM+fzEIrB1NT8Nlk7ElAbi RAeU912MQBYpu1FBVlPTrpWxJk9afcM2ASaIU/H93+jHNtOg/BBmRHSqKzp7AS4qJr67Iqixbrf P2X9pX7qKdddtsqnFT38FJ/K4WauVQ== X-Google-Smtp-Source: AGHT+IFUI9C+HEb6LHYERhuHBiIkskoP/oQNqw8dL11KmTdmFcq3J4PcDwYyNrizsj3s51UNF6x6Tg== X-Received: by 2002:a17:907:3f90:b0:aa6:5201:7ae3 with SMTP id a640c23a62f3a-ab789bfc955mr786185566b.40.1739032890587; Sat, 08 Feb 2025 08:41:30 -0800 (PST) Received: from localhost.localdomain ([2a02:908:e842:bf20:e115:64e7:5d6:176]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ab773337f3dsm501539166b.139.2025.02.08.08.41.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Feb 2025 08:41:30 -0800 (PST) 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 v7 10/11] kconfig: Add loader.gif Date: Sat, 8 Feb 2025 17:39:58 +0100 Message-Id: <20250208163959.3973163-11-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250208163959.3973163-1-ole0811sch@gmail.com> References: <20250208163959.3973163-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 Sat Feb 8 16:39:59 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ole Schuerks X-Patchwork-Id: 13966449 Received: from mail-ej1-f50.google.com (mail-ej1-f50.google.com [209.85.218.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 7F45D244AFE; Sat, 8 Feb 2025 16:41:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739032899; cv=none; b=R2Qjza/aRSSHrgXextK3YrGWvOrr4YMJ5mp5YVW+811ZU/lu2WZbVKoYJe+neN6OuRkiwjayqk2Bb38YEf/k1pbS6w7MelpteLVg3wEYWkfQh6l/e4+u/Q0pP0hbxbtwJRO+lidjCkqQpO1vEqlQwkwY+IrDLISacFchb7nQpqM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739032899; c=relaxed/simple; bh=xGwi4psJJOZC8oqFMX4FH7oNE+Fjw6HTeW5J9RMFR3c=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=trlpi29d81PhFx7eKccJ6c5QUUvxSKNYZHsGhafK3n8quddyCFDKD84rpXu8Xrby9iAdDA3syRq7bSUoiIqfZt8I+l2MTJmARtyh5/rmuPLSZcGL2tgb54SwHdWdZLfi9Z/+914Cu4WkcPXsxeML3f5xCvtzcw0d12ygzcMfrTs= 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=NPtRdjJn; arc=none smtp.client-ip=209.85.218.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="NPtRdjJn" Received: by mail-ej1-f50.google.com with SMTP id a640c23a62f3a-ab795eba976so197538666b.0; Sat, 08 Feb 2025 08:41:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1739032896; x=1739637696; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=kGngZIwfO/jqpHla3vwHAdrO/yliWLs6xLpeLI2Xezk=; b=NPtRdjJnB6FjK4eD918bzHRBEdXPsNZ3HtYbfujqesbwh//se6nm/CVMoTyJy3qAnM rmEAnHgqdr1Ipv8yGJLoyTKIQqjV3DKZeBBFrXRGuTjEsoAv0JPZ5GHVF3RsZTkQrAWB V8rzMAyQhR6UVI2XtKadsjq3xqDpINTMK9QsAj2wHC6P2xUOP9llP+iBkGCFoIEsdDf6 LY1oslx6gXYFHZZPXdcjppDIK66dXIWAEVtUNDAiAF+vfCR7pPIIOLAkI+S5jFcjd2Nh uTw4wscQiXhVArKGInMDWqKh37X/IjorxRJ56IKie1JqrK5CDqencqEQDW4CwcIc4Bhj 6cjQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1739032896; x=1739637696; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=kGngZIwfO/jqpHla3vwHAdrO/yliWLs6xLpeLI2Xezk=; b=Vm4FiGMKQopfYkKlrOuoWd+9ScVyQNlFlIf4LaQ7rcpiYQtpnl2ITc1EGGVHU6E6x6 YGYXKquO7lRUFFSzcT5xqYheqe1SRejGta+8V50s3az89foEsVSryW8eBoRlrGB4UHfm yu/e5NXchu56ZCZFdJ43CwC4RBt3PeD8iYT0tOyUsXwZO/FuO8yqKX7ipLzn7+SFCVTi ItLN/Ok5uV2lXwJ9ZHFB554PVfdufpEqTAq+XePMoI1HTt6Zu7DmkLp5QKZzYdHo8jft 23ASt2iRijn8506i+OZwreyYx5cW5SdqgzNjWsX6LrzaSO3mHEI/5cnZEY7KFaNZiTlt fejg== X-Forwarded-Encrypted: i=1; AJvYcCUk/5AalFnzcQ9IutXx9Fs9URelyIufFkelknuCNsKRD70b72Jl0N/pHSbjdtQDlXMYaeMM+ZQrWS9lmKA=@vger.kernel.org X-Gm-Message-State: AOJu0Yw/jP09y57XHzExkRK0OjI6yYldRNVKXuYLWrx20NoDBsxr2xU1 BLJijFFoEZ3aACkS1bKiWjuUZF4OxOO5Odr5Arc78H1ymlezpKcC8NG2mg== X-Gm-Gg: ASbGnctZuGCv10Wrq3Wc7D+qxbLmnyEGhwSqZO5zF/A4Bai4y/GDrilw8aYZkJP6IA7 AhKpN7gB3nz6CRSjiyaRzif+lVevKHBF7/4oknDNogDeYCJafV1VTNYknk5oTF7kY6lLItZvnNn AkIolILhDI4LBBiVvW6hMXkn0Zw6jk4F1w1sC5c6KPv2AV04sEYi1YV3BYPjzUuGJfcYmkzQ7xa Z2U+ISMua15t1apXcWEbGFxd1dW+HWlOruTKq4/JJwBuRV8D+V+xISDzk3wJtlSutlXjE3Q/08h Cjgt5VbVLAuTVGbT+sN29/OV2FS9sA== X-Google-Smtp-Source: AGHT+IE40RmfH5zbhEsfUF13K2v7/nQmB6Wqch1RrLCWdbjL7yzGlcwxlJXoUNgWH7NMbbpApzXXew== X-Received: by 2002:a17:907:d03:b0:ab3:a4f6:7551 with SMTP id a640c23a62f3a-ab76e8cc47fmr1269835966b.13.1739032895654; Sat, 08 Feb 2025 08:41:35 -0800 (PST) Received: from localhost.localdomain ([2a02:908:e842:bf20:e115:64e7:5d6:176]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ab773337f3dsm501539166b.139.2025.02.08.08.41.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Feb 2025 08:41:35 -0800 (PST) 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 v7 11/11] kconfig: Add documentation for the conflict resolver Date: Sat, 8 Feb 2025 17:39:59 +0100 Message-Id: <20250208163959.3973163-12-ole0811sch@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250208163959.3973163-1-ole0811sch@gmail.com> References: <20250208163959.3973163-1-ole0811sch@gmail.com> Precedence: bulk X-Mailing-List: linux-kbuild@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add documentation for the interface of the conflict resolver and instructions for installing PicoSAT. The target audience is everyone using xconfig, in particular, kernel developers, end users, system administrators, and distributors. Signed-off-by: Ole Schuerks --- Documentation/kbuild/kconfig.rst | 56 ++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/Documentation/kbuild/kconfig.rst b/Documentation/kbuild/kconfig.rst index fc4e845bc249..d49ab303e757 100644 --- a/Documentation/kbuild/kconfig.rst +++ b/Documentation/kbuild/kconfig.rst @@ -285,6 +285,62 @@ Searching in xconfig: You can also enter a different search string without having to return to the main menu. +Conflict resolution +------------------- + + xconfig has support for conflict resolution. A conflict is in this case any + situation where you want to change the value of a symbol, but + unfulfilled dependencies prevent this. You can create a list of symbols + and their desired values, and the conflict resolver will calculate a series + of changes in xconfig, which allows setting the symbols to their desired + values. + +Requirements: + + To use the conflict resolver, PicoSAT needs to be installed as a library. + + Debian-based distributions:: + + sudo apt install picosat + + Fedora:: + + sudo dnf install picosat + + You can also build PicoSAT yourself from the `sources + `_. The conflict resolver + requires that PicoSAT is built with tracing enabled (e.g., using the + configure.sh script with the "--trace" option). It expects the shared + library to be named "libpicosat-trace.so", "libpicosat-trace.so.0" or + "libpicosat-trace.so.1". + +Usage: + + To add a symbol to the list of symbols whose values should be changed (that + is, the 'conflict'), you select the symbol in the main view of xconfig. With + the button "Add symbol" you add the symbol to the conflict, which makes it + appear in a table below the main view. You need to switch to "Show Prompt + Options" under the tab "Option" if the symbol is hidden in the main view. + You can set the desired value of a symbol by either clicking on the + corresponding cell in the column "Wanted Value," or by selecting the + symbol's row and using one of the buttons above the table. + + Once the 'conflict' is declared, the solutions can be calculated using the + button "Calculate Fixes". Once calculated, they appear in the menu on the + bottom right. You can select a solution from up to three candidates. The + solutions are presented in a table that shows which values the symbols need + to have to resolve the conflict. Using the button "Apply selected solution" + the indicated changes can automatically be applied. If you want to change + the values manually, the symbols are color-coded to indicate the order in + which they need to be set: Green means that a symbol is already set to the + calculated value. Gray means that a symbol cannot yet be set to the + calculated value and that other symbols' values need to be changed first. + Red means that a symbol is not yet set to the calculated value, but that you + can set it to the calculated value. + + Note that in rare cases the conflict resolver cannot resolve the conflict + even when a solution exists, it suggests unnecessary changes, or it suggests + changes that do not resolve the conflict. gconfig =======