Skip to content

Commit fdbff88

Browse files
authored
[RISCV][GISel] Add support for G_FCMP with F and D extensions. (#70624)
We only have instructions for OEQ, OLT, and OLE. We need to convert other comparison codes into those. I think we'll likely want to split this up in the future to support optimizations. Maybe do some of it in the legalizer or in a new post legalizer lowering pass. So this patch is just enough to get something working without adding 11 additional patterns to tablegen for each type.
1 parent aae30f9 commit fdbff88

File tree

9 files changed

+2897
-0
lines changed

9 files changed

+2897
-0
lines changed

llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ class RISCVInstructionSelector : public InstructionSelector {
6767
bool selectSExtInreg(MachineInstr &MI, MachineIRBuilder &MIB) const;
6868
bool selectSelect(MachineInstr &MI, MachineIRBuilder &MIB,
6969
MachineRegisterInfo &MRI) const;
70+
bool selectFPCompare(MachineInstr &MI, MachineIRBuilder &MIB,
71+
MachineRegisterInfo &MRI) const;
7072

7173
ComplexRendererFns selectShiftMask(MachineOperand &Root) const;
7274
ComplexRendererFns selectAddrRegImm(MachineOperand &Root) const;
@@ -415,6 +417,8 @@ bool RISCVInstructionSelector::select(MachineInstr &MI) {
415417
}
416418
case TargetOpcode::G_SELECT:
417419
return selectSelect(MI, MIB, MRI);
420+
case TargetOpcode::G_FCMP:
421+
return selectFPCompare(MI, MIB, MRI);
418422
default:
419423
return false;
420424
}
@@ -820,6 +824,130 @@ bool RISCVInstructionSelector::selectSelect(MachineInstr &MI,
820824
return constrainSelectedInstRegOperands(*Result, TII, TRI, RBI);
821825
}
822826

827+
// Convert an FCMP predicate to one of the supported F or D instructions.
828+
static unsigned getFCmpOpcode(CmpInst::Predicate Pred, unsigned Size) {
829+
assert((Size == 32 || Size == 64) && "Unsupported size");
830+
switch (Pred) {
831+
default:
832+
llvm_unreachable("Unsupported predicate");
833+
case CmpInst::FCMP_OLT:
834+
return Size == 32 ? RISCV::FLT_S : RISCV::FLT_D;
835+
case CmpInst::FCMP_OLE:
836+
return Size == 32 ? RISCV::FLE_S : RISCV::FLE_D;
837+
case CmpInst::FCMP_OEQ:
838+
return Size == 32 ? RISCV::FEQ_S : RISCV::FEQ_D;
839+
}
840+
}
841+
842+
// Try legalizing an FCMP by swapping or inverting the predicate to one that
843+
// is supported.
844+
static bool legalizeFCmpPredicate(Register &LHS, Register &RHS,
845+
CmpInst::Predicate &Pred, bool &NeedInvert) {
846+
auto isLegalFCmpPredicate = [](CmpInst::Predicate Pred) {
847+
return Pred == CmpInst::FCMP_OLT || Pred == CmpInst::FCMP_OLE ||
848+
Pred == CmpInst::FCMP_OEQ;
849+
};
850+
851+
assert(!isLegalFCmpPredicate(Pred) && "Predicate already legal?");
852+
853+
CmpInst::Predicate InvPred = CmpInst::getSwappedPredicate(Pred);
854+
if (isLegalFCmpPredicate(InvPred)) {
855+
Pred = InvPred;
856+
std::swap(LHS, RHS);
857+
return true;
858+
}
859+
860+
InvPred = CmpInst::getInversePredicate(Pred);
861+
NeedInvert = true;
862+
if (isLegalFCmpPredicate(InvPred)) {
863+
Pred = InvPred;
864+
return true;
865+
}
866+
InvPred = CmpInst::getSwappedPredicate(InvPred);
867+
if (isLegalFCmpPredicate(InvPred)) {
868+
Pred = InvPred;
869+
std::swap(LHS, RHS);
870+
return true;
871+
}
872+
873+
return false;
874+
}
875+
876+
// Emit a sequence of instructions to compare LHS and RHS using Pred. Return
877+
// the result in DstReg.
878+
// FIXME: Maybe we should expand this earlier.
879+
bool RISCVInstructionSelector::selectFPCompare(MachineInstr &MI,
880+
MachineIRBuilder &MIB,
881+
MachineRegisterInfo &MRI) const {
882+
auto &CmpMI = cast<GFCmp>(MI);
883+
CmpInst::Predicate Pred = CmpMI.getCond();
884+
885+
Register DstReg = CmpMI.getReg(0);
886+
Register LHS = CmpMI.getLHSReg();
887+
Register RHS = CmpMI.getRHSReg();
888+
889+
unsigned Size = MRI.getType(LHS).getSizeInBits();
890+
assert((Size == 32 || Size == 64) && "Unexpected size");
891+
892+
Register TmpReg = DstReg;
893+
894+
bool NeedInvert = false;
895+
// First try swapping operands or inverting.
896+
if (legalizeFCmpPredicate(LHS, RHS, Pred, NeedInvert)) {
897+
if (NeedInvert)
898+
TmpReg = MRI.createVirtualRegister(&RISCV::GPRRegClass);
899+
auto Cmp = MIB.buildInstr(getFCmpOpcode(Pred, Size), {TmpReg}, {LHS, RHS});
900+
if (!Cmp.constrainAllUses(TII, TRI, RBI))
901+
return false;
902+
} else if (Pred == CmpInst::FCMP_ONE || Pred == CmpInst::FCMP_UEQ) {
903+
// fcmp one LHS, RHS => (OR (FLT LHS, RHS), (FLT RHS, LHS))
904+
NeedInvert = Pred == CmpInst::FCMP_UEQ;
905+
auto Cmp1 = MIB.buildInstr(getFCmpOpcode(CmpInst::FCMP_OLT, Size),
906+
{&RISCV::GPRRegClass}, {LHS, RHS});
907+
if (!Cmp1.constrainAllUses(TII, TRI, RBI))
908+
return false;
909+
auto Cmp2 = MIB.buildInstr(getFCmpOpcode(CmpInst::FCMP_OLT, Size),
910+
{&RISCV::GPRRegClass}, {RHS, LHS});
911+
if (!Cmp2.constrainAllUses(TII, TRI, RBI))
912+
return false;
913+
if (NeedInvert)
914+
TmpReg = MRI.createVirtualRegister(&RISCV::GPRRegClass);
915+
auto Or =
916+
MIB.buildInstr(RISCV::OR, {TmpReg}, {Cmp1.getReg(0), Cmp2.getReg(0)});
917+
if (!Or.constrainAllUses(TII, TRI, RBI))
918+
return false;
919+
} else if (Pred == CmpInst::FCMP_ORD || Pred == CmpInst::FCMP_UNO) {
920+
// fcmp ord LHS, RHS => (AND (FEQ LHS, LHS), (FEQ RHS, RHS))
921+
// FIXME: If LHS and RHS are the same we can use a single FEQ.
922+
NeedInvert = Pred == CmpInst::FCMP_UNO;
923+
auto Cmp1 = MIB.buildInstr(getFCmpOpcode(CmpInst::FCMP_OEQ, Size),
924+
{&RISCV::GPRRegClass}, {LHS, LHS});
925+
if (!Cmp1.constrainAllUses(TII, TRI, RBI))
926+
return false;
927+
auto Cmp2 = MIB.buildInstr(getFCmpOpcode(CmpInst::FCMP_OEQ, Size),
928+
{&RISCV::GPRRegClass}, {RHS, RHS});
929+
if (!Cmp2.constrainAllUses(TII, TRI, RBI))
930+
return false;
931+
if (NeedInvert)
932+
TmpReg = MRI.createVirtualRegister(&RISCV::GPRRegClass);
933+
auto And =
934+
MIB.buildInstr(RISCV::AND, {TmpReg}, {Cmp1.getReg(0), Cmp2.getReg(0)});
935+
if (!And.constrainAllUses(TII, TRI, RBI))
936+
return false;
937+
} else
938+
llvm_unreachable("Unhandled predicate");
939+
940+
// Emit an XORI to invert the result if needed.
941+
if (NeedInvert) {
942+
auto Xor = MIB.buildInstr(RISCV::XORI, {DstReg}, {TmpReg}).addImm(1);
943+
if (!Xor.constrainAllUses(TII, TRI, RBI))
944+
return false;
945+
}
946+
947+
MI.eraseFromParent();
948+
return true;
949+
}
950+
823951
namespace llvm {
824952
InstructionSelector *
825953
createRISCVInstructionSelector(const RISCVTargetMachine &TM,

llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,14 @@ RISCVLegalizerInfo::RISCVLegalizerInfo(const RISCVSubtarget &ST) {
215215
typeIs(1, s32)(Query));
216216
});
217217

218+
getActionDefinitionsBuilder(G_FCMP)
219+
.legalIf([=, &ST](const LegalityQuery &Query) -> bool {
220+
return typeIs(0, sXLen)(Query) &&
221+
((ST.hasStdExtF() && typeIs(1, s32)(Query)) ||
222+
(ST.hasStdExtD() && typeIs(1, s64)(Query)));
223+
})
224+
.clampScalar(0, sXLen, sXLen);
225+
218226
getActionDefinitionsBuilder(G_FCONSTANT)
219227
.legalIf([=, &ST](const LegalityQuery &Query) -> bool {
220228
return (ST.hasStdExtF() && typeIs(0, s32)(Query)) ||

llvm/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,17 @@ RISCVRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
235235
getOperandsMapping({getFPValueMapping(Ty.getSizeInBits()), nullptr});
236236
break;
237237
}
238+
case TargetOpcode::G_FCMP: {
239+
LLT Ty = MRI.getType(MI.getOperand(2).getReg());
240+
241+
unsigned Size = Ty.getSizeInBits();
242+
assert((Size == 32 || Size == 64) && "Unsupported size for G_FCMP");
243+
244+
auto *FPRValueMapping = getFPValueMapping(Size);
245+
OperandsMapping = getOperandsMapping(
246+
{GPRValueMapping, nullptr, FPRValueMapping, FPRValueMapping});
247+
break;
248+
}
238249
default:
239250
return getInvalidInstructionMapping();
240251
}

0 commit comments

Comments
 (0)