15#include "llvm/ADT/APInt.h"
16#include "llvm/ADT/BitVector.h"
17#include "llvm/ADT/STLExtras.h"
18#include "llvm/Support/MathExtras.h"
33expandTruthTableToInputSpaceSmall(
const llvm::APInt &tt,
34 ArrayRef<unsigned> mapping,
35 unsigned numExpandedInputs) {
36 assert(numExpandedInputs <= 6 &&
"small fast path requires <= 6 inputs");
38 unsigned numOrigInputs = mapping.size();
39 unsigned expandedSize = 1U << numExpandedInputs;
43 static constexpr uint64_t
kVarMasks[6] = {
44 0xAAAAAAAAAAAAAAAAULL,
45 0xCCCCCCCCCCCCCCCCULL,
46 0xF0F0F0F0F0F0F0F0ULL,
47 0xFF00FF00FF00FF00ULL,
48 0xFFFF0000FFFF0000ULL,
52 uint64_t sizeMask = llvm::maskTrailingOnes<uint64_t>(expandedSize);
53 unsigned origSize = 1U << numOrigInputs;
54 uint64_t origMask = llvm::maskTrailingOnes<uint64_t>(origSize);
55 uint64_t origTT = tt.getZExtValue() & origMask;
59 std::array<std::array<uint64_t, 2>, 6> constrainMasks{};
60 for (
unsigned i = 0; i < numOrigInputs; ++i) {
61 uint64_t posMask =
kVarMasks[mapping[i]] & sizeMask;
62 constrainMasks[i][0] = (~posMask) & sizeMask;
63 constrainMasks[i][1] = posMask;
66 uint64_t activePatterns = origTT;
67 bool useComplementResult =
false;
71 if (
static_cast<unsigned>(llvm::popcount(origTT)) > (origSize / 2)) {
72 activePatterns = (~origTT) & origMask;
73 useComplementResult =
true;
77 while (activePatterns) {
78 unsigned origIdx = llvm::countr_zero(activePatterns);
79 activePatterns &= activePatterns - 1;
82 for (
unsigned i = 0; i < numOrigInputs; ++i)
83 pattern &= constrainMasks[i][(origIdx >> i) & 1U];
85 if (useComplementResult)
91 return llvm::APInt(expandedSize, result);
95expandTruthTableToInputSpaceGeneric(
const llvm::APInt &tt,
96 ArrayRef<unsigned> mapping,
97 unsigned numExpandedInputs) {
98 unsigned numOrigInputs = mapping.size();
99 unsigned expandedSize = 1U << numExpandedInputs;
101 llvm::APInt result = llvm::APInt::getZero(expandedSize);
102 for (
unsigned expandedIdx = 0; expandedIdx < expandedSize; ++expandedIdx) {
103 unsigned origIdx = 0;
104 for (
unsigned i = 0; i < numOrigInputs; ++i)
105 if ((expandedIdx >> mapping[i]) & 1U)
108 result.setBit(expandedIdx);
122 const llvm::APInt &output) {
125 assert(input.getBitWidth() < 32 &&
"Input width too large");
126 unsigned offset = input.getZExtValue() *
numOutputs;
128 table.setBitVal(offset + i, output[i]);
133 assert(permutation.size() ==
numInputs &&
"Permutation size mismatch");
136 for (
unsigned i = 0; i < (1u <<
numInputs); ++i) {
138 llvm::APInt permutedInput = input;
141 for (
unsigned j = 0; j <
numInputs; ++j) {
142 if (permutation[j] != j)
143 permutedInput.setBitVal(j, input[permutation[j]]);
155 for (
unsigned i = 0; i < (1u <<
numInputs); ++i) {
159 llvm::APInt negatedInput = input ^ llvm::APInt(
numInputs, mask);
171 for (
unsigned i = 0; i < (1u <<
numInputs); ++i) {
176 llvm::APInt negatedOutput = output ^ llvm::APInt(
numOutputs, negation);
198 os <<
table <<
")\n";
201 for (
unsigned i = 0; i <
numInputs; ++i) {
202 os <<
"i" << i <<
" ";
205 os <<
"o" << i <<
" ";
216 for (
unsigned i = 0; i < (1u <<
numInputs); ++i) {
218 for (
unsigned j = 0; j <
numInputs; ++j) {
219 os << ((i >> j) & 1) <<
" ";
227 os << output[j] <<
" ";
235 ArrayRef<unsigned> mapping,
236 unsigned numExpandedInputs) {
237 unsigned numOrigInputs = mapping.size();
238 unsigned expandedSize = 1U << numExpandedInputs;
240 if (numOrigInputs == numExpandedInputs) {
241 bool isIdentity =
true;
242 for (
unsigned i = 0; i < numOrigInputs && isIdentity; ++i)
243 isIdentity = mapping[i] == i;
245 return tt.zext(expandedSize);
249 return llvm::APInt::getZero(expandedSize);
251 return llvm::APInt::getAllOnes(expandedSize);
253 if (numExpandedInputs <= 6)
254 return expandTruthTableToInputSpaceSmall(tt, mapping, numExpandedInputs);
255 return expandTruthTableToInputSpaceGeneric(tt, mapping, numExpandedInputs);
268llvm::SmallVector<unsigned> identityPermutation(
unsigned size) {
269 llvm::SmallVector<unsigned> identity(size);
270 for (
unsigned i = 0; i < size; ++i)
278unsigned permuteNegationMask(
unsigned negationMask,
279 ArrayRef<unsigned> permutation) {
281 for (
unsigned i = 0; i < permutation.size(); ++i)
282 if (negationMask & (1u << permutation[i]))
289llvm::SmallVector<unsigned> invertPermutation(ArrayRef<unsigned> permutation) {
290 llvm::SmallVector<unsigned> inverse(permutation.size());
291 for (
unsigned i = 0; i < permutation.size(); ++i) {
292 inverse[permutation[i]] = i;
297llvm::SmallVector<unsigned>
298expandInputPermutation(
const std::array<uint8_t, 4> &permutation) {
299 llvm::SmallVector<unsigned> result;
300 result.reserve(permutation.size());
301 for (uint8_t index : permutation)
302 result.push_back(index);
306struct NPNTransform4 {
309 std::array<uint8_t, 16> outputToSource = {};
312 std::array<uint8_t, 16> inverseOutputToSource = {};
313 std::array<uint8_t, 4> inputPermutation = {};
314 uint8_t inputNegation = 0;
315 bool outputNegation =
false;
318uint16_t applyNPNTransform4(uint16_t truthTable,
319 const std::array<uint8_t, 16> &outputToSource,
320 bool outputNegation) {
322 for (
unsigned output = 0; output != 16; ++output) {
323 unsigned bit = (truthTable >> outputToSource[output]) & 1u;
326 result |=
static_cast<uint16_t
>(bit << output);
331void buildCanonicalOrderNPNTransforms4(
332 llvm::SmallVectorImpl<NPNTransform4> &transforms) {
334 transforms.reserve(24 * 16 * 2);
338 for (
unsigned negMask = 0; negMask != 16; ++negMask) {
339 std::array<unsigned, 4> permutation = {0, 1, 2, 3};
341 std::array<unsigned, 4> inversePermutation = {};
342 for (
unsigned i = 0; i != 4; ++i)
343 inversePermutation[permutation[i]] = i;
345 uint8_t currentNegMask = permuteNegationMask(negMask, permutation);
346 for (
unsigned outputNegation = 0; outputNegation != 2; ++outputNegation) {
347 NPNTransform4 transform;
348 transform.inputNegation = currentNegMask;
349 transform.outputNegation = outputNegation;
350 for (
unsigned i = 0; i != 4; ++i)
351 transform.inputPermutation[i] = permutation[i];
353 for (
unsigned output = 0; output != 16; ++output) {
355 for (
unsigned input = 0; input != 4; ++input) {
356 unsigned bit = (output >> inversePermutation[input]) & 1u;
357 bit ^= (negMask >> input) & 1u;
358 source |= bit << input;
360 transform.outputToSource[output] = source;
361 transform.inverseOutputToSource[source] = output;
363 transforms.push_back(transform);
365 }
while (std::next_permutation(permutation.begin(), permutation.end()));
369void collectNPN4Representatives(ArrayRef<NPNTransform4> transforms,
370 llvm::SmallVectorImpl<uint16_t> &reps) {
371 llvm::BitVector seen(1u << 16,
false);
374 for (
unsigned seed = 0; seed != (1u << 16); ++seed) {
380 uint16_t representative = seed;
381 for (
const auto &transform : transforms) {
382 uint16_t member = applyNPNTransform4(seed, transform.outputToSource,
383 transform.outputNegation);
385 representative = std::min(representative, member);
387 reps.push_back(representative);
394 llvm::SmallVectorImpl<uint16_t> &representatives) {
395 llvm::SmallVector<NPNTransform4, 24 * 16 * 2> transforms;
396 buildCanonicalOrderNPNTransforms4(transforms);
397 collectNPN4Representatives(transforms, representatives);
401 llvm::SmallVector<uint16_t, 222> representatives;
404 llvm::SmallVector<NPNTransform4, 24 * 16 * 2> transforms;
405 buildCanonicalOrderNPNTransforms4(transforms);
407 llvm::BitVector initialized(
entries4.size(),
false);
408 auto isBetterEntry = [&](
const Entry4 &candidate,
const Entry4 ¤t) {
418 for (uint16_t representative : representatives) {
419 for (
const auto &transform : transforms) {
423 applyNPNTransform4(representative, transform.inverseOutputToSource,
424 transform.outputNegation);
432 if (!initialized.test(member) ||
433 isBetterEntry(candidate,
entries4[member])) {
435 initialized.set(member);
440 assert(initialized.all() &&
"expected to populate all 4-input NPN entries");
449 expandInputPermutation(entry.inputPermutation),
450 entry.inputNegation, entry.outputNegation);
456 llvm::SmallVectorImpl<unsigned> &permutation)
const {
458 "NPN classes must have the same number of inputs");
460 "NPN classes must be equivalent for input mapping");
473 permutation.push_back(thisInverse[canonicalPos]);
486 for (uint32_t negMask = 0; negMask < (1u << tt.
numInputs); ++negMask) {
490 auto permutation = identityPermutation(tt.
numInputs);
496 unsigned currentNegMask = permuteNegationMask(negMask, permutation);
499 for (
unsigned outputNegMask = 0; outputNegMask < (1u << tt.
numOutputs);
506 NPNClass candidate(candidateTT, permutation, currentNegMask,
512 canonical = std::move(candidate);
514 }
while (std::next_permutation(permutation.begin(), permutation.end()));
522 os <<
" Input permutation: [";
529 os <<
" Input negation: 0b";
534 os <<
" Output negation: 0b";
539 os <<
" Canonical truth table:\n";
553 {0x5555555555555555ULL, 0x3333333333333333ULL, 0x0F0F0F0F0F0F0F0FULL,
554 0x00FF00FF00FF00FFULL, 0x0000FFFF0000FFFFULL,
555 0x00000000FFFFFFFFULL},
556 {0xAAAAAAAAAAAAAAAAULL, 0xCCCCCCCCCCCCCCCCULL, 0xF0F0F0F0F0F0F0F0ULL,
557 0xFF00FF00FF00FF00ULL, 0xFFFF0000FFFF0000ULL,
558 0xFFFFFFFF00000000ULL},
564template <
bool positive>
566 assert(numVars <= 64 &&
"Number of variables too large");
567 assert(varIndex < numVars &&
"Variable index out of bounds");
568 uint64_t numBits = 1u << numVars;
573 uint64_t maskValue =
kVarMasks[positive][varIndex];
576 maskValue &= (1ULL << numBits) - 1;
577 return APInt(numBits, maskValue);
582 uint64_t patternWidth = 1u << (varIndex + 1);
583 APInt
pattern(patternWidth, 0);
586 uint64_t shift = 1u << varIndex;
587 if constexpr (positive) {
589 pattern.setBits(shift, patternWidth);
595 return APInt::getSplat(numBits,
pattern);
600 return createVarMaskImpl<true>(numVars, varIndex);
601 return createVarMaskImpl<false>(numVars, varIndex);
619std::pair<APInt, APInt>
621 assert(numVars <= 64 &&
"Number of variables too large");
622 assert(var < numVars &&
"Variable index out of bounds");
623 assert(f.getBitWidth() == (1u << numVars) &&
"Truth table size mismatch");
624 uint64_t numBits = 1u << numVars;
625 uint64_t shift = 1u << var;
631 APInt blockPattern = APInt::getLowBitsSet(2 * shift, shift);
632 APInt mask0 = APInt::getSplat(numBits, blockPattern);
633 APInt mask1 = mask0.shl(shift);
636 APInt selected0 = f & mask0;
637 APInt selected1 = f & mask1;
640 APInt cof0 = selected0 | selected0.shl(shift);
641 APInt cof1 = selected1 | selected1.lshr(shift);
652 for (
const auto &cube :
cubes) {
653 APInt cubeTT = ~APInt(1 <<
numVars, 0);
654 for (
unsigned i = 0; i <
numVars; ++i) {
655 if (cube.hasLiteral(i))
665 os <<
"SOPForm: " <<
numVars <<
" vars, " <<
cubes.size() <<
" cubes\n";
666 for (
const auto &cube :
cubes) {
668 for (
unsigned i = 0; i <
numVars; ++i) {
669 if (cube.mask & (1ULL << i)) {
670 os << ((cube.inverted & (1ULL << i)) ?
"!" :
"");
671 os <<
"x" << i <<
" ";
720APInt isopImpl(
const APInt &tt,
const APInt &dc,
unsigned numVars,
721 unsigned varIndex,
SOPForm &result) {
722 assert((tt & ~dc).isZero() &&
"tt must be subset of dc");
729 if (dc.isAllOnes()) {
730 result.
cubes.emplace_back();
741 APInt negCof, posCof, negDC, posDC;
742 for (
int i = varIndex - 1; i >= 0; --i) {
745 if (negCof != posCof || negDC != posDC) {
750 assert(var >= 0 &&
"No variable found in support");
753 size_t negBegin = result.
cubes.size();
754 APInt negCover = isopImpl(negCof & ~posDC, negDC, numVars, var, result);
755 size_t negEnd = result.
cubes.size();
758 APInt posCover = isopImpl(posCof & ~negDC, posDC, numVars, var, result);
759 size_t posEnd = result.
cubes.size();
762 APInt remaining = (negCof & ~negCover) | (posCof & ~posCover);
763 APInt sharedCover = isopImpl(remaining, negDC & posDC, numVars, var, result);
766 APInt negMask = createVarMaskImpl<false>(numVars, var);
767 APInt totalCover = sharedCover | (negCover & negMask) | (posCover & ~negMask);
770 for (
size_t i = negBegin; i < negEnd; ++i)
771 result.
cubes[i].setLiteral(var,
true);
773 for (
size_t i = negEnd; i < posEnd; ++i)
774 result.
cubes[i].setLiteral(var,
false);
782 assert((1u << numVars) == truthTable.getBitWidth() &&
783 "Truth table size must match 2^numVars");
786 if (numVars == 0 || truthTable.isZero())
789 (void)isopImpl(truthTable, truthTable, numVars, numVars, sop);
assert(baseType &&"element must be base type")
RewritePatternSet pattern
static APInt createVarMaskImpl(unsigned numVars, unsigned varIndex)
Create a mask for a variable in the truth table.
static constexpr uint64_t kVarMasks[2][6]
Precomputed masks for variables in truth tables up to 6 variables (64 bits).
std::array< Entry4, 1u<< 16 > entries4
bool lookup(const BinaryTruthTable &tt, NPNClass &result) const
Returns false if the given truth table shape is unsupported.
llvm::APInt expandTruthTableToInputSpace(const llvm::APInt &tt, ArrayRef< unsigned > inputMapping, unsigned numExpandedInputs)
Expand a truth table to a larger input space using the given input mapping.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
SOPForm extractISOP(const llvm::APInt &truthTable, unsigned numVars)
Extract ISOP (Irredundant Sum-of-Products) from a truth table.
std::pair< llvm::APInt, llvm::APInt > computeCofactors(const llvm::APInt &f, unsigned numVars, unsigned varIndex)
Compute cofactor of a Boolean function for a given variable.
llvm::APInt createVarMask(unsigned numVars, unsigned varIndex, bool positive)
Create a mask for a variable in the truth table.
void collectCanonicalNPN4Representatives(llvm::SmallVectorImpl< uint16_t > &representatives)
Collect all canonical 4-input single-output NPN representatives in ascending truth-table order.
Represents a boolean function as a truth table.
BinaryTruthTable applyOutputNegation(unsigned negation) const
Apply output negation to create a new truth table.
llvm::APInt table
Truth table data as a packed bit vector.
BinaryTruthTable applyInputNegation(unsigned mask) const
Apply input negation to create a new truth table.
unsigned numInputs
Number of inputs for this boolean function.
void dump(llvm::raw_ostream &os=llvm::errs()) const
Debug dump method for truth tables.
bool isLexicographicallySmaller(const BinaryTruthTable &other) const
Check if this truth table is lexicographically smaller than another.
bool operator==(const BinaryTruthTable &other) const
Equality comparison for truth tables.
llvm::APInt getOutput(const llvm::APInt &input) const
Get the output value for a given input combination.
BinaryTruthTable applyPermutation(ArrayRef< unsigned > permutation) const
Apply input permutation to create a new truth table.
void setOutput(const llvm::APInt &input, const llvm::APInt &output)
Set the output value for a given input combination.
unsigned numOutputs
Number of outputs for this boolean function.
Represents the canonical form of a boolean function under NPN equivalence.
unsigned outputNegation
Output negation mask.
static NPNClass computeNPNCanonicalForm(const BinaryTruthTable &tt)
Compute the canonical NPN form for a given truth table.
void getInputPermutation(const NPNClass &targetNPN, llvm::SmallVectorImpl< unsigned > &permutation) const
Get input permutation from this NPN class to another equivalent NPN class.
BinaryTruthTable truthTable
Canonical truth table.
void dump(llvm::raw_ostream &os=llvm::errs()) const
Debug dump method for NPN classes.
bool equivalentOtherThanPermutation(const NPNClass &other) const
Equality comparison for NPN classes.
llvm::SmallVector< unsigned > inputPermutation
Input permutation applied.
unsigned inputNegation
Input negation mask.
bool isLexicographicallySmaller(const NPNClass &other) const
std::array< uint8_t, 4 > inputPermutation