Skip to content

Commit

Permalink
[AArch64][SVE] Asm: Add parsing/printing support for exact FP immedia…
Browse files Browse the repository at this point in the history
…tes.

Some instructions require of a limited set of FP immediates as operands,
for example '#0.5 or brson#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



git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@334826 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
sdesmalen-arm committed Jun 15, 2018
1 parent f58bfff commit dde50dc
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 46 deletions.
2 changes: 1 addition & 1 deletion lib/Target/AArch64/AArch64InstrFormats.td
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ def ExtendOperandLSL64 : AsmOperandClass {
// 8-bit floating-point immediate encodings.
def FPImmOperand : AsmOperandClass {
let Name = "FPImm";
let ParserMethod = "tryParseFPImm";
let ParserMethod = "tryParseFPImm<true>";
let DiagnosticType = "InvalidFPImm";
}

Expand Down
21 changes: 21 additions & 0 deletions lib/Target/AArch64/AArch64SystemOperands.td
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,27 @@ def : SVEPREDPAT<"mul4", 0x1d>;
def : SVEPREDPAT<"mul3", 0x1e>;
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.
//===----------------------------------------------------------------------===//
Expand Down
145 changes: 100 additions & 45 deletions lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ class AArch64AsmParser : public MCTargetAsmParser {
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);
Expand Down Expand Up @@ -273,7 +274,8 @@ class AArch64Operand : public MCParsedAsmOperand {
};

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 {
Expand Down Expand Up @@ -419,9 +421,14 @@ class AArch64Operand : public MCParsedAsmOperand {
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 {
Expand Down Expand Up @@ -872,7 +879,11 @@ class AArch64Operand : public MCParsedAsmOperand {
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; }

Expand Down Expand Up @@ -1080,6 +1091,39 @@ class AArch64Operand : public MCParsedAsmOperand {
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;
Expand Down Expand Up @@ -1342,6 +1386,13 @@ class AArch64Operand : public MCParsedAsmOperand {
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
Expand Down Expand Up @@ -1481,7 +1532,8 @@ class AArch64Operand : public MCParsedAsmOperand {

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 {
Expand Down Expand Up @@ -1700,10 +1752,11 @@ class AArch64Operand : public MCParsedAsmOperand {
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;
Expand Down Expand Up @@ -1791,8 +1844,10 @@ class AArch64Operand : public MCParsedAsmOperand {
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();
Expand Down Expand Up @@ -2285,6 +2340,7 @@ AArch64AsmParser::tryParseAdrLabel(OperandVector &Operands) {
}

/// tryParseFPImm - A floating point immediate expression operand.
template<bool AddFPZeroAsLiteral>
OperandMatchResultTy
AArch64AsmParser::tryParseFPImm(OperandVector &Operands) {
MCAsmParser &Parser = getParser();
Expand All @@ -2296,45 +2352,44 @@ AArch64AsmParser::tryParseFPImm(OperandVector &Operands) {
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();
if (!Tok.is(AsmToken::Real) && !Tok.is(AsmToken::Integer)) {
if (!Hash)
return MatchOperand_NoMatch;
TokError("invalid floating point immediate");
return MatchOperand_ParseFail;
}

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;
}
// 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;
}
Parser.Lex(); // Eat the token.
Operands.push_back(AArch64Operand::CreateFPImm(Val, S, getContext()));
return MatchOperand_Success;

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()));
}

if (!Hash)
return MatchOperand_NoMatch;
Parser.Lex(); // Eat the token.

TokError("invalid floating point immediate");
return MatchOperand_ParseFail;
return MatchOperand_Success;
}

/// tryParseImmWithOptionalShift - Parse immediate operand, optionally with
Expand Down
10 changes: 10 additions & 0 deletions lib/Target/AArch64/InstPrinter/AArch64InstPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1517,3 +1517,13 @@ void AArch64InstPrinter::printZPRasFPR(const MCInst *MI, unsigned OpNum,
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);
}
3 changes: 3 additions & 0 deletions lib/Target/AArch64/InstPrinter/AArch64InstPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ class AArch64InstPrinter : public MCInstPrinter {
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 {
Expand Down
12 changes: 12 additions & 0 deletions lib/Target/AArch64/SVEInstrFormats.td
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,18 @@ def addsub_imm8_opt_lsl_i64 : imm8_opt_lsl<64, "uint64_t", SVEAddSubImmOperand64
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
Expand Down
7 changes: 7 additions & 0 deletions lib/Target/AArch64/Utils/AArch64BaseInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ namespace llvm {
}
}

namespace llvm {
namespace AArch64ExactFPImm {
#define GET_EXACTFPIMM_IMPL
#include "AArch64GenSystemOperands.inc"
}
}

namespace llvm {
namespace AArch64PState {
#define GET_PSTATE_IMPL
Expand Down
10 changes: 10 additions & 0 deletions lib/Target/AArch64/Utils/AArch64BaseInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,16 @@ namespace AArch64SVEPredPattern {
#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;
Expand Down

0 comments on commit dde50dc

Please sign in to comment.