mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 11:13:28 +01:00
[aarch64] Support APInt and APFloat in ImmLeaf subclasses and make AArch64 use them.
Summary: The purpose of this patch is to expose more information about ImmLeaf-like PatLeaf's so that GlobalISel can learn to import them. Previously, ImmLeaf could only be used to test int64_t's produced by sign-extending an APInt. Other tests on immediates had to use the generic PatLeaf and extract the constant using C++. With this patch, tablegen will know how to generate predicates for APInt, and APFloat. This will allow it to 'do the right thing' for both SelectionDAG and GlobalISel which require different methods of extracting the immediate from the IR. This is NFC for SelectionDAG since the new code is equivalent to the previous code. It's also NFC for FastISel because FastIselShouldIgnore is 1 for the ImmLeaf subclasses. Enabling FastIselShouldIgnore == 0 for these new subclasses will require a significant re-factor of FastISel. For GlobalISel, it's currently NFC because the relevant code to import the affected rules is not yet present. This will be added in a later patch. Depends on D36086 Reviewers: ab, t.p.northover, qcolombet, rovka, aditya_nandakumar Reviewed By: qcolombet Subscribers: bjope, aemerson, rengolin, javed.absar, igorb, llvm-commits, kristof.beyls Differential Revision: https://reviews.llvm.org/D36534 llvm-svn: 315747
This commit is contained in:
parent
a3c69c204d
commit
4c2aa5c732
@ -1119,6 +1119,21 @@ public:
|
||||
llvm_unreachable("Unexpected semantics");
|
||||
}
|
||||
|
||||
/// We don't rely on operator== working on double values, as
|
||||
/// it returns true for things that are clearly not equal, like -0.0 and 0.0.
|
||||
/// As such, this method can be used to do an exact bit-for-bit comparison of
|
||||
/// two floating point values.
|
||||
///
|
||||
/// We leave the version with the double argument here because it's just so
|
||||
/// convenient to write "2.0" and the like. Without this function we'd
|
||||
/// have to duplicate its logic everywhere it's called.
|
||||
bool isExactlyValue(double V) const {
|
||||
bool ignored;
|
||||
APFloat Tmp(V);
|
||||
Tmp.convert(getSemantics(), APFloat::rmNearestTiesToEven, &ignored);
|
||||
return bitwiseIsEqual(Tmp);
|
||||
}
|
||||
|
||||
unsigned int convertToHexString(char *DST, unsigned int HexDigits,
|
||||
bool UpperCase, roundingMode RM) const {
|
||||
APFLOAT_DISPATCH_ON_SEMANTICS(
|
||||
|
@ -1490,11 +1490,7 @@ public:
|
||||
/// convenient to write "2.0" and the like. Without this function we'd
|
||||
/// have to duplicate its logic everywhere it's called.
|
||||
bool isExactlyValue(double V) const {
|
||||
bool ignored;
|
||||
APFloat Tmp(V);
|
||||
Tmp.convert(Value->getValueAPF().getSemantics(),
|
||||
APFloat::rmNearestTiesToEven, &ignored);
|
||||
return isExactlyValue(Tmp);
|
||||
return Value->getValueAPF().isExactlyValue(V);
|
||||
}
|
||||
bool isExactlyValue(const APFloat& V) const;
|
||||
|
||||
|
@ -676,12 +676,41 @@ class PatLeaf<dag frag, code pred = [{}], SDNodeXForm xform = NOOP_SDNodeXForm>
|
||||
// If FastIsel should ignore all instructions that have an operand of this type,
|
||||
// the FastIselShouldIgnore flag can be set. This is an optimization to reduce
|
||||
// the code size of the generated fast instruction selector.
|
||||
class ImmLeaf<ValueType vt, code pred, SDNodeXForm xform = NOOP_SDNodeXForm>
|
||||
: PatFrag<(ops), (vt imm), [{}], xform> {
|
||||
class ImmLeaf<ValueType vt, code pred, SDNodeXForm xform = NOOP_SDNodeXForm,
|
||||
SDNode ImmNode = imm>
|
||||
: PatFrag<(ops), (vt ImmNode), [{}], xform> {
|
||||
let ImmediateCode = pred;
|
||||
bit FastIselShouldIgnore = 0;
|
||||
|
||||
// Is the data type of the immediate an APInt?
|
||||
bit IsAPInt = 0;
|
||||
|
||||
// Is the data type of the immediate an APFloat?
|
||||
bit IsAPFloat = 0;
|
||||
}
|
||||
|
||||
// An ImmLeaf except that Imm is an APInt. This is useful when you need to
|
||||
// zero-extend the immediate instead of sign-extend it.
|
||||
//
|
||||
// Note that FastISel does not currently understand IntImmLeaf and will not
|
||||
// generate code for rules that make use of it. As such, it does not make sense
|
||||
// to replace ImmLeaf with IntImmLeaf. However, replacing PatLeaf with an
|
||||
// IntImmLeaf will allow GlobalISel to import the rule.
|
||||
class IntImmLeaf<ValueType vt, code pred, SDNodeXForm xform = NOOP_SDNodeXForm>
|
||||
: ImmLeaf<vt, pred, xform> {
|
||||
let IsAPInt = 1;
|
||||
let FastIselShouldIgnore = 1;
|
||||
}
|
||||
|
||||
// An ImmLeaf except that Imm is an APFloat.
|
||||
//
|
||||
// Note that FastISel does not currently understand FPImmLeaf and will not
|
||||
// generate code for rules that make use of it.
|
||||
class FPImmLeaf<ValueType vt, code pred, SDNodeXForm xform = NOOP_SDNodeXForm>
|
||||
: ImmLeaf<vt, pred, xform, fpimm> {
|
||||
let IsAPFloat = 1;
|
||||
let FastIselShouldIgnore = 1;
|
||||
}
|
||||
|
||||
// Leaf fragments.
|
||||
|
||||
|
@ -500,14 +500,14 @@ let DiagnosticType = "LogicalSecondSource" in {
|
||||
let Name = "LogicalImm64Not";
|
||||
}
|
||||
}
|
||||
def logical_imm32 : Operand<i32>, PatLeaf<(imm), [{
|
||||
return AArch64_AM::isLogicalImmediate(N->getZExtValue(), 32);
|
||||
def logical_imm32 : Operand<i32>, IntImmLeaf<i32, [{
|
||||
return AArch64_AM::isLogicalImmediate(Imm.getZExtValue(), 32);
|
||||
}], logical_imm32_XFORM> {
|
||||
let PrintMethod = "printLogicalImm32";
|
||||
let ParserMatchClass = LogicalImm32Operand;
|
||||
}
|
||||
def logical_imm64 : Operand<i64>, PatLeaf<(imm), [{
|
||||
return AArch64_AM::isLogicalImmediate(N->getZExtValue(), 64);
|
||||
def logical_imm64 : Operand<i64>, IntImmLeaf<i64, [{
|
||||
return AArch64_AM::isLogicalImmediate(Imm.getZExtValue(), 64);
|
||||
}], logical_imm64_XFORM> {
|
||||
let PrintMethod = "printLogicalImm64";
|
||||
let ParserMatchClass = LogicalImm64Operand;
|
||||
@ -754,8 +754,8 @@ class arith_extended_reg32to64<ValueType Ty> : Operand<Ty>,
|
||||
|
||||
// Floating-point immediate.
|
||||
def fpimm16 : Operand<f16>,
|
||||
PatLeaf<(f16 fpimm), [{
|
||||
return AArch64_AM::getFP16Imm(N->getValueAPF()) != -1;
|
||||
FPImmLeaf<f16, [{
|
||||
return AArch64_AM::getFP16Imm(Imm) != -1;
|
||||
}], SDNodeXForm<fpimm, [{
|
||||
APFloat InVal = N->getValueAPF();
|
||||
uint32_t enc = AArch64_AM::getFP16Imm(InVal);
|
||||
@ -765,8 +765,8 @@ def fpimm16 : Operand<f16>,
|
||||
let PrintMethod = "printFPImmOperand";
|
||||
}
|
||||
def fpimm32 : Operand<f32>,
|
||||
PatLeaf<(f32 fpimm), [{
|
||||
return AArch64_AM::getFP32Imm(N->getValueAPF()) != -1;
|
||||
FPImmLeaf<f32, [{
|
||||
return AArch64_AM::getFP32Imm(Imm) != -1;
|
||||
}], SDNodeXForm<fpimm, [{
|
||||
APFloat InVal = N->getValueAPF();
|
||||
uint32_t enc = AArch64_AM::getFP32Imm(InVal);
|
||||
@ -776,8 +776,8 @@ def fpimm32 : Operand<f32>,
|
||||
let PrintMethod = "printFPImmOperand";
|
||||
}
|
||||
def fpimm64 : Operand<f64>,
|
||||
PatLeaf<(f64 fpimm), [{
|
||||
return AArch64_AM::getFP64Imm(N->getValueAPF()) != -1;
|
||||
FPImmLeaf<f64, [{
|
||||
return AArch64_AM::getFP64Imm(Imm) != -1;
|
||||
}], SDNodeXForm<fpimm, [{
|
||||
APFloat InVal = N->getValueAPF();
|
||||
uint32_t enc = AArch64_AM::getFP64Imm(InVal);
|
||||
@ -792,8 +792,8 @@ def fpimm8 : Operand<i32> {
|
||||
let PrintMethod = "printFPImmOperand";
|
||||
}
|
||||
|
||||
def fpimm0 : PatLeaf<(fpimm), [{
|
||||
return N->isExactlyValue(+0.0);
|
||||
def fpimm0 : FPImmLeaf<fAny, [{
|
||||
return Imm.isExactlyValue(+0.0);
|
||||
}]>;
|
||||
|
||||
// Vector lane operands
|
||||
@ -847,10 +847,9 @@ def VectorIndexD : Operand<i64>, ImmLeaf<i64, [{
|
||||
// aaaaaaaa bbbbbbbb cccccccc dddddddd eeeeeeee ffffffff gggggggg hhhhhhhh
|
||||
// are encoded as the eight bit value 'abcdefgh'.
|
||||
def simdimmtype10 : Operand<i32>,
|
||||
PatLeaf<(f64 fpimm), [{
|
||||
return AArch64_AM::isAdvSIMDModImmType10(N->getValueAPF()
|
||||
.bitcastToAPInt()
|
||||
.getZExtValue());
|
||||
FPImmLeaf<f64, [{
|
||||
return AArch64_AM::isAdvSIMDModImmType10(
|
||||
Imm.bitcastToAPInt().getZExtValue());
|
||||
}], SDNodeXForm<fpimm, [{
|
||||
APFloat InVal = N->getValueAPF();
|
||||
uint32_t enc = AArch64_AM::encodeAdvSIMDModImmType10(N->getValueAPF()
|
||||
|
@ -862,6 +862,24 @@ std::string TreePredicateFn::getImmCode() const {
|
||||
return PatFragRec->getRecord()->getValueAsString("ImmediateCode");
|
||||
}
|
||||
|
||||
bool TreePredicateFn::immCodeUsesAPInt() const {
|
||||
return getOrigPatFragRecord()->getRecord()->getValueAsBit("IsAPInt");
|
||||
}
|
||||
|
||||
bool TreePredicateFn::immCodeUsesAPFloat() const {
|
||||
bool Unset;
|
||||
// The return value will be false when IsAPFloat is unset.
|
||||
return getOrigPatFragRecord()->getRecord()->getValueAsBitOrUnset("IsAPFloat",
|
||||
Unset);
|
||||
}
|
||||
|
||||
std::string TreePredicateFn::getImmType() const {
|
||||
if (immCodeUsesAPInt())
|
||||
return "const APInt &";
|
||||
if (immCodeUsesAPFloat())
|
||||
return "const APFloat &";
|
||||
return "int64_t";
|
||||
}
|
||||
|
||||
/// isAlwaysTrue - Return true if this is a noop predicate.
|
||||
bool TreePredicateFn::isAlwaysTrue() const {
|
||||
@ -882,8 +900,13 @@ std::string TreePredicateFn::getCodeToRunOnSDNode() const {
|
||||
// Handle immediate predicates first.
|
||||
std::string ImmCode = getImmCode();
|
||||
if (!ImmCode.empty()) {
|
||||
std::string Result =
|
||||
" int64_t Imm = cast<ConstantSDNode>(Node)->getSExtValue();\n";
|
||||
std::string Result = " " + getImmType() + " Imm = ";
|
||||
if (immCodeUsesAPFloat())
|
||||
Result += "cast<ConstantFPSDNode>(Node)->getValueAPF();\n";
|
||||
else if (immCodeUsesAPInt())
|
||||
Result += "cast<ConstantSDNode>(Node)->getAPIntValue();\n";
|
||||
else
|
||||
Result += "cast<ConstantSDNode>(Node)->getSExtValue();\n";
|
||||
return Result + ImmCode;
|
||||
}
|
||||
|
||||
|
@ -475,9 +475,14 @@ public:
|
||||
/// appropriate.
|
||||
std::string getCodeToRunOnSDNode() const;
|
||||
|
||||
/// Get the data type of the argument to getImmediatePredicateCode().
|
||||
std::string getImmType() const;
|
||||
|
||||
private:
|
||||
std::string getPredCode() const;
|
||||
std::string getImmCode() const;
|
||||
bool immCodeUsesAPInt() const;
|
||||
bool immCodeUsesAPFloat() const;
|
||||
};
|
||||
|
||||
|
||||
|
@ -1913,6 +1913,9 @@ private:
|
||||
importImplicitDefRenderers(BuildMIAction &DstMIBuilder,
|
||||
const std::vector<Record *> &ImplicitDefs) const;
|
||||
|
||||
void emitImmPredicates(raw_ostream &OS,
|
||||
std::function<bool(const Record *R)> Filter);
|
||||
|
||||
/// Analyze pattern \p P, returning a matcher for it if possible.
|
||||
/// Otherwise, return an Error explaining why we don't support it.
|
||||
Expected<RuleMatcher> runOnPattern(const PatternToMatch &P);
|
||||
@ -2559,6 +2562,38 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
|
||||
return std::move(M);
|
||||
}
|
||||
|
||||
// Emit imm predicate table and an enum to reference them with.
|
||||
// The 'Predicate_' part of the name is redundant but eliminating it is more
|
||||
// trouble than it's worth.
|
||||
void GlobalISelEmitter::emitImmPredicates(
|
||||
raw_ostream &OS, std::function<bool(const Record *R)> Filter) {
|
||||
std::vector<const Record *> MatchedRecords;
|
||||
const auto &Defs = RK.getAllDerivedDefinitions("PatFrag");
|
||||
std::copy_if(Defs.begin(), Defs.end(), std::back_inserter(MatchedRecords),
|
||||
[&](Record *Record) {
|
||||
return !Record->getValueAsString("ImmediateCode").empty() &&
|
||||
Filter(Record);
|
||||
});
|
||||
|
||||
OS << "// PatFrag predicates.\n"
|
||||
<< "enum {\n";
|
||||
StringRef EnumeratorSeparator = " = GIPFP_Invalid + 1,\n";
|
||||
for (const auto *Record : MatchedRecords) {
|
||||
OS << " GIPFP_Predicate_" << Record->getName() << EnumeratorSeparator;
|
||||
EnumeratorSeparator = ",\n";
|
||||
}
|
||||
OS << "};\n";
|
||||
for (const auto *Record : MatchedRecords)
|
||||
OS << " static bool Predicate_" << Record->getName() << "(int64_t Imm) {"
|
||||
<< Record->getValueAsString("ImmediateCode") << " }\n";
|
||||
OS << "static InstructionSelector::ImmediatePredicateFn ImmPredicateFns[] = "
|
||||
"{\n"
|
||||
<< " nullptr,\n";
|
||||
for (const auto *Record : MatchedRecords)
|
||||
OS << " Predicate_" << Record->getName() << ",\n";
|
||||
OS << "};\n";
|
||||
}
|
||||
|
||||
void GlobalISelEmitter::run(raw_ostream &OS) {
|
||||
// Track the GINodeEquiv definitions.
|
||||
gatherNodeEquivs();
|
||||
@ -2742,32 +2777,11 @@ void GlobalISelEmitter::run(raw_ostream &OS) {
|
||||
OS << "};\n"
|
||||
<< "// See constructor for table contents\n\n";
|
||||
|
||||
// Emit imm predicate table and an enum to reference them with.
|
||||
// The 'Predicate_' part of the name is redundant but eliminating it is more
|
||||
// trouble than it's worth.
|
||||
{
|
||||
OS << "// PatFrag predicates.\n"
|
||||
<< "enum {\n";
|
||||
StringRef EnumeratorSeparator = " = GIPFP_Invalid + 1,\n";
|
||||
for (const auto *Record : RK.getAllDerivedDefinitions("PatFrag")) {
|
||||
if (!Record->getValueAsString("ImmediateCode").empty()) {
|
||||
OS << " GIPFP_Predicate_" << Record->getName() << EnumeratorSeparator;
|
||||
EnumeratorSeparator = ",\n";
|
||||
}
|
||||
}
|
||||
OS << "};\n";
|
||||
}
|
||||
for (const auto *Record : RK.getAllDerivedDefinitions("PatFrag"))
|
||||
if (!Record->getValueAsString("ImmediateCode").empty())
|
||||
OS << " static bool Predicate_" << Record->getName() << "(int64_t Imm) {"
|
||||
<< Record->getValueAsString("ImmediateCode") << " }\n";
|
||||
OS << "static InstructionSelector::ImmediatePredicateFn ImmPredicateFns[] = "
|
||||
"{\n"
|
||||
<< " nullptr,\n";
|
||||
for (const auto *Record : RK.getAllDerivedDefinitions("PatFrag"))
|
||||
if (!Record->getValueAsString("ImmediateCode").empty())
|
||||
OS << " Predicate_" << Record->getName() << ",\n";
|
||||
OS << "};\n";
|
||||
emitImmPredicates(OS, [](const Record *R) {
|
||||
bool Unset;
|
||||
return !R->getValueAsBitOrUnset("IsAPFloat", Unset) &&
|
||||
!R->getValueAsBit("IsAPInt");
|
||||
});
|
||||
|
||||
OS << "bool " << Target.getName()
|
||||
<< "InstructionSelector::selectImpl(MachineInstr &I) const {\n"
|
||||
|
Loading…
Reference in New Issue
Block a user