From 2e09c514c07518a018c36456029908573a08468a Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 2 Jul 2014 04:50:23 +0000 Subject: [PATCH] aarch64: support target-specific .req assembler directive Based on the support for .req on ARM. The aarch64 variant has to keep track if the alias register was a vector register (v0-31) or a general purpose or VFP/Advanced SIMD ([bhsdq]0-31) register. Patch by Janne Grunau! llvm-svn: 212161 --- .../AArch64/AsmParser/AArch64AsmParser.cpp | 99 ++++++++++++++++++- .../MC/AArch64/dot-req-case-insensitive.s | 18 ++++ llvm/test/MC/AArch64/dot-req-diagnostics.s | 37 +++++++ llvm/test/MC/AArch64/dot-req.s | 37 +++++++ 4 files changed, 188 insertions(+), 3 deletions(-) create mode 100644 llvm/test/MC/AArch64/dot-req-case-insensitive.s create mode 100644 llvm/test/MC/AArch64/dot-req-diagnostics.s create mode 100644 llvm/test/MC/AArch64/dot-req.s diff --git a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp index c4d840d90b54..b8f77fde3516 100644 --- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp +++ b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp @@ -43,6 +43,9 @@ private: MCSubtargetInfo &STI; MCAsmParser &Parser; + // Map of register aliases registers via the .req directive. + StringMap > RegisterReqs; + AArch64TargetStreamer &getTargetStreamer() { MCTargetStreamer &TS = *getParser().getStreamer().getTargetStreamer(); return static_cast(TS); @@ -56,6 +59,7 @@ private: bool parseSysAlias(StringRef Name, SMLoc NameLoc, OperandVector &Operands); AArch64CC::CondCode parseCondCodeString(StringRef Cond); bool parseCondCode(OperandVector &Operands, bool invertCondCode); + unsigned matchRegisterNameAlias(StringRef Name, bool isVector); int tryParseRegister(); int tryMatchVectorRegister(StringRef &Kind, bool expected); bool parseRegister(OperandVector &Operands); @@ -74,6 +78,9 @@ private: bool parseDirectiveLOH(StringRef LOH, SMLoc L); bool parseDirectiveLtorg(SMLoc L); + bool parseDirectiveReq(StringRef Name, SMLoc L); + bool parseDirectiveUnreq(SMLoc L); + bool validateInstruction(MCInst &Inst, SmallVectorImpl &Loc); bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, OperandVector &Operands, MCStreamer &Out, @@ -1825,6 +1832,26 @@ bool AArch64AsmParser::ParseRegister(unsigned &RegNo, SMLoc &StartLoc, return (RegNo == (unsigned)-1); } +// Matches a register name or register alias previously defined by '.req' +unsigned AArch64AsmParser::matchRegisterNameAlias(StringRef Name, + bool isVector) { + unsigned RegNum = isVector ? matchVectorRegName(Name) + : MatchRegisterName(Name); + + if (RegNum == 0) { + // Check for aliases registered via .req. Canonicalize to lower case. + // That's more consistent since register names are case insensitive, and + // it's how the original entry was passed in from MC/MCParser/AsmParser. + auto Entry = RegisterReqs.find(Name.lower()); + if (Entry == RegisterReqs.end()) + return 0; + // set RegNum if the match is the right kind of register + if (isVector == Entry->getValue().first) + RegNum = Entry->getValue().second; + } + return RegNum; +} + /// tryParseRegister - Try to parse a register name. The token must be an /// Identifier when called, and if it is a register name the token is eaten and /// the register is added to the operand list. @@ -1833,7 +1860,7 @@ int AArch64AsmParser::tryParseRegister() { assert(Tok.is(AsmToken::Identifier) && "Token is not an Identifier"); std::string lowerCase = Tok.getString().lower(); - unsigned RegNum = MatchRegisterName(lowerCase); + unsigned RegNum = matchRegisterNameAlias(lowerCase, false); // Also handle a few aliases of registers. if (RegNum == 0) RegNum = StringSwitch(lowerCase) @@ -1863,7 +1890,8 @@ int AArch64AsmParser::tryMatchVectorRegister(StringRef &Kind, bool expected) { // a '.'. size_t Start = 0, Next = Name.find('.'); StringRef Head = Name.slice(Start, Next); - unsigned RegNum = matchVectorRegName(Head); + unsigned RegNum = matchRegisterNameAlias(Head, true); + if (RegNum) { if (Next != StringRef::npos) { Kind = Name.slice(Next, StringRef::npos); @@ -2861,7 +2889,7 @@ AArch64AsmParser::tryParseGPR64sp0Operand(OperandVector &Operands) { if (!Tok.is(AsmToken::Identifier)) return MatchOperand_NoMatch; - unsigned RegNum = MatchRegisterName(Tok.getString().lower()); + unsigned RegNum = matchRegisterNameAlias(Tok.getString().lower(), false); MCContext &Ctx = getContext(); const MCRegisterInfo *RI = Ctx.getRegisterInfo(); @@ -3078,6 +3106,15 @@ bool AArch64AsmParser::ParseInstruction(ParseInstructionInfo &Info, .Case("bnv", "b.nv") .Default(Name); + // First check for the AArch64-specific .req directive. + if (Parser.getTok().is(AsmToken::Identifier) && + Parser.getTok().getIdentifier() == ".req") { + parseDirectiveReq(Name, NameLoc); + // We always return 'error' for this, as we're done with this + // statement and don't need to match the 'instruction." + return true; + } + // Create the leading tokens for the mnemonic, split by '.' characters. size_t Start = 0, Next = Name.find('.'); StringRef Head = Name.slice(Start, Next); @@ -3857,6 +3894,9 @@ bool AArch64AsmParser::ParseDirective(AsmToken DirectiveID) { return parseDirectiveTLSDescCall(Loc); if (IDVal == ".ltorg" || IDVal == ".pool") return parseDirectiveLtorg(Loc); + if (IDVal == ".unreq") + return parseDirectiveUnreq(DirectiveID.getLoc()); + return parseDirectiveLOH(IDVal, Loc); } @@ -3964,6 +4004,59 @@ bool AArch64AsmParser::parseDirectiveLtorg(SMLoc L) { return false; } +/// parseDirectiveReq +/// ::= name .req registername +bool AArch64AsmParser::parseDirectiveReq(StringRef Name, SMLoc L) { + Parser.Lex(); // Eat the '.req' token. + SMLoc SRegLoc = getLoc(); + unsigned RegNum = tryParseRegister(); + bool IsVector = false; + + if (RegNum == static_cast(-1)) { + StringRef Kind; + RegNum = tryMatchVectorRegister(Kind, false); + if (!Kind.empty()) { + Error(SRegLoc, "vector register without type specifier expected"); + return false; + } + IsVector = true; + } + + if (RegNum == static_cast(-1)) { + Parser.eatToEndOfStatement(); + Error(SRegLoc, "register name or alias expected"); + return false; + } + + // Shouldn't be anything else. + if (Parser.getTok().isNot(AsmToken::EndOfStatement)) { + Error(Parser.getTok().getLoc(), "unexpected input in .req directive"); + Parser.eatToEndOfStatement(); + return false; + } + + Parser.Lex(); // Consume the EndOfStatement + + auto pair = std::make_pair(IsVector, RegNum); + if (RegisterReqs.GetOrCreateValue(Name, pair).getValue() != pair) + Warning(L, "ignoring redefinition of register alias '" + Name + "'"); + + return true; +} + +/// parseDirectiveUneq +/// ::= .unreq registername +bool AArch64AsmParser::parseDirectiveUnreq(SMLoc L) { + if (Parser.getTok().isNot(AsmToken::Identifier)) { + Error(Parser.getTok().getLoc(), "unexpected input in .unreq directive."); + Parser.eatToEndOfStatement(); + return false; + } + RegisterReqs.erase(Parser.getTok().getIdentifier().lower()); + Parser.Lex(); // Eat the identifier. + return false; +} + bool AArch64AsmParser::classifySymbolRef(const MCExpr *Expr, AArch64MCExpr::VariantKind &ELFRefKind, diff --git a/llvm/test/MC/AArch64/dot-req-case-insensitive.s b/llvm/test/MC/AArch64/dot-req-case-insensitive.s new file mode 100644 index 000000000000..e68b1012f752 --- /dev/null +++ b/llvm/test/MC/AArch64/dot-req-case-insensitive.s @@ -0,0 +1,18 @@ +// RUN: llvm-mc -triple=arm64-eabi < %s | FileCheck %s +_foo: + OBJECT .req x2 + mov x4, OBJECT + mov x4, oBjEcT + .unreq oBJECT + +_foo2: + OBJECT .req w5 + mov w4, OBJECT + .unreq OBJECT + +// CHECK-LABEL: _foo: +// CHECK: mov x4, x2 +// CHECK: mov x4, x2 + +// CHECK-LABEL: _foo2: +// CHECK: mov w4, w5 diff --git a/llvm/test/MC/AArch64/dot-req-diagnostics.s b/llvm/test/MC/AArch64/dot-req-diagnostics.s new file mode 100644 index 000000000000..44065f8d1946 --- /dev/null +++ b/llvm/test/MC/AArch64/dot-req-diagnostics.s @@ -0,0 +1,37 @@ +// RUN: not llvm-mc -triple aarch64-none-linux-gnu < %s 2>&1 | FileCheck --check-prefix=CHECK --check-prefix=CHECK-ERROR %s + +bar: + fred .req x5 + fred .req x6 +// CHECK-ERROR: warning: ignoring redefinition of register alias 'fred' +// CHECK-ERROR: fred .req x6 +// CHECK-ERROR: ^ + + ada .req v2.8b +// CHECK-ERROR: error: vector register without type specifier expected +// CHECK-ERROR: ada .req v2.8b +// CHECK-ERROR: ^ + + bob .req lisa +// CHECK-ERROR: error: register name or alias expected +// CHECK-ERROR: bob .req lisa +// CHECK-ERROR: ^ + + lisa .req x1, 23 +// CHECK-ERROR: error: unexpected input in .req directive +// CHECK-ERROR: lisa .req x1, 23 +// CHECK-ERROR: ^ + + mov bob, fred +// CHECK-ERROR: error: invalid operand for instruction +// CHECK-ERROR: mov bob, fred +// CHECK-ERROR: ^ + + .unreq 1 +// CHECK-ERROR: error: unexpected input in .unreq directive. +// CHECK-ERROR: .unreq 1 +// CHECK-ERROR: ^ + + mov x1, fred +// CHECK: mov x1, x5 +// CHECK-NOT: mov x1, x6 diff --git a/llvm/test/MC/AArch64/dot-req.s b/llvm/test/MC/AArch64/dot-req.s new file mode 100644 index 000000000000..947f945bded8 --- /dev/null +++ b/llvm/test/MC/AArch64/dot-req.s @@ -0,0 +1,37 @@ +// RUN: llvm-mc -triple=aarch64-none-linux-gnu -show-encoding < %s | FileCheck %s + +bar: + fred .req x5 + mov fred, x11 + .unreq fred + fred .req w6 + mov w1, fred + + bob .req fred + ada .req w1 + mov ada, bob + .unreq bob + .unreq fred + .unreq ada +// CHECK: mov x5, x11 // encoding: [0xe5,0x03,0x0b,0xaa] +// CHECK: mov w1, w6 // encoding: [0xe1,0x03,0x06,0x2a] +// CHECK: mov w1, w6 // encoding: [0xe1,0x03,0x06,0x2a] + + bob .req b6 + hanah .req h5 + sam .req s4 + dora .req d3 + quentin .req q2 + vesna .req v1 + addv bob, v0.8b + mov hanah, v4.h[3] + fadd s0, sam, sam + fmov d2, dora + ldr quentin, [sp] + mov v0.8b, vesna.8b +// CHECK: addv b6, v0.8b // encoding: [0x06,0xb8,0x31,0x0e] +// CHECK: mov h5, v4.h[3] // encoding: [0x85,0x04,0x0e,0x5e] +// CHECK: fadd s0, s4, s4 // encoding: [0x80,0x28,0x24,0x1e] +// CHECK: fmov d2, d3 // encoding: [0x62,0x40,0x60,0x1e] +// CHECK: ldr q2, [sp] // encoding: [0xe2,0x03,0xc0,0x3d] +// CHECK: mov v0.8b, v1.8b // encoding: [0x20,0x1c,0xa1,0x0e]