Skip to content

Commit

Permalink
aarch64: support target-specific .req assembler directive
Browse files Browse the repository at this point in the history
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!

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@212161 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
compnerd committed Jul 2, 2014
1 parent b80dce0 commit 8f91084
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 3 deletions.
99 changes: 96 additions & 3 deletions lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ class AArch64AsmParser : public MCTargetAsmParser {
MCSubtargetInfo &STI;
MCAsmParser &Parser;

// Map of register aliases registers via the .req directive.
StringMap<std::pair<bool, unsigned> > RegisterReqs;

AArch64TargetStreamer &getTargetStreamer() {
MCTargetStreamer &TS = *getParser().getStreamer().getTargetStreamer();
return static_cast<AArch64TargetStreamer &>(TS);
Expand All @@ -56,6 +59,7 @@ class AArch64AsmParser : public MCTargetAsmParser {
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);
Expand All @@ -74,6 +78,9 @@ class AArch64AsmParser : public MCTargetAsmParser {
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<SMLoc> &Loc);
bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
OperandVector &Operands, MCStreamer &Out,
Expand Down Expand Up @@ -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.
Expand All @@ -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<unsigned>(lowerCase)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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<unsigned>(-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<unsigned>(-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,
Expand Down
18 changes: 18 additions & 0 deletions test/MC/AArch64/dot-req-case-insensitive.s
Original file line number Diff line number Diff line change
@@ -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
37 changes: 37 additions & 0 deletions test/MC/AArch64/dot-req-diagnostics.s
Original file line number Diff line number Diff line change
@@ -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
37 changes: 37 additions & 0 deletions test/MC/AArch64/dot-req.s
Original file line number Diff line number Diff line change
@@ -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]

0 comments on commit 8f91084

Please sign in to comment.