[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/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