[AArch64][SVE] Asm: Add parsing/printing support for exact FP immediates.
Some instructions require of a limited set of FP immediates as operands,
for example '#0.5 or #1.0' for SVE's FADD instruction.
This patch adds support for parsing and printing such FP immediates as
exact values (e.g. #0.499999 is not accepted for #0.5).
Reviewers: rengolin, fhahn, SjoerdMeijer, samparker, javed.absar
Reviewed By: SjoerdMeijer
Differential Revision: https://reviews.llvm.org/D47711
llvm-svn: 334826
diff --git a/llvm/lib/Target/AArch64/AArch64InstrFormats.td b/llvm/lib/Target/AArch64/AArch64InstrFormats.td
index 683ab69..be845e1 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrFormats.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrFormats.td
@@ -167,7 +167,7 @@
// 8-bit floating-point immediate encodings.
def FPImmOperand : AsmOperandClass {
let Name = "FPImm";
- let ParserMethod = "tryParseFPImm";
+ let ParserMethod = "tryParseFPImm<true>";
let DiagnosticType = "InvalidFPImm";
}
diff --git a/llvm/lib/Target/AArch64/AArch64SystemOperands.td b/llvm/lib/Target/AArch64/AArch64SystemOperands.td
index cc1fb58..9de12ef 100644
--- a/llvm/lib/Target/AArch64/AArch64SystemOperands.td
+++ b/llvm/lib/Target/AArch64/AArch64SystemOperands.td
@@ -235,6 +235,27 @@
def : SVEPREDPAT<"all", 0x1f>;
//===----------------------------------------------------------------------===//
+// Exact FP Immediates.
+//
+// These definitions are used to create a lookup table with FP Immediates that
+// is used for a few instructions that only accept a limited set of exact FP
+// immediates values.
+//===----------------------------------------------------------------------===//
+class ExactFPImm<string name, string repr, bits<4> enum > : SearchableTable {
+ let SearchableFields = ["Enum", "Repr"];
+ let EnumValueField = "Enum";
+
+ string Name = name;
+ bits<4> Enum = enum;
+ string Repr = repr;
+}
+
+def : ExactFPImm<"zero", "0.0", 0x0>;
+def : ExactFPImm<"half", "0.5", 0x1>;
+def : ExactFPImm<"one", "1.0", 0x2>;
+def : ExactFPImm<"two", "2.0", 0x3>;
+
+//===----------------------------------------------------------------------===//
// PState instruction options.
//===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
index 6db575a..ae9a351 100644
--- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
+++ b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
@@ -132,6 +132,7 @@
OperandMatchResultTy tryParsePSBHint(OperandVector &Operands);
OperandMatchResultTy tryParseAdrpLabel(OperandVector &Operands);
OperandMatchResultTy tryParseAdrLabel(OperandVector &Operands);
+ template<bool AddFPZeroAsLiteral>
OperandMatchResultTy tryParseFPImm(OperandVector &Operands);
OperandMatchResultTy tryParseImmWithOptionalShift(OperandVector &Operands);
OperandMatchResultTy tryParseGPR64sp0Operand(OperandVector &Operands);
@@ -273,7 +274,8 @@
};
struct FPImmOp {
- unsigned Val; // Encoded 8-bit representation.
+ uint64_t Val; // APFloat value bitcasted to uint64_t.
+ bool IsExact; // describes whether parsed value was exact.
};
struct BarrierOp {
@@ -419,9 +421,14 @@
return CondCode.Code;
}
- unsigned getFPImm() const {
- assert(Kind == k_FPImm && "Invalid access!");
- return FPImm.Val;
+ APFloat getFPImm() const {
+ assert (Kind == k_FPImm && "Invalid access!");
+ return APFloat(APFloat::IEEEdouble(), APInt(64, FPImm.Val, true));
+ }
+
+ bool getFPImmIsExact() const {
+ assert (Kind == k_FPImm && "Invalid access!");
+ return FPImm.IsExact;
}
unsigned getBarrier() const {
@@ -872,7 +879,11 @@
return AArch64_AM::isMOVNMovAlias(Value, Shift, RegWidth);
}
- bool isFPImm() const { return Kind == k_FPImm; }
+ bool isFPImm() const {
+ return Kind == k_FPImm &&
+ AArch64_AM::getFP64Imm(getFPImm().bitcastToAPInt()) != -1;
+ }
+
bool isBarrier() const { return Kind == k_Barrier; }
bool isSysReg() const { return Kind == k_SysReg; }
@@ -1080,6 +1091,39 @@
ST == AArch64_AM::ASR || ST == AArch64_AM::ROR ||
ST == AArch64_AM::MSL);
}
+
+ template <unsigned ImmEnum> DiagnosticPredicate isExactFPImm() const {
+ if (Kind != k_FPImm)
+ return DiagnosticPredicateTy::NoMatch;
+
+ if (getFPImmIsExact()) {
+ // Lookup the immediate from table of supported immediates.
+ auto *Desc = AArch64ExactFPImm::lookupExactFPImmByEnum(ImmEnum);
+ assert(Desc && "Unknown enum value");
+
+ // Calculate its FP value.
+ APFloat RealVal(APFloat::IEEEdouble());
+ if (RealVal.convertFromString(Desc->Repr, APFloat::rmTowardZero) !=
+ APFloat::opOK)
+ llvm_unreachable("FP immediate is not exact");
+
+ if (getFPImm().bitwiseIsEqual(RealVal))
+ return DiagnosticPredicateTy::Match;
+ }
+
+ return DiagnosticPredicateTy::NearMatch;
+ }
+
+ template <unsigned ImmA, unsigned ImmB>
+ DiagnosticPredicate isExactFPImm() const {
+ DiagnosticPredicate Res = DiagnosticPredicateTy::NoMatch;
+ if ((Res = isExactFPImm<ImmA>()))
+ return DiagnosticPredicateTy::Match;
+ if ((Res = isExactFPImm<ImmB>()))
+ return DiagnosticPredicateTy::Match;
+ return Res;
+ }
+
bool isExtend() const {
if (!isShiftExtend())
return false;
@@ -1342,6 +1386,13 @@
Inst.addOperand(MCOperand::createImm(getVectorIndex()));
}
+ template <unsigned ImmIs0, unsigned ImmIs1>
+ void addExactFPImmOperands(MCInst &Inst, unsigned N) const {
+ assert(N == 1 && "Invalid number of operands!");
+ assert(bool(isExactFPImm<ImmIs0, ImmIs1>()) && "Invalid operand");
+ Inst.addOperand(MCOperand::createImm(bool(isExactFPImm<ImmIs1>())));
+ }
+
void addImmOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
// If this is a pageoff symrefexpr with an addend, adjust the addend
@@ -1481,7 +1532,8 @@
void addFPImmOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
- Inst.addOperand(MCOperand::createImm(getFPImm()));
+ Inst.addOperand(MCOperand::createImm(
+ AArch64_AM::getFP64Imm(getFPImm().bitcastToAPInt())));
}
void addBarrierOperands(MCInst &Inst, unsigned N) const {
@@ -1700,10 +1752,11 @@
return Op;
}
- static std::unique_ptr<AArch64Operand> CreateFPImm(unsigned Val, SMLoc S,
- MCContext &Ctx) {
+ static std::unique_ptr<AArch64Operand>
+ CreateFPImm(APFloat Val, bool IsExact, SMLoc S, MCContext &Ctx) {
auto Op = make_unique<AArch64Operand>(k_FPImm, Ctx);
- Op->FPImm.Val = Val;
+ Op->FPImm.Val = Val.bitcastToAPInt().getSExtValue();
+ Op->FPImm.IsExact = IsExact;
Op->StartLoc = S;
Op->EndLoc = S;
return Op;
@@ -1791,8 +1844,10 @@
void AArch64Operand::print(raw_ostream &OS) const {
switch (Kind) {
case k_FPImm:
- OS << "<fpimm " << getFPImm() << "("
- << AArch64_AM::getFPImmFloat(getFPImm()) << ") >";
+ OS << "<fpimm " << getFPImm().bitcastToAPInt().getZExtValue();
+ if (!getFPImmIsExact())
+ OS << " (inexact)";
+ OS << ">";
break;
case k_Barrier: {
StringRef Name = getBarrierName();
@@ -2285,6 +2340,7 @@
}
/// tryParseFPImm - A floating point immediate expression operand.
+template<bool AddFPZeroAsLiteral>
OperandMatchResultTy
AArch64AsmParser::tryParseFPImm(OperandVector &Operands) {
MCAsmParser &Parser = getParser();
@@ -2296,45 +2352,44 @@
bool isNegative = parseOptionalToken(AsmToken::Minus);
const AsmToken &Tok = Parser.getTok();
- if (Tok.is(AsmToken::Real) || Tok.is(AsmToken::Integer)) {
- int64_t Val;
- if (Tok.is(AsmToken::Integer) && Tok.getString().startswith("0x")) {
- Val = Tok.getIntVal();
- if (Val > 255 || isNegative) {
- TokError("encoded floating point value out of range");
- return MatchOperand_ParseFail;
- }
- } else {
- APFloat RealVal(APFloat::IEEEdouble(), Tok.getString());
- if (isNegative)
- RealVal.changeSign();
-
- uint64_t IntVal = RealVal.bitcastToAPInt().getZExtValue();
- Val = AArch64_AM::getFP64Imm(APInt(64, IntVal));
-
- // Check for out of range values. As an exception we let Zero through,
- // but as tokens instead of an FPImm so that it can be matched by the
- // appropriate alias if one exists.
- if (RealVal.isPosZero()) {
- Parser.Lex(); // Eat the token.
- Operands.push_back(AArch64Operand::CreateToken("#0", false, S, getContext()));
- Operands.push_back(AArch64Operand::CreateToken(".0", false, S, getContext()));
- return MatchOperand_Success;
- } else if (Val == -1) {
- TokError("expected compatible register or floating-point constant");
- return MatchOperand_ParseFail;
- }
- }
- Parser.Lex(); // Eat the token.
- Operands.push_back(AArch64Operand::CreateFPImm(Val, S, getContext()));
- return MatchOperand_Success;
+ if (!Tok.is(AsmToken::Real) && !Tok.is(AsmToken::Integer)) {
+ if (!Hash)
+ return MatchOperand_NoMatch;
+ TokError("invalid floating point immediate");
+ return MatchOperand_ParseFail;
}
- if (!Hash)
- return MatchOperand_NoMatch;
+ // Parse hexadecimal representation.
+ if (Tok.is(AsmToken::Integer) && Tok.getString().startswith("0x")) {
+ if (Tok.getIntVal() > 255 || isNegative) {
+ TokError("encoded floating point value out of range");
+ return MatchOperand_ParseFail;
+ }
- TokError("invalid floating point immediate");
- return MatchOperand_ParseFail;
+ APFloat F((double)AArch64_AM::getFPImmFloat(Tok.getIntVal()));
+ Operands.push_back(
+ AArch64Operand::CreateFPImm(F, true, S, getContext()));
+ } else {
+ // Parse FP representation.
+ APFloat RealVal(APFloat::IEEEdouble());
+ auto Status =
+ RealVal.convertFromString(Tok.getString(), APFloat::rmTowardZero);
+ if (isNegative)
+ RealVal.changeSign();
+
+ if (AddFPZeroAsLiteral && RealVal.isPosZero()) {
+ Operands.push_back(
+ AArch64Operand::CreateToken("#0", false, S, getContext()));
+ Operands.push_back(
+ AArch64Operand::CreateToken(".0", false, S, getContext()));
+ } else
+ Operands.push_back(AArch64Operand::CreateFPImm(
+ RealVal, Status == APFloat::opOK, S, getContext()));
+ }
+
+ Parser.Lex(); // Eat the token.
+
+ return MatchOperand_Success;
}
/// tryParseImmWithOptionalShift - Parse immediate operand, optionally with
diff --git a/llvm/lib/Target/AArch64/InstPrinter/AArch64InstPrinter.cpp b/llvm/lib/Target/AArch64/InstPrinter/AArch64InstPrinter.cpp
index 3d52fa1..8f4fca7 100644
--- a/llvm/lib/Target/AArch64/InstPrinter/AArch64InstPrinter.cpp
+++ b/llvm/lib/Target/AArch64/InstPrinter/AArch64InstPrinter.cpp
@@ -1517,3 +1517,13 @@
unsigned Reg = MI->getOperand(OpNum).getReg();
O << getRegisterName(Reg - AArch64::Z0 + Base);
}
+
+template <unsigned ImmIs0, unsigned ImmIs1>
+void AArch64InstPrinter::printExactFPImm(const MCInst *MI, unsigned OpNum,
+ const MCSubtargetInfo &STI,
+ raw_ostream &O) {
+ auto *Imm0Desc = AArch64ExactFPImm::lookupExactFPImmByEnum(ImmIs0);
+ auto *Imm1Desc = AArch64ExactFPImm::lookupExactFPImmByEnum(ImmIs1);
+ unsigned Val = MI->getOperand(OpNum).getImm();
+ O << "#" << (Val ? Imm1Desc->Repr : Imm0Desc->Repr);
+}
diff --git a/llvm/lib/Target/AArch64/InstPrinter/AArch64InstPrinter.h b/llvm/lib/Target/AArch64/InstPrinter/AArch64InstPrinter.h
index 446a065..15cb363 100644
--- a/llvm/lib/Target/AArch64/InstPrinter/AArch64InstPrinter.h
+++ b/llvm/lib/Target/AArch64/InstPrinter/AArch64InstPrinter.h
@@ -183,6 +183,9 @@
template <int Width>
void printZPRasFPR(const MCInst *MI, unsigned OpNum,
const MCSubtargetInfo &STI, raw_ostream &O);
+ template <unsigned ImmIs0, unsigned ImmIs1>
+ void printExactFPImm(const MCInst *MI, unsigned OpNum,
+ const MCSubtargetInfo &STI, raw_ostream &O);
};
class AArch64AppleInstPrinter : public AArch64InstPrinter {
diff --git a/llvm/lib/Target/AArch64/SVEInstrFormats.td b/llvm/lib/Target/AArch64/SVEInstrFormats.td
index 9382473..8802fb8 100644
--- a/llvm/lib/Target/AArch64/SVEInstrFormats.td
+++ b/llvm/lib/Target/AArch64/SVEInstrFormats.td
@@ -203,6 +203,18 @@
return AArch64_AM::isSVEAddSubImm<int64_t>(Imm);
}]>;
+class SVEExactFPImm<string Suffix, string ValA, string ValB> : AsmOperandClass {
+ let Name = "SVEExactFPImmOperand" # Suffix;
+ let DiagnosticType = "Invalid" # Name;
+ let ParserMethod = "tryParseFPImm<false>";
+ let PredicateMethod = "isExactFPImm<" # ValA # ", " # ValB # ">";
+ let RenderMethod = "addExactFPImmOperands<" # ValA # ", " # ValB # ">";
+}
+
+class SVEExactFPImmOperand<string Suffix, string ValA, string ValB> : Operand<i32> {
+ let PrintMethod = "printExactFPImm<" # ValA # ", " # ValB # ">";
+ let ParserMatchClass = SVEExactFPImm<Suffix, ValA, ValB>;
+}
//===----------------------------------------------------------------------===//
// SVE PTrue - These are used extensively throughout the pattern matching so
diff --git a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.cpp b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.cpp
index 3fa383b..5f6eaa0 100644
--- a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.cpp
+++ b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.cpp
@@ -75,6 +75,13 @@
}
namespace llvm {
+ namespace AArch64ExactFPImm {
+#define GET_EXACTFPIMM_IMPL
+#include "AArch64GenSystemOperands.inc"
+ }
+}
+
+namespace llvm {
namespace AArch64PState {
#define GET_PSTATE_IMPL
#include "AArch64GenSystemOperands.inc"
diff --git a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h
index 26f2a86..86bf699 100644
--- a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h
+++ b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h
@@ -352,6 +352,16 @@
#include "AArch64GenSystemOperands.inc"
}
+namespace AArch64ExactFPImm {
+ struct ExactFPImm {
+ const char *Name;
+ int Enum;
+ const char *Repr;
+ };
+#define GET_EXACTFPIMM_DECL
+#include "AArch64GenSystemOperands.inc"
+}
+
namespace AArch64PState {
struct PState : SysAlias{
using SysAlias::SysAlias;