-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[X86][GlobalIsel] Support IS_FP_CLASS intrinsic 1/4 #151084
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,7 @@ | |
#include "X86LegalizerInfo.h" | ||
#include "X86Subtarget.h" | ||
#include "X86TargetMachine.h" | ||
#include "llvm/CodeGen/CodeGenCommonISel.h" | ||
#include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h" | ||
#include "llvm/CodeGen/GlobalISel/LegalizerHelper.h" | ||
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" | ||
|
@@ -579,6 +580,7 @@ X86LegalizerInfo::X86LegalizerInfo(const X86Subtarget &STI, | |
getActionDefinitionsBuilder({G_DYN_STACKALLOC, G_STACKSAVE, G_STACKRESTORE}) | ||
.lower(); | ||
|
||
getActionDefinitionsBuilder(G_IS_FPCLASS).custom(); | ||
// fp intrinsics | ||
getActionDefinitionsBuilder(G_INTRINSIC_ROUNDEVEN) | ||
.scalarize(0) | ||
|
@@ -616,6 +618,8 @@ bool X86LegalizerInfo::legalizeCustom(LegalizerHelper &Helper, MachineInstr &MI, | |
return legalizeFPTOSI(MI, MRI, Helper); | ||
case TargetOpcode::G_GET_ROUNDING: | ||
return legalizeGETROUNDING(MI, MRI, Helper); | ||
case TargetOpcode::G_IS_FPCLASS: | ||
return legalizeIsFPClass(MI, MRI, Helper); | ||
} | ||
llvm_unreachable("expected switch to return"); | ||
} | ||
|
@@ -853,10 +857,236 @@ bool X86LegalizerInfo::legalizeGETROUNDING(MachineInstr &MI, | |
auto RetValTrunc = MIRBuilder.buildZExtOrTrunc(DstTy, RetVal); | ||
|
||
MIRBuilder.buildCopy(Dst, RetValTrunc); | ||
MI.eraseFromParent(); | ||
return true; | ||
} | ||
|
||
bool X86LegalizerInfo::expandFPClassTestForF32OrF64( | ||
MachineInstr &MI, MachineRegisterInfo &MRI, LegalizerHelper &Helper) const { | ||
MachineIRBuilder &MIRBuilder = Helper.MIRBuilder; | ||
auto [DstReg, DstTy, SrcReg, SrcTy] = MI.getFirst2RegLLTs(); | ||
FPClassTest Test = static_cast<FPClassTest>(MI.getOperand(2).getImm()); | ||
assert(!SrcTy.isVector() && "G_IS_FPCLASS does not support vectors yet"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should not be an assertion. You can always trivially scalarize in the rules, or return false to fallback |
||
const fltSemantics &Semantics = getFltSemanticForLLT(SrcTy.getScalarType()); | ||
|
||
// Some checks may be represented as inversion of simpler check, for example | ||
// "inf|normal|subnormal|zero" => !"nan". | ||
bool IsInverted = false; | ||
|
||
if (FPClassTest InvertedCheck = invertFPClassTestIfSimpler(Test, false)) { | ||
Test = InvertedCheck; | ||
IsInverted = true; | ||
} | ||
|
||
// In the general case use integer operations. | ||
unsigned BitSize = SrcTy.getScalarSizeInBits(); | ||
LLT IntVT = LLT::scalar(BitSize); | ||
MachineInstrBuilder OpAsInt = MIRBuilder.buildCopy(IntVT, SrcReg); | ||
|
||
// Various Mask | ||
APInt SignMask = APInt::getSignMask(BitSize); | ||
APInt ValueMask = APInt::getSignedMaxValue(BitSize); | ||
APInt Inf = APFloat::getInf(Semantics).bitcastToAPInt(); | ||
APInt InfPlus1 = Inf + 1; | ||
APInt ExpMask = Inf; | ||
APInt AllOneMantissa = APFloat::getLargest(Semantics).bitcastToAPInt() & ~Inf; | ||
APInt QNaNBitMask = | ||
APInt::getOneBitSet(BitSize, AllOneMantissa.getActiveBits() - 1); | ||
APInt InvertionMask = APInt::getAllOnes(DstTy.getScalarSizeInBits()); | ||
|
||
auto ValueMaskV = MIRBuilder.buildConstant(IntVT, ValueMask); | ||
auto SignBitV = MIRBuilder.buildConstant(IntVT, SignMask); | ||
auto ExpMaskV = MIRBuilder.buildConstant(IntVT, ExpMask); | ||
auto ZeroV = MIRBuilder.buildConstant(IntVT, 0); | ||
auto InfV = MIRBuilder.buildConstant(IntVT, Inf); | ||
auto InfPlus1V = MIRBuilder.buildConstant(IntVT, InfPlus1); | ||
auto ResultInvertedV = MIRBuilder.buildConstant(DstTy, InvertionMask); | ||
|
||
MachineInstrBuilder Res; | ||
const auto appendResult = [&](MachineInstrBuilder &PartialRes) { | ||
if (PartialRes.getInstr()) { | ||
if (Res.getInstr()) { | ||
Res = MIRBuilder.buildOr(DstTy, Res, PartialRes); | ||
} else { | ||
Res = PartialRes; | ||
} | ||
} | ||
}; | ||
// Split the value into sign bit and absolute value. | ||
auto AbsV = MIRBuilder.buildAnd(IntVT, OpAsInt, ValueMaskV); | ||
auto SignVDestReg = MRI.createGenericVirtualRegister(LLT::scalar(1)); | ||
auto SignV = | ||
MIRBuilder.buildICmp(CmpInst::ICMP_SLT, SignVDestReg, OpAsInt, ZeroV); | ||
|
||
// Tests that involve more than one class should be processed first. | ||
MachineInstrBuilder PartialRes; | ||
|
||
if ((Test & fcFinite) == fcFinite) { | ||
// finite(V) ==> abs(V) < exp_mask | ||
PartialRes = MIRBuilder.buildICmp( | ||
IsInverted ? CmpInst::ICMP_SGE : CmpInst::ICMP_SLT, | ||
MRI.createGenericVirtualRegister(LLT::scalar(1)), AbsV, ExpMaskV); | ||
Test &= ~fcFinite; | ||
} else if ((Test & fcFinite) == fcPosFinite) { | ||
// finite(V) && V > 0 ==> V < exp_mask | ||
PartialRes = MIRBuilder.buildICmp( | ||
IsInverted ? CmpInst::ICMP_UGE : CmpInst::ICMP_ULT, | ||
MRI.createGenericVirtualRegister(LLT::scalar(1)), OpAsInt, ExpMaskV); | ||
Test &= ~fcPosFinite; | ||
} else if ((Test & fcFinite) == fcNegFinite) { | ||
// finite(V) && V < 0 ==> abs(V) < exp_mask && signbit == 1 | ||
auto PartialResPart = MIRBuilder.buildICmp( | ||
CmpInst::ICMP_SLT, MRI.createGenericVirtualRegister(LLT::scalar(1)), | ||
AbsV, ExpMaskV); | ||
PartialRes = MIRBuilder.buildAnd(LLT::scalar(1), PartialResPart, SignV); | ||
Test &= ~fcNegFinite; | ||
} | ||
appendResult(PartialRes); | ||
|
||
if (FPClassTest PartialCheck = Test & (fcZero | fcSubnormal)) { | ||
// fcZero | fcSubnormal => test all exponent bits are 0 | ||
// TODO: Handle sign bit specific cases | ||
if (PartialCheck == (fcZero | fcSubnormal)) { | ||
auto ExpBits = MIRBuilder.buildAnd(IntVT, OpAsInt, ExpMaskV); | ||
auto ExpIsZero = MIRBuilder.buildICmp( | ||
CmpInst::ICMP_EQ, MRI.createGenericVirtualRegister(LLT::scalar(1)), | ||
ExpBits, ZeroV); | ||
appendResult(ExpIsZero); | ||
Test &= ~PartialCheck & fcAllFlags; | ||
} | ||
} | ||
|
||
// Check for individual classes. | ||
if (unsigned PartialCheck = Test & fcZero) { | ||
if (PartialCheck == fcPosZero) | ||
PartialRes = MIRBuilder.buildICmp( | ||
CmpInst::ICMP_EQ, MRI.createGenericVirtualRegister(LLT::scalar(1)), | ||
OpAsInt, ZeroV); | ||
else if (PartialCheck == fcZero) | ||
PartialRes = MIRBuilder.buildICmp( | ||
CmpInst::ICMP_EQ, MRI.createGenericVirtualRegister(LLT::scalar(1)), | ||
AbsV, ZeroV); | ||
else // ISD::fcNegZero | ||
PartialRes = MIRBuilder.buildICmp( | ||
CmpInst::ICMP_EQ, MRI.createGenericVirtualRegister(LLT::scalar(1)), | ||
OpAsInt, SignBitV); | ||
appendResult(PartialRes); | ||
} | ||
if (unsigned PartialCheck = Test & fcSubnormal) { | ||
assert("Not Supported yet!"); | ||
} | ||
if (unsigned PartialCheck = Test & fcInf) { | ||
if (PartialCheck == fcPosInf) | ||
PartialRes = MIRBuilder.buildICmp( | ||
IsInverted ? CmpInst::ICMP_NE : CmpInst::ICMP_EQ, | ||
MRI.createGenericVirtualRegister(LLT::scalar(1)), OpAsInt, InfV); | ||
else if (PartialCheck == fcInf) | ||
PartialRes = MIRBuilder.buildICmp( | ||
IsInverted ? CmpInst::ICMP_NE : CmpInst::ICMP_EQ, | ||
MRI.createGenericVirtualRegister(LLT::scalar(1)), AbsV, InfV); | ||
else { // ISD::fcNegInf | ||
APInt NegInf = APFloat::getInf(Semantics, true).bitcastToAPInt(); | ||
auto NegInfV = MIRBuilder.buildConstant(IntVT, NegInf); | ||
PartialRes = MIRBuilder.buildICmp( | ||
IsInverted ? CmpInst::ICMP_NE : CmpInst::ICMP_EQ, | ||
MRI.createGenericVirtualRegister(LLT::scalar(1)), OpAsInt, NegInfV); | ||
} | ||
MIRBuilder.buildCopy(DstReg, PartialRes); | ||
MI.eraseFromParent(); | ||
return true; | ||
} | ||
if (unsigned PartialCheck = Test & fcNan) { | ||
APInt InfWithQnanBit = Inf | QNaNBitMask; | ||
auto InfWithQnanBitV = MIRBuilder.buildConstant(IntVT, InfWithQnanBit); | ||
if (PartialCheck == fcNan) { | ||
// isnan(V) ==> abs(V) > int(inf) | ||
auto AbsDstReg = MRI.createGenericVirtualRegister(LLT::scalar(BitSize)); | ||
auto FAbsV = MIRBuilder.buildCopy(AbsDstReg, SrcReg); | ||
auto InfVDstReg = MRI.createGenericVirtualRegister(LLT::scalar(BitSize)); | ||
PartialRes = MIRBuilder.buildFCmp( | ||
CmpInst::FCMP_UEQ, MRI.createGenericVirtualRegister(LLT::scalar(1)), | ||
FAbsV, FAbsV); | ||
} else if (PartialCheck == fcQNan) { | ||
// isquiet(V) ==> abs(V) >= (unsigned(Inf) | quiet_bit) | ||
PartialRes = MIRBuilder.buildICmp( | ||
IsInverted ? CmpInst::ICMP_SLT : CmpInst::ICMP_SGE, | ||
MRI.createGenericVirtualRegister(LLT::scalar(1)), AbsV, | ||
InfWithQnanBitV); | ||
|
||
} else { // ISD::fcSNan | ||
// issignaling(V) ==> abs(V) > unsigned(Inf) && | ||
// abs(V) < (unsigned(Inf) | quiet_bit) | ||
auto IsNotQnan = MIRBuilder.buildICmp( | ||
CmpInst::ICMP_SLT, MRI.createGenericVirtualRegister(LLT::scalar(1)), | ||
AbsV, InfWithQnanBitV); | ||
auto IsNan = MIRBuilder.buildICmp( | ||
CmpInst::ICMP_SGE, MRI.createGenericVirtualRegister(LLT::scalar(1)), | ||
AbsV, InfPlus1V); | ||
PartialRes = MIRBuilder.buildAnd(LLT::scalar(1), IsNan, IsNotQnan); | ||
} | ||
MIRBuilder.buildCopy(DstReg, PartialRes); | ||
MI.eraseFromParent(); | ||
return true; | ||
} | ||
if (unsigned PartialCheck = Test & fcNormal) { | ||
assert("Not Supported yet!"); | ||
} | ||
if (unsigned PartialCheck = Test & fcSubnormal) { | ||
// subnormal(V) ==> abs(V) < exp_mask && signbit == 0 | ||
auto ExpBits = MIRBuilder.buildAnd(IntVT, OpAsInt, ExpMaskV); | ||
auto ExpIsZero = MIRBuilder.buildICmp( | ||
CmpInst::ICMP_EQ, MRI.createGenericVirtualRegister(LLT::scalar(1)), | ||
ExpBits, ZeroV); | ||
auto SignBit = MIRBuilder.buildICmp( | ||
CmpInst::ICMP_EQ, MRI.createGenericVirtualRegister(LLT::scalar(1)), | ||
SignV, ZeroV); | ||
PartialRes = MIRBuilder.buildAnd(LLT::scalar(1), ExpIsZero, SignBit); | ||
appendResult(PartialRes); | ||
} | ||
if (!Res.getInstr()) { | ||
Res = MIRBuilder.buildConstant(LLT::scalar(1), IsInverted); | ||
MIRBuilder.buildCopy(DstReg, Res); | ||
MI.eraseFromParent(); | ||
return true; | ||
} | ||
|
||
MIRBuilder.buildCopy(DstReg, Res); | ||
MI.eraseFromParent(); | ||
return true; | ||
} | ||
bool X86LegalizerInfo::expandFPClassTestForF80(MachineInstr &MI, | ||
MachineRegisterInfo &MRI, | ||
LegalizerHelper &Helper) const { | ||
return false; | ||
} | ||
|
||
bool X86LegalizerInfo::legalizeIsFPClass(MachineInstr &MI, | ||
MachineRegisterInfo &MRI, | ||
LegalizerHelper &Helper) const { | ||
MachineIRBuilder &MIRBuilder = Helper.MIRBuilder; | ||
auto [DstReg, DstTy, SrcReg, SrcTy] = MI.getFirst2RegLLTs(); | ||
assert(!SrcTy.isVector() && "G_IS_FPCLASS does not support vectors yet"); | ||
|
||
FPClassTest Mask = static_cast<FPClassTest>(MI.getOperand(2).getImm()); | ||
if (Mask == fcNone) { | ||
MIRBuilder.buildConstant(DstReg, 0); | ||
MI.eraseFromParent(); | ||
return true; | ||
} | ||
if (Mask == fcAllFlags) { | ||
MIRBuilder.buildConstant(DstReg, 1); | ||
MI.eraseFromParent(); | ||
return true; | ||
} | ||
bool IsF80 = (SrcTy == LLT::scalar(80)); | ||
// For f32/f64/f80 if NoFpException is set, we can use the FCMP | ||
// Some checks can be implemented using float comparisons, if floating point | ||
// exceptions are ignored. | ||
|
||
if (IsF80) | ||
return expandFPClassTestForF80(MI, MRI, Helper); | ||
return expandFPClassTestForF32OrF64(MI, MRI, Helper); | ||
} | ||
|
||
bool X86LegalizerInfo::legalizeIntrinsic(LegalizerHelper &Helper, | ||
MachineInstr &MI) const { | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like a rewritten version of
LegalizerHelper::lowerISFPCLASS
, do we have something x86 specific here?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Original version does not match with X86 outputs due SetCC combination, Algebraic Combination and CC rewrites.
While doing X86, I have created outputs those are close to DAG outputs.