Skip to content

Commit 845f862

Browse files
committed
More debugging
1 parent 3dad928 commit 845f862

File tree

4 files changed

+1165
-8
lines changed

4 files changed

+1165
-8
lines changed

include/circt/Support/SatSolver.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@
2222
#ifndef CIRCT_SUPPORT_SATSOLVER_H
2323
#define CIRCT_SUPPORT_SATSOLVER_H
2424

25+
#include "llvm/ADT/ArrayRef.h"
2526
#include "llvm/ADT/STLFunctionalExtras.h"
2627
#include "llvm/ADT/SmallVector.h"
2728
#include "llvm/Support/Compiler.h"
29+
#include "llvm/Support/raw_ostream.h"
2830
#include <cassert>
2931
#include <cstdint>
3032

@@ -207,6 +209,13 @@ class MiniSATSolver : public IncrementalSATSolverBase<MiniSATSolver> {
207209
/// Pre-allocate variables up to maxVar (1-indexed) without adding clauses.
208210
void reserveVars(int maxVar) { ensureVar(maxVar); }
209211

212+
/// Export current formula in DIMACS CNF format. Optionally includes
213+
/// assumptions as unit clauses. Useful for debugging and creating reproducers.
214+
/// Format: "p cnf <vars> <clauses>\n" followed by clauses (space-separated
215+
/// literals terminated by 0).
216+
void dumpDIMACS(llvm::raw_ostream &os,
217+
llvm::ArrayRef<int> assumptions = {}) const;
218+
210219
/// Save current solver state. Must be at decision level 0 with no pending
211220
/// propagations. On rollback, variables/clauses added after bookmark are
212221
/// removed, watch lists are rebuilt from surviving clauses, and VSIDS

lib/Dialect/Synth/Transforms/FunctionalReduction.cpp

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,10 @@ void FRAIGSolver<SATSolverT>::verifyCandidates() {
688688
LLVM_DEBUG(llvm::dbgs() << "FRAIG: Starting SAT verification with "
689689
<< equivClasses.size() << " equivalence classes\n");
690690

691+
// Check if user wants to dump all SAT queries
692+
const char *dumpDir = std::getenv("FRAIG_DUMP_SAT");
693+
bool dumpAll = (dumpDir != nullptr);
694+
691695
// Global solver with bookmark/rollback (ABC-style):
692696
// - Pre-allocate variables for all values so they persist across rollbacks
693697
// - Learned clauses accumulate across equivalence classes (knowledge sharing)
@@ -729,6 +733,20 @@ void FRAIGSolver<SATSolverT>::verifyCandidates() {
729733

730734
// Check 1: Can rep=1 and target=0?
731735
stats.numSATCalls++;
736+
737+
// Optionally dump SAT query for debugging
738+
if (dumpAll) {
739+
std::string filename = std::string(dumpDir) + "/sat_query_" +
740+
std::to_string(stats.numSATCalls) + "_check1.cnf";
741+
std::error_code EC;
742+
llvm::raw_fd_ostream out(filename, EC);
743+
if (!EC) {
744+
solver.dumpDIMACS(out, {repVar, -targetLit});
745+
LLVM_DEBUG(llvm::dbgs()
746+
<< "FRAIG: Dumped SAT query to " << filename << "\n");
747+
}
748+
}
749+
732750
solver.assume(repVar);
733751
solver.assume(-targetLit);
734752
auto result1 = solver.solve(conflictLimit);
@@ -749,6 +767,20 @@ void FRAIGSolver<SATSolverT>::verifyCandidates() {
749767

750768
// Check 2: Can rep=0 and target=1?
751769
stats.numSATCalls++;
770+
771+
// Optionally dump SAT query for debugging
772+
if (dumpAll) {
773+
std::string filename = std::string(dumpDir) + "/sat_query_" +
774+
std::to_string(stats.numSATCalls) + "_check2.cnf";
775+
std::error_code EC;
776+
llvm::raw_fd_ostream out(filename, EC);
777+
if (!EC) {
778+
solver.dumpDIMACS(out, {-repVar, targetLit});
779+
LLVM_DEBUG(llvm::dbgs()
780+
<< "FRAIG: Dumped SAT query to " << filename << "\n");
781+
}
782+
}
783+
752784
solver.assume(-repVar);
753785
solver.assume(targetLit);
754786
auto result2 = solver.solve(conflictLimit);
@@ -940,6 +972,7 @@ class CadicalSATSolver : public IncrementalSATSolverBase<CadicalSATSolver> {
940972
LLVM_DEBUG(llvm::dbgs() << "Assuming: " << lit << "\n");
941973
solver->assume(lit);
942974
testSolver.assume(lit);
975+
currentAssumptions.push_back(lit);
943976
}
944977

945978
[[nodiscard]] Result solve(int64_t confLimit = -1) {
@@ -979,8 +1012,38 @@ class CadicalSATSolver : public IncrementalSATSolverBase<CadicalSATSolver> {
9791012
: (miniSatResult == MiniSATSolver::kUNSAT ? "UNSAT"
9801013
: "UNKNOWN"))
9811014
<< "\n";
1015+
1016+
// Dump DIMACS reproducer for debugging
1017+
static int mismatchCount = 0;
1018+
std::string filename =
1019+
"/tmp/cadical_mismatch_" + std::to_string(++mismatchCount) + ".cnf";
1020+
std::error_code EC;
1021+
llvm::raw_fd_ostream out(filename, EC);
1022+
if (!EC) {
1023+
dumpDIMACS(out, currentAssumptions);
1024+
llvm::errs() << "Dumped reproducer to " << filename << "\n";
1025+
} else {
1026+
llvm::errs() << "Failed to dump reproducer: " << EC.message() << "\n";
1027+
}
1028+
1029+
// Also dump MiniSAT's view
1030+
std::string miniFilename =
1031+
"/tmp/minisat_mismatch_" + std::to_string(mismatchCount) + ".cnf";
1032+
llvm::raw_fd_ostream miniOut(miniFilename, EC);
1033+
if (!EC) {
1034+
// Convert assumptions to proper format for dumpDIMACS
1035+
llvm::SmallVector<int> extAssumptions;
1036+
for (int lit : currentAssumptions) {
1037+
extAssumptions.push_back(lit);
1038+
}
1039+
testSolver.dumpDIMACS(miniOut, extAssumptions);
1040+
llvm::errs() << "Dumped MiniSAT view to " << miniFilename << "\n";
1041+
}
9821042
}
9831043

1044+
// Clear assumptions for next solve
1045+
currentAssumptions.clear();
1046+
9841047
return cadicalResult;
9851048
}
9861049

@@ -1046,13 +1109,46 @@ class CadicalSATSolver : public IncrementalSATSolverBase<CadicalSATSolver> {
10461109

10471110
[[nodiscard]] bool hasBookmark() const { return hasBookmark_; }
10481111

1112+
/// Export current formula in DIMACS CNF format for debugging.
1113+
void dumpDIMACS(llvm::raw_ostream &os,
1114+
llvm::ArrayRef<int> assumptions = {}) const {
1115+
// Count total clauses
1116+
size_t totalClauses = clauses.size() + assumptions.size();
1117+
1118+
// DIMACS header (need to track maxVar)
1119+
int maxVar = 0;
1120+
for (const auto &clause : clauses) {
1121+
for (int lit : clause) {
1122+
maxVar = std::max(maxVar, std::abs(lit));
1123+
}
1124+
}
1125+
for (int lit : assumptions) {
1126+
maxVar = std::max(maxVar, std::abs(lit));
1127+
}
1128+
1129+
os << "p cnf " << maxVar << " " << totalClauses << "\n";
1130+
1131+
// All clauses
1132+
for (const auto &clause : clauses) {
1133+
for (int lit : clause)
1134+
os << lit << " ";
1135+
os << "0\n";
1136+
}
1137+
1138+
// Assumptions as unit clauses
1139+
for (int lit : assumptions) {
1140+
os << lit << " 0\n";
1141+
}
1142+
}
1143+
10491144
private:
10501145
CaDiCaL::Solver *solver;
10511146
bool hasBookmark_ = false;
10521147
llvm::SmallVector<llvm::SmallVector<int, 4>, 0> clauses; // All clauses added
10531148
llvm::SmallVector<llvm::SmallVector<int, 4>, 0>
10541149
bookmarkedClauses; // Clauses at bookmark
10551150
llvm::SmallVector<int, 4> currentClause; // Current clause being built
1151+
llvm::SmallVector<int, 4> currentAssumptions; // Current assumptions
10561152

10571153
MiniSATSolver testSolver;
10581154
};

lib/Support/SatSolver.cpp

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@
88

99
#include "circt/Support/SatSolver.h"
1010
#include "llvm/ADT/STLExtras.h"
11+
#include "llvm/Support/Debug.h"
1112
#include "llvm/Support/raw_ostream.h"
1213
#include <algorithm>
1314
#include <cmath>
1415

16+
#define DEBUG_TYPE "sat-solver"
17+
1518
using namespace circt;
1619

1720
//===----------------------------------------------------------------------===//
@@ -45,6 +48,46 @@ int MiniSATSolver::val(int v) const {
4548
return vars[v - 1].modelVal == kTrue ? v : -v;
4649
}
4750

51+
void MiniSATSolver::dumpDIMACS(llvm::raw_ostream &os,
52+
llvm::ArrayRef<int> assumptions) const {
53+
// Count total clauses: problem + learnt + binary + assumptions
54+
size_t totalClauses = problemClauses.size() + learntClauses.size() +
55+
binaryClauses.size() + learntBinaryClauses.size() +
56+
assumptions.size();
57+
58+
// DIMACS header
59+
os << "p cnf " << numVars << " " << totalClauses << "\n";
60+
61+
// Problem clauses (non-binary)
62+
for (const auto &clause : problemClauses) {
63+
for (int lit : clause.lits)
64+
os << decodeLit(lit) << " ";
65+
os << "0\n";
66+
}
67+
68+
// Binary problem clauses
69+
for (const auto &[lit0, lit1] : binaryClauses) {
70+
os << decodeLit(lit0) << " " << decodeLit(lit1) << " 0\n";
71+
}
72+
73+
// Learned clauses (optional - helps reproducibility)
74+
for (const auto &clause : learntClauses) {
75+
for (int lit : clause.lits)
76+
os << decodeLit(lit) << " ";
77+
os << "0\n";
78+
}
79+
80+
// Binary learned clauses
81+
for (const auto &[lit0, lit1] : learntBinaryClauses) {
82+
os << decodeLit(lit0) << " " << decodeLit(lit1) << " 0\n";
83+
}
84+
85+
// Assumptions as unit clauses
86+
for (int lit : assumptions) {
87+
os << lit << " 0\n";
88+
}
89+
}
90+
4891
//===----------------------------------------------------------------------===//
4992
// Variable management
5093
//===----------------------------------------------------------------------===//
@@ -135,7 +178,6 @@ void MiniSATSolver::backtrack(int level) {
135178
<< " clearFrom=" << clearFrom << "\n";
136179

137180
auto sf = [this](int x) { return vars[x].activity; };
138-
bool found865 = false;
139181
for (int i = static_cast<int>(trail.size()) - 1; i >= clearFrom; i--) {
140182
int v = litVar(trail[i]);
141183
auto &var = vars[v];
@@ -643,7 +685,17 @@ MiniSATSolver::Result MiniSATSolver::search(int64_t confBudget) {
643685
int backLevel;
644686
analyze(confl, learntClause, backLevel);
645687
backtrack(std::max(backLevel, rootLevel));
646-
recordLearnt(learntClause);
688+
// Only record learned clauses if they don't involve assumption levels.
689+
// Learned clauses containing literals from assumption levels (1..rootLevel)
690+
// are contaminated and invalid for other assumption sets.
691+
if (backLevel < rootLevel) {
692+
// Don't learn - clause is tainted by assumptions
693+
LLVM_DEBUG(llvm::dbgs() << "LEARN: Skipping tainted clause (backLevel="
694+
<< backLevel << " < rootLevel=" << rootLevel
695+
<< ")\n");
696+
} else {
697+
recordLearnt(learntClause);
698+
}
647699
decayVarActivity();
648700
decayClauseActivity();
649701
} else {
@@ -719,13 +771,9 @@ MiniSATSolver::Result MiniSATSolver::solveImpl(int64_t confLimit) {
719771
<< " assign=" << static_cast<int>(vars[v].assign)
720772
<< " wantedLit=" << enc << "\n";
721773
}
722-
llvm::errs() << " After enqueue: trail.size=" << trail.size()
723-
<< " var865.assign=" << static_cast<int>(vars[865].assign)
724-
<< "\n";
774+
llvm::errs() << " After enqueue: trail.size=" << trail.size() << "\n";
725775
Conflict assumptionConfl = propagate();
726-
llvm::errs() << " After propagate: trail.size=" << trail.size()
727-
<< " var865.assign=" << static_cast<int>(vars[865].assign)
728-
<< "\n";
776+
llvm::errs() << " After propagate: trail.size=" << trail.size() << "\n";
729777
if (!enqueueOk || !assumptionConfl.isNone()) {
730778
llvm::errs() << "SOLVE: Assumption conflict! enqueueOk=" << enqueueOk
731779
<< " conflictNone=" << assumptionConfl.isNone() << "\n";

0 commit comments

Comments
 (0)