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) {
99 unsigned numOrigInputs = mapping.size();
100 unsigned expandedSize = 1U << numExpandedInputs;
101 unsigned numOrigBits = tt.getBitWidth();
104 SmallVector<std::array<llvm::APInt, 2>, 16> masks;
105 masks.reserve(numOrigInputs);
106 for (
unsigned i = 0; i < numOrigInputs; ++i) {
107 unsigned mappedInput = mapping[i];
115 unsigned numOnes = tt.popcount();
116 bool enumerateSetBits = numOnes <= numOrigBits / 2;
118 llvm::APInt result = enumerateSetBits ? llvm::APInt::getZero(expandedSize)
119 :
llvm::APInt::getAllOnes(expandedSize);
121 for (
unsigned origIdx = 0, e = numOrigBits; origIdx < e; ++origIdx) {
122 if (tt[origIdx] != enumerateSetBits)
125 llvm::APInt
pattern = llvm::APInt::getAllOnes(expandedSize);
126 for (
unsigned i = 0; i < numOrigInputs; ++i) {
127 bool bit = (origIdx >> i) & 1U;
131 if (enumerateSetBits)
147 const llvm::APInt &output) {
150 assert(input.getBitWidth() < 32 &&
"Input width too large");
151 unsigned offset = input.getZExtValue() *
numOutputs;
153 table.setBitVal(offset + i, output[i]);
158 assert(permutation.size() ==
numInputs &&
"Permutation size mismatch");
161 for (
unsigned i = 0; i < (1u <<
numInputs); ++i) {
163 llvm::APInt permutedInput = input;
166 for (
unsigned j = 0; j <
numInputs; ++j) {
167 if (permutation[j] != j)
168 permutedInput.setBitVal(j, input[permutation[j]]);
180 for (
unsigned i = 0; i < (1u <<
numInputs); ++i) {
184 llvm::APInt negatedInput = input ^ llvm::APInt(
numInputs, mask);
196 for (
unsigned i = 0; i < (1u <<
numInputs); ++i) {
201 llvm::APInt negatedOutput = output ^ llvm::APInt(
numOutputs, negation);
223 os <<
table <<
")\n";
226 for (
unsigned i = 0; i <
numInputs; ++i) {
227 os <<
"i" << i <<
" ";
230 os <<
"o" << i <<
" ";
241 for (
unsigned i = 0; i < (1u <<
numInputs); ++i) {
243 for (
unsigned j = 0; j <
numInputs; ++j) {
244 os << ((i >> j) & 1) <<
" ";
252 os << output[j] <<
" ";
260 ArrayRef<unsigned> mapping,
261 unsigned numExpandedInputs) {
262 unsigned numOrigInputs = mapping.size();
263 unsigned expandedSize = 1U << numExpandedInputs;
265 if (numOrigInputs == numExpandedInputs) {
266 bool isIdentity =
true;
267 for (
unsigned i = 0; i < numOrigInputs && isIdentity; ++i)
268 isIdentity = mapping[i] == i;
270 return tt.zext(expandedSize);
274 return llvm::APInt::getZero(expandedSize);
276 return llvm::APInt::getAllOnes(expandedSize);
278 if (numExpandedInputs <= 6)
279 return expandTruthTableToInputSpaceSmall(tt, mapping, numExpandedInputs);
280 return expandTruthTableToInputSpaceGeneric(tt, mapping, numExpandedInputs);
293llvm::SmallVector<unsigned> identityPermutation(
unsigned size) {
294 llvm::SmallVector<unsigned> identity(size);
295 for (
unsigned i = 0; i < size; ++i)
303unsigned permuteNegationMask(
unsigned negationMask,
304 ArrayRef<unsigned> permutation) {
306 for (
unsigned i = 0; i < permutation.size(); ++i)
307 if (negationMask & (1u << permutation[i]))
314llvm::SmallVector<unsigned> invertPermutation(ArrayRef<unsigned> permutation) {
315 llvm::SmallVector<unsigned> inverse(permutation.size());
316 for (
unsigned i = 0; i < permutation.size(); ++i) {
317 inverse[permutation[i]] = i;
322llvm::SmallVector<unsigned>
323expandInputPermutation(
const std::array<uint8_t, 4> &permutation) {
324 llvm::SmallVector<unsigned> result;
325 result.reserve(permutation.size());
326 for (uint8_t index : permutation)
327 result.push_back(index);
331struct NPNTransform4 {
334 std::array<uint8_t, 16> outputToSource = {};
337 std::array<uint8_t, 16> inverseOutputToSource = {};
338 std::array<uint8_t, 4> inputPermutation = {};
339 uint8_t inputNegation = 0;
340 bool outputNegation =
false;
343uint16_t applyNPNTransform4(uint16_t truthTable,
344 const std::array<uint8_t, 16> &outputToSource,
345 bool outputNegation) {
347 for (
unsigned output = 0; output != 16; ++output) {
348 unsigned bit = (truthTable >> outputToSource[output]) & 1u;
351 result |=
static_cast<uint16_t
>(bit << output);
356void buildCanonicalOrderNPNTransforms4(
357 llvm::SmallVectorImpl<NPNTransform4> &transforms) {
359 transforms.reserve(24 * 16 * 2);
363 for (
unsigned negMask = 0; negMask != 16; ++negMask) {
364 std::array<unsigned, 4> permutation = {0, 1, 2, 3};
366 std::array<unsigned, 4> inversePermutation = {};
367 for (
unsigned i = 0; i != 4; ++i)
368 inversePermutation[permutation[i]] = i;
370 uint8_t currentNegMask = permuteNegationMask(negMask, permutation);
371 for (
unsigned outputNegation = 0; outputNegation != 2; ++outputNegation) {
372 NPNTransform4 transform;
373 transform.inputNegation = currentNegMask;
374 transform.outputNegation = outputNegation;
375 for (
unsigned i = 0; i != 4; ++i)
376 transform.inputPermutation[i] = permutation[i];
378 for (
unsigned output = 0; output != 16; ++output) {
380 for (
unsigned input = 0; input != 4; ++input) {
381 unsigned bit = (output >> inversePermutation[input]) & 1u;
382 bit ^= (negMask >> input) & 1u;
383 source |= bit << input;
385 transform.outputToSource[output] = source;
386 transform.inverseOutputToSource[source] = output;
388 transforms.push_back(transform);
390 }
while (std::next_permutation(permutation.begin(), permutation.end()));
394void collectNPN4Representatives(ArrayRef<NPNTransform4> transforms,
395 llvm::SmallVectorImpl<uint16_t> &reps) {
396 llvm::BitVector seen(1u << 16,
false);
399 for (
unsigned seed = 0; seed != (1u << 16); ++seed) {
405 uint16_t representative = seed;
406 for (
const auto &transform : transforms) {
407 uint16_t member = applyNPNTransform4(seed, transform.outputToSource,
408 transform.outputNegation);
410 representative = std::min(representative, member);
412 reps.push_back(representative);
419 llvm::SmallVectorImpl<uint16_t> &representatives) {
420 llvm::SmallVector<NPNTransform4, 24 * 16 * 2> transforms;
421 buildCanonicalOrderNPNTransforms4(transforms);
422 collectNPN4Representatives(transforms, representatives);
426 llvm::SmallVector<uint16_t, 222> representatives;
429 llvm::SmallVector<NPNTransform4, 24 * 16 * 2> transforms;
430 buildCanonicalOrderNPNTransforms4(transforms);
432 llvm::BitVector initialized(
entries4.size(),
false);
433 auto isBetterEntry = [&](
const Entry4 &candidate,
const Entry4 ¤t) {
443 for (uint16_t representative : representatives) {
444 for (
const auto &transform : transforms) {
448 applyNPNTransform4(representative, transform.inverseOutputToSource,
449 transform.outputNegation);
457 if (!initialized.test(member) ||
458 isBetterEntry(candidate,
entries4[member])) {
460 initialized.set(member);
465 assert(initialized.all() &&
"expected to populate all 4-input NPN entries");
474 expandInputPermutation(entry.inputPermutation),
475 entry.inputNegation, entry.outputNegation);
481 llvm::SmallVectorImpl<unsigned> &permutation)
const {
483 "NPN classes must have the same number of inputs");
485 "NPN classes must be equivalent for input mapping");
498 permutation.push_back(thisInverse[canonicalPos]);
511 for (uint32_t negMask = 0; negMask < (1u << tt.
numInputs); ++negMask) {
515 auto permutation = identityPermutation(tt.
numInputs);
521 unsigned currentNegMask = permuteNegationMask(negMask, permutation);
524 for (
unsigned outputNegMask = 0; outputNegMask < (1u << tt.
numOutputs);
531 NPNClass candidate(candidateTT, permutation, currentNegMask,
537 canonical = std::move(candidate);
539 }
while (std::next_permutation(permutation.begin(), permutation.end()));
547 os <<
" Input permutation: [";
554 os <<
" Input negation: 0b";
559 os <<
" Output negation: 0b";
564 os <<
" Canonical truth table:\n";
578 {0x5555555555555555ULL, 0x3333333333333333ULL, 0x0F0F0F0F0F0F0F0FULL,
579 0x00FF00FF00FF00FFULL, 0x0000FFFF0000FFFFULL,
580 0x00000000FFFFFFFFULL},
581 {0xAAAAAAAAAAAAAAAAULL, 0xCCCCCCCCCCCCCCCCULL, 0xF0F0F0F0F0F0F0F0ULL,
582 0xFF00FF00FF00FF00ULL, 0xFFFF0000FFFF0000ULL,
583 0xFFFFFFFF00000000ULL},
589template <
bool positive>
591 assert(numVars <= 64 &&
"Number of variables too large");
592 assert(varIndex < numVars &&
"Variable index out of bounds");
593 uint64_t numBits = 1u << numVars;
598 uint64_t maskValue =
kVarMasks[positive][varIndex];
601 maskValue &= (1ULL << numBits) - 1;
602 return APInt(numBits, maskValue);
607 uint64_t patternWidth = 1u << (varIndex + 1);
608 APInt
pattern(patternWidth, 0);
611 uint64_t shift = 1u << varIndex;
612 if constexpr (positive) {
614 pattern.setBits(shift, patternWidth);
620 return APInt::getSplat(numBits,
pattern);
625 return createVarMaskImpl<true>(numVars, varIndex);
626 return createVarMaskImpl<false>(numVars, varIndex);
644std::pair<APInt, APInt>
646 assert(numVars <= 64 &&
"Number of variables too large");
647 assert(var < numVars &&
"Variable index out of bounds");
648 assert(f.getBitWidth() == (1u << numVars) &&
"Truth table size mismatch");
649 uint64_t numBits = 1u << numVars;
650 uint64_t shift = 1u << var;
656 APInt blockPattern = APInt::getLowBitsSet(2 * shift, shift);
657 APInt mask0 = APInt::getSplat(numBits, blockPattern);
658 APInt mask1 = mask0.shl(shift);
661 APInt selected0 = f & mask0;
662 APInt selected1 = f & mask1;
665 APInt cof0 = selected0 | selected0.shl(shift);
666 APInt cof1 = selected1 | selected1.lshr(shift);
677 for (
const auto &cube :
cubes) {
678 APInt cubeTT = ~APInt(1 <<
numVars, 0);
679 for (
unsigned i = 0; i <
numVars; ++i) {
680 if (cube.hasLiteral(i))
690 os <<
"SOPForm: " <<
numVars <<
" vars, " <<
cubes.size() <<
" cubes\n";
691 for (
const auto &cube :
cubes) {
693 for (
unsigned i = 0; i <
numVars; ++i) {
694 if (cube.mask & (1ULL << i)) {
695 os << ((cube.inverted & (1ULL << i)) ?
"!" :
"");
696 os <<
"x" << i <<
" ";
745APInt isopImpl(
const APInt &tt,
const APInt &dc,
unsigned numVars,
746 unsigned varIndex,
SOPForm &result) {
747 assert((tt & ~dc).isZero() &&
"tt must be subset of dc");
754 if (dc.isAllOnes()) {
755 result.
cubes.emplace_back();
766 APInt negCof, posCof, negDC, posDC;
767 for (
int i = varIndex - 1; i >= 0; --i) {
770 if (negCof != posCof || negDC != posDC) {
775 assert(var >= 0 &&
"No variable found in support");
778 size_t negBegin = result.
cubes.size();
779 APInt negCover = isopImpl(negCof & ~posDC, negDC, numVars, var, result);
780 size_t negEnd = result.
cubes.size();
783 APInt posCover = isopImpl(posCof & ~negDC, posDC, numVars, var, result);
784 size_t posEnd = result.
cubes.size();
787 APInt remaining = (negCof & ~negCover) | (posCof & ~posCover);
788 APInt sharedCover = isopImpl(remaining, negDC & posDC, numVars, var, result);
791 APInt negMask = createVarMaskImpl<false>(numVars, var);
792 APInt totalCover = sharedCover | (negCover & negMask) | (posCover & ~negMask);
795 for (
size_t i = negBegin; i < negEnd; ++i)
796 result.
cubes[i].setLiteral(var,
true);
798 for (
size_t i = negEnd; i < posEnd; ++i)
799 result.
cubes[i].setLiteral(var,
false);
807 assert((1u << numVars) == truthTable.getBitWidth() &&
808 "Truth table size must match 2^numVars");
811 if (numVars == 0 || truthTable.isZero())
814 (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