diff --git a/test/TableGen/ambiguous-composition.td b/test/TableGen/ambiguous-composition.td new file mode 100644 index 00000000000..da83466b4d0 --- /dev/null +++ b/test/TableGen/ambiguous-composition.td @@ -0,0 +1,92 @@ +// RUN: llvm-tblgen -gen-register-info -I %p/../../include %s 2>&1 | FileCheck %s +// +// CHECK-NOT: warning: SubRegIndex Test::subreg_h64 and Test::subreg_h32 compose ambiguously as Test::subreg_hh32 or Test::subreg_h32 +// CHECK: warning: SubRegIndex Test::subreg_l64 and Test::subreg_l32 compose ambiguously as Test::subreg_ll32 or Test::subreg_l32 + +include "llvm/Target/Target.td" + +def TestInstrInfo : InstrInfo { +} + +def Test : Target { + let InstructionSet = TestInstrInfo; +} + +let Namespace = "Test" in { + def subreg_l32 : SubRegIndex<32, 0>; + def subreg_h32 : SubRegIndex<32, 32>; + def subreg_h64 : SubRegIndex<64, 64>; + def subreg_l64 : SubRegIndex<64, 0>; + def subreg_hh32 : ComposedSubRegIndex; + def subreg_ll32 : ComposedSubRegIndex; +} + +class TestReg s> : RegisterWithSubRegs { + let Namespace = "Test"; +} + +// -------------------------------------------------------------------- +// A situation that previously caused the warning about ambiguous +// composition. +// +// The actual subregister actions are: +// subreg_h64: { F0Q->F0D V0Q->F0D } +// subreg_h32: { F0D->F0S F0Q->F2S V0Q->F0S } +// composition: { F0Q->F0S V0Q->F0S } (this is the same as subreg_hh32) +// +// For the register V0Q, subreg_hh32(V0Q) = subreg_h32(V0Q) = F0S, which +// would be enough to trigger the warning about ambiguous composition. +// However, for F0Q, subreg_hh32(F0Q) = F0S, while subreg_h32(F0Q) = F2S, +// which shows that there two subregister indices are different. +// Make sure that the warning is not emitted in this case. + +class FPR32 : TestReg { +} + +class FPR64 : TestReg { + let SubRegIndices = [subreg_h32]; +} + +class FPR128 : TestReg { + let SubRegIndices = [subreg_h64, subreg_h32]; +} + +class VPR128 : TestReg { + let SubRegIndices = [subreg_h64]; +} + +def F0S : FPR32<"f0s">; +def F1S : FPR32<"f1s">; +def F2S : FPR32<"f2s">; + +def F0D : FPR64<"f0d", F0S>; +def F0Q : FPR128<"f0q", F0D, F2S>; +def V0Q : VPR128<"v0q", F0D>; + +def FP32 : RegisterClass<"FP32", [f32], 32, (add F0S)>; +def FP64 : RegisterClass<"FP64", [f64], 64, (add F0D)>; +def FP128 : RegisterClass<"FP128", [v2f64], 128, (add F0Q)>; +def VP128 : RegisterClass<"VP128", [v2f64], 128, (add V0Q)>; + +// -------------------------------------------------------------------- +// A situation where the warning is legitimate. +// Make sure that the warning is still displayed. + +class GPR32 : TestReg { +} + +class GPR64 : TestReg { + let SubRegIndices = [subreg_l32]; +} + +class GPR128 : TestReg { + let SubRegIndices = [subreg_l64]; +} + +def G0S : GPR32<"g0s">; +def G0D : GPR64<"g0d", G0S>; +def G0Q : GPR128<"g0q", G0D>; + +def GP32 : RegisterClass<"GP32", [i32], 32, (add G0S)>; +def GP64 : RegisterClass<"GP64", [i64], 64, (add G0D)>; +def GP128 : RegisterClass<"GP128", [v2i64], 128, (add G0Q)>; diff --git a/utils/TableGen/CodeGenRegisters.cpp b/utils/TableGen/CodeGenRegisters.cpp index e70a79f08af..74a2b078dfb 100644 --- a/utils/TableGen/CodeGenRegisters.cpp +++ b/utils/TableGen/CodeGenRegisters.cpp @@ -20,6 +20,7 @@ #include "llvm/ADT/IntEqClasses.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" @@ -1309,6 +1310,55 @@ getConcatSubRegIndex(const SmallVector &Parts) { } void CodeGenRegBank::computeComposites() { + using RegMap = std::map; + + // Subreg -> { Reg->Reg }, where the right-hand side is the mapping from + // register to (sub)register associated with the action of the left-hand + // side subregister. + std::map SubRegAction; + for (const CodeGenRegister &R : Registers) { + const CodeGenRegister::SubRegMap &SM = R.getSubRegs(); + for (std::pair P : SM) + SubRegAction[P.first].insert({&R, P.second}); + } + + // Calculate the composition of two subregisters as compositions of their + // associated actions. + auto compose = [&SubRegAction] (const CodeGenSubRegIndex *Sub1, + const CodeGenSubRegIndex *Sub2) { + RegMap C; + const RegMap &Img1 = SubRegAction.at(Sub1); + const RegMap &Img2 = SubRegAction.at(Sub2); + for (std::pair P : Img1) { + auto F = Img2.find(P.second); + if (F != Img2.end()) + C.insert({P.first, F->second}); + } + return C; + }; + + // Check if the two maps agree on the intersection of their domains. + auto agree = [] (const RegMap &Map1, const RegMap &Map2) { + // Technically speaking, an empty map agrees with any other map, but + // this could flag false positives. We're interested in non-vacuous + // agreements. + if (Map1.empty() || Map2.empty()) + return false; + for (std::pair P : Map1) { + auto F = Map2.find(P.first); + if (F == Map2.end() || P.second != F->second) + return false; + } + return true; + }; + + using CompositePair = std::pair; + SmallSet UserDefined; + for (const CodeGenSubRegIndex &Idx : SubRegIndices) + for (auto P : Idx.getComposites()) + UserDefined.insert(std::make_pair(&Idx, P.first)); + // Keep track of TopoSigs visited. We only need to visit each TopoSig once, // and many registers will share TopoSigs on regular architectures. BitVector TopoSigs(getNumTopoSigs()); @@ -1341,11 +1391,15 @@ void CodeGenRegBank::computeComposites() { assert(Idx3 && "Sub-register doesn't have an index"); // Conflicting composition? Emit a warning but allow it. - if (CodeGenSubRegIndex *Prev = Idx1->addComposite(Idx2, Idx3)) - PrintWarning(Twine("SubRegIndex ") + Idx1->getQualifiedName() + - " and " + Idx2->getQualifiedName() + - " compose ambiguously as " + Prev->getQualifiedName() + - " or " + Idx3->getQualifiedName()); + if (CodeGenSubRegIndex *Prev = Idx1->addComposite(Idx2, Idx3)) { + // If the composition was not user-defined, always emit a warning. + if (!UserDefined.count({Idx1, Idx2}) || + agree(compose(Idx1, Idx2), SubRegAction.at(Idx3))) + PrintWarning(Twine("SubRegIndex ") + Idx1->getQualifiedName() + + " and " + Idx2->getQualifiedName() + + " compose ambiguously as " + Prev->getQualifiedName() + + " or " + Idx3->getQualifiedName()); + } } } }