| //===--- CodeGenHwModes.cpp -----------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // Classes to parse and store HW mode information for instruction selection | |
| //===----------------------------------------------------------------------===// | |
| #include "CodeGenHwModes.h" | |
| #include "SubtargetFeatureInfo.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include "llvm/TableGen/Error.h" | |
| #include "llvm/TableGen/Record.h" | |
| #include <algorithm> | |
| #include <set> | |
| using namespace llvm; | |
| #define DEBUG_TYPE "codegen-hwmodes" | |
| StringRef CodeGenHwModes::DefaultModeName = "DefaultMode"; | |
| HwMode::HwMode(const Record *R) { | |
| Name = R->getName(); | |
| Predicates = R->getValueAsListOfDefs("Predicates"); | |
| } | |
| LLVM_DUMP_METHOD | |
| void HwMode::dump() const { | |
| dbgs() << Name << ": "; | |
| ListSeparator LS; | |
| for (const Record *R : Predicates) | |
| dbgs() << LS << R->getName(); | |
| dbgs() << '\n'; | |
| } | |
| HwModeSelect::HwModeSelect(const Record *R, CodeGenHwModes &CGH) { | |
| std::vector<const Record *> Modes = R->getValueAsListOfDefs("Modes"); | |
| std::vector<const Record *> Objects = R->getValueAsListOfDefs("Objects"); | |
| for (auto [Mode, Object] : zip_equal(Modes, Objects)) { | |
| unsigned ModeId = CGH.getHwModeId(Mode); | |
| Items.emplace_back(ModeId, Object); | |
| } | |
| } | |
| LLVM_DUMP_METHOD | |
| void HwModeSelect::dump() const { | |
| dbgs() << '{'; | |
| for (const PairType &P : Items) | |
| dbgs() << " (" << P.first << ',' << P.second->getName() << ')'; | |
| dbgs() << " }\n"; | |
| } | |
| // Helper to represent predicates semantically. | |
| HwModePredicates::HwModePredicates(ArrayRef<const Record *> Preds) { | |
| HasNonAsmPredicates = | |
| !getRequiredFeatures(FeaturesSet, AnyOfFeatureSets, Preds); | |
| } | |
| // DefaultMode is implicitly active only when *none* of the other target | |
| // HwModes are active. | |
| // | |
| // Mathematically: DefaultMode = !(Mode_1 || Mode_2 || ... || Mode_N) | |
| // = !Mode_1 && !Mode_2 && ... && !Mode_N | |
| // | |
| // If Mode_1 requires (FeatureA && FeatureB), then !Mode_1 requires | |
| // (!FeatureA || !FeatureB). We construct these implicit OR-sets for all | |
| // other modes and add them to DefaultMode's requirements. | |
| HwModePredicates | |
| HwModePredicates::createForDefaultMode(const CodeGenHwModes &CGH) { | |
| HwModePredicates SP; | |
| for (unsigned M = 1; M < CGH.getNumModeIds(); ++M) { | |
| const HwMode &Mode = CGH.getMode(M); | |
| HwModePredicates ModePreds(Mode.Predicates); | |
| if (ModePreds.HasNonAsmPredicates) { | |
| LLVM_DEBUG(dbgs().indent(2) | |
| << "Warning: HwMode '" << Mode.Name | |
| << "' has non-assembler predicates. Ignoring for " | |
| "DefaultMode negation.\n"); | |
| SP.HasNonAsmPredicates = true; | |
| continue; | |
| } | |
| // Negate the features in FeaturesSet: !(F1 && F2) = !F1 || !F2 | |
| std::set<SubtargetFeatureLiteral> NegatedFeatures; | |
| for (const auto &Op : ModePreds.FeaturesSet) { | |
| NegatedFeatures.insert({Op.Feature, !Op.IsNot}); | |
| } | |
| if (ModePreds.AnyOfFeatureSets.empty()) { | |
| if (!NegatedFeatures.empty()) | |
| SP.AnyOfFeatureSets.insert(std::move(NegatedFeatures)); | |
| } else if (ModePreds.AnyOfFeatureSets.size() == 1) { | |
| // Negate a mode with a single OR-set: | |
| // !(F1 && F2 && (L1 || L2)) = !F1 || !F2 || (!L1 && !L2) | |
| // In CNF: (!F1 || !F2 || !L1) && (!F1 || !F2 || !L2) | |
| const auto &OrSet = *ModePreds.AnyOfFeatureSets.begin(); | |
| for (const auto &Op : OrSet) { | |
| std::set<SubtargetFeatureLiteral> NewOrSet = NegatedFeatures; | |
| NewOrSet.insert({Op.Feature, !Op.IsNot}); | |
| SP.AnyOfFeatureSets.insert(std::move(NewOrSet)); | |
| } | |
| } else { | |
| LLVM_DEBUG(dbgs().indent(2) | |
| << "Warning: HwMode '" << Mode.Name | |
| << "' has multiple complex predicates. Ignoring for " | |
| "DefaultMode negation.\n"); | |
| } | |
| } | |
| return SP; | |
| } | |
| void HwModePredicates::add(const HwModePredicates &Other) { | |
| FeaturesSet.insert(Other.FeaturesSet.begin(), Other.FeaturesSet.end()); | |
| AnyOfFeatureSets.insert(Other.AnyOfFeatureSets.begin(), | |
| Other.AnyOfFeatureSets.end()); | |
| HasNonAsmPredicates |= Other.HasNonAsmPredicates; | |
| } | |
| // Evaluates if the current set of predicates contains a contradiction. | |
| // Performs unit propagation: if we have a known feature F, we can simplify | |
| // OR-sets (A || B) containing F or !F. | |
| // Returns true if the OrSet is satisfied, false otherwise. | |
| // If not satisfied, fills SimplifiedSet with remaining active literals. | |
| static bool simplifyOrSet(const std::set<SubtargetFeatureLiteral> &OrSet, | |
| const std::set<SubtargetFeatureLiteral> &FeaturesSet, | |
| std::set<SubtargetFeatureLiteral> &SimplifiedSet) { | |
| for (const auto &Lit : OrSet) { | |
| if (FeaturesSet.count(Lit)) | |
| return true; // Satisfied | |
| if (!FeaturesSet.count({Lit.Feature, !Lit.IsNot})) | |
| SimplifiedSet.insert(Lit); | |
| } | |
| return false; | |
| } | |
| // Evaluates if the current set of predicates contains a contradiction. | |
| // Performs unit propagation: if we have a known feature F, we can simplify | |
| // OR-sets (A || B) containing F or !F. | |
| bool HwModePredicates::isSelfContradictory() { | |
| // Check for immediate contradictions (e.g. requiring both F and !F). | |
| for (const auto &Lit : FeaturesSet) { | |
| if (FeaturesSet.count({Lit.Feature, !Lit.IsNot})) | |
| return true; | |
| } | |
| std::set<std::set<SubtargetFeatureLiteral>> NewAnyOfs; | |
| bool FeaturesChanged = false; | |
| for (const auto &OrSet : AnyOfFeatureSets) { | |
| std::set<SubtargetFeatureLiteral> SimplifiedSet; | |
| if (simplifyOrSet(OrSet, FeaturesSet, SimplifiedSet)) | |
| continue; // Discard satisfied set | |
| // All literals in this OR-set are false, causing a contradiction. | |
| if (SimplifiedSet.empty()) | |
| return true; | |
| if (SimplifiedSet.size() == 1) { | |
| // Unit clause: only one choice remains, so it must be true. | |
| FeaturesSet.insert(*SimplifiedSet.begin()); | |
| FeaturesChanged = true; | |
| } else { | |
| NewAnyOfs.insert(std::move(SimplifiedSet)); | |
| } | |
| } | |
| if (FeaturesChanged) { | |
| AnyOfFeatureSets = std::move(NewAnyOfs); | |
| return isSelfContradictory(); | |
| } | |
| return false; | |
| } | |
| // Two predicate sets conflict if their union is self-contradictory. | |
| bool HwModePredicates::conflictsWith(const HwModePredicates &Other) const { | |
| HwModePredicates Combined(*this); | |
| Combined.add(Other); | |
| return Combined.isSelfContradictory(); | |
| } | |
| CodeGenHwModes::CodeGenHwModes(const RecordKeeper &RK) : Records(RK) { | |
| for (const Record *R : Records.getAllDerivedDefinitions("HwMode")) { | |
| // The default mode needs a definition in the .td sources for TableGen | |
| // to accept references to it. We need to ignore the definition here. | |
| if (R->getName() == DefaultModeName) | |
| continue; | |
| Modes.emplace_back(R); | |
| ModeIds.try_emplace(R, Modes.size()); | |
| } | |
| assert(Modes.size() <= 32 && "number of HwModes exceeds maximum of 32"); | |
| for (const Record *R : Records.getAllDerivedDefinitions("HwModeSelect")) { | |
| auto P = ModeSelects.emplace(R, HwModeSelect(R, *this)); | |
| assert(P.second); | |
| (void)P; | |
| } | |
| } | |
| const HwModePredicates & | |
| CodeGenHwModes::getModePredicates(unsigned ModeId) const { | |
| if (PredicatesByMode.empty()) { | |
| PredicatesByMode.resize(getNumModeIds()); | |
| PredicatesByMode[DefaultMode] = | |
| HwModePredicates::createForDefaultMode(*this); | |
| for (unsigned M = 1; M < getNumModeIds(); ++M) | |
| PredicatesByMode[M] = HwModePredicates(getMode(M).Predicates); | |
| } | |
| return PredicatesByMode[ModeId]; | |
| } | |
| unsigned CodeGenHwModes::getHwModeId(const Record *R) const { | |
| if (R->getName() == DefaultModeName) | |
| return DefaultMode; | |
| auto F = ModeIds.find(R); | |
| assert(F != ModeIds.end() && "Unknown mode name"); | |
| return F->second; | |
| } | |
| const HwModeSelect &CodeGenHwModes::getHwModeSelect(const Record *R) const { | |
| auto F = ModeSelects.find(R); | |
| assert(F != ModeSelects.end() && "Record is not a \"mode select\""); | |
| return F->second; | |
| } | |
| LLVM_DUMP_METHOD | |
| void CodeGenHwModes::dump() const { | |
| dbgs() << "Modes: {\n"; | |
| for (const HwMode &M : Modes) { | |
| dbgs() << " "; | |
| M.dump(); | |
| } | |
| dbgs() << "}\n"; | |
| dbgs() << "ModeIds: {\n"; | |
| for (const auto &P : ModeIds) | |
| dbgs() << " " << P.first->getName() << " -> " << P.second << '\n'; | |
| dbgs() << "}\n"; | |
| dbgs() << "ModeSelects: {\n"; | |
| for (const auto &P : ModeSelects) { | |
| dbgs() << " " << P.first->getName() << " -> "; | |
| P.second.dump(); | |
| } | |
| dbgs() << "}\n"; | |
| } | |
| // Resolves a HwModeSelect record based on pattern predicates. | |
| // Returns a unique resolved record if compatible, or nullptr if ambiguous. | |
| const Record * | |
| CodeGenHwModes::resolveModeSelect(const Record *SelectRec, | |
| ArrayRef<const Record *> PatPreds) const { | |
| if (!SelectRec->isSubClassOf("HwModeSelect")) | |
| return SelectRec; | |
| LLVM_DEBUG(dbgs() << "Trying to resolve HwModeSelect '" | |
| << SelectRec->getName() << "'\n"); | |
| const HwModeSelect &MS = getHwModeSelect(SelectRec); | |
| std::set<const Record *> ResolvedObjects; | |
| // Construct and parse the pattern predicates ONCE here | |
| HwModePredicates PatPredsSet(PatPreds); | |
| for (const auto &[ModeId, Obj] : MS.Items) { | |
| const HwModePredicates &ModePreds = getModePredicates(ModeId); | |
| if (!ModePreds.conflictsWith(PatPredsSet)) { | |
| LLVM_DEBUG(dbgs() << " HwMode '" << getModeName(ModeId, true) | |
| << "' is compatible -> " << Obj->getName() << "\n"); | |
| ResolvedObjects.insert(Obj); | |
| } else { | |
| LLVM_DEBUG(dbgs() << " HwMode '" << getModeName(ModeId, true) | |
| << "' is incompatible due to semantic conflict\n"); | |
| } | |
| } | |
| if (ResolvedObjects.size() == 1) { | |
| const Record *Resolved = *ResolvedObjects.begin(); | |
| LLVM_DEBUG(dbgs() << " Resolved to unique object: " << Resolved->getName() | |
| << "\n"); | |
| return Resolved; | |
| } | |
| if (ResolvedObjects.empty()) { | |
| LLVM_DEBUG(dbgs() << " No active modes resolved for '" | |
| << SelectRec->getName() << "'\n"); | |
| } else { | |
| LLVM_DEBUG( | |
| dbgs() << " Multiple active modes resolved to different objects for '" | |
| << SelectRec->getName() << "'\n"); | |
| } | |
| return nullptr; | |
| } |