diff --git a/lib/MC/MCParser/AsmParser.cpp b/lib/MC/MCParser/AsmParser.cpp index d101ad2c4bf..5de21504783 100644 --- a/lib/MC/MCParser/AsmParser.cpp +++ b/lib/MC/MCParser/AsmParser.cpp @@ -204,6 +204,9 @@ private: /// ParseEscapedString - Parse the current token as a string which may include /// escaped characters and return the string contents. bool ParseEscapedString(std::string &Data); + + const MCExpr *ApplyModifierToExpr(const MCExpr *E, + MCSymbolRefExpr::VariantKind Variant); }; /// \brief Generic implementations of directive handling, etc. which is shared @@ -485,8 +488,13 @@ bool AsmParser::ParsePrimaryExpr(const MCExpr *&Res, SMLoc &EndLoc) { // Lookup the symbol variant if used. MCSymbolRefExpr::VariantKind Variant = MCSymbolRefExpr::VK_None; - if (Split.first.size() != Identifier.size()) + if (Split.first.size() != Identifier.size()) { Variant = MCSymbolRefExpr::getVariantKindForName(Split.second); + if (Variant == MCSymbolRefExpr::VK_Invalid) { + Variant = MCSymbolRefExpr::VK_None; + TokError("invalid variant '" + Split.second + "'"); + } + } // If this is an absolute variable reference, substitute it now to preserve // semantics in the face of reassignment. @@ -564,6 +572,52 @@ bool AsmParser::ParseExpression(const MCExpr *&Res) { return ParseExpression(Res, EndLoc); } +const MCExpr * +AsmParser::ApplyModifierToExpr(const MCExpr *E, + MCSymbolRefExpr::VariantKind Variant) { + // Recurse over the given expression, rebuilding it to apply the given variant + // if there is exactly one symbol. + switch (E->getKind()) { + case MCExpr::Target: + case MCExpr::Constant: + return 0; + + case MCExpr::SymbolRef: { + const MCSymbolRefExpr *SRE = cast(E); + + if (SRE->getKind() != MCSymbolRefExpr::VK_None) { + TokError("invalid variant on expression '" + + getTok().getIdentifier() + "' (already modified)"); + return E; + } + + return MCSymbolRefExpr::Create(&SRE->getSymbol(), Variant, getContext()); + } + + case MCExpr::Unary: { + const MCUnaryExpr *UE = cast(E); + const MCExpr *Sub = ApplyModifierToExpr(UE->getSubExpr(), Variant); + if (!Sub) + return 0; + return MCUnaryExpr::Create(UE->getOpcode(), Sub, getContext()); + } + + case MCExpr::Binary: { + const MCBinaryExpr *BE = cast(E); + const MCExpr *LHS = ApplyModifierToExpr(BE->getLHS(), Variant); + const MCExpr *RHS = ApplyModifierToExpr(BE->getRHS(), Variant); + + if (!LHS && !RHS) + return 0; + + if (!LHS) LHS = BE->getLHS(); + if (!RHS) RHS = BE->getRHS(); + + return MCBinaryExpr::Create(BE->getOpcode(), LHS, RHS, getContext()); + } + } +} + /// ParseExpression - Parse an expression and return it. /// /// expr ::= expr +,- expr -> lowest. @@ -577,6 +631,31 @@ bool AsmParser::ParseExpression(const MCExpr *&Res, SMLoc &EndLoc) { if (ParsePrimaryExpr(Res, EndLoc) || ParseBinOpRHS(1, Res, EndLoc)) return true; + // As a special case, we support 'a op b @ modifier' by rewriting the + // expression to include the modifier. This is inefficient, but in general we + // expect users to use 'a@modifier op b'. + if (Lexer.getKind() == AsmToken::At) { + Lex(); + + if (Lexer.isNot(AsmToken::Identifier)) + return TokError("unexpected symbol modifier following '@'"); + + MCSymbolRefExpr::VariantKind Variant = + MCSymbolRefExpr::getVariantKindForName(getTok().getIdentifier()); + if (Variant == MCSymbolRefExpr::VK_Invalid) + return TokError("invalid variant '" + getTok().getIdentifier() + "'"); + + const MCExpr *ModifiedRes = ApplyModifierToExpr(Res, Variant); + if (!ModifiedRes) { + return TokError("invalid modifier '" + getTok().getIdentifier() + + "' (no symbols present)"); + return true; + } + + Res = ModifiedRes; + Lex(); + } + // Try to constant fold it up front, if possible. int64_t Value; if (Res->EvaluateAsAbsolute(Value)) @@ -610,7 +689,7 @@ static unsigned getBinOpPrecedence(AsmToken::TokenKind K, default: return 0; // not a binop. - // Lowest Precedence: &&, || + // Lowest Precedence: &&, ||, @ case AsmToken::AmpAmp: Kind = MCBinaryExpr::LAnd; return 1; diff --git a/test/MC/AsmParser/expr_symbol_modifiers.s b/test/MC/AsmParser/expr_symbol_modifiers.s new file mode 100644 index 00000000000..7371c97cbdf --- /dev/null +++ b/test/MC/AsmParser/expr_symbol_modifiers.s @@ -0,0 +1,14 @@ +// RUN: not llvm-mc -triple x86_64-unknown-unknown %s > %t 2> %t.err +// RUN: FileCheck < %t %s +// RUN: FileCheck -check-prefix=CHECK-STDERR < %t.err %s + +// CHECK: .long 1 +.long 1 +// CHECK-STDERR: invalid modifier 'GOTPCREL' (no symbols present) +.long 10 + 4@GOTPCREL +// CHECK: .long a@GOTPCREL+4 +.long a + 4@GOTPCREL +// CHECK: .long a@GOTPCREL+b@GOTPCREL +.long (a + b)@GOTPCREL +// CHECK: .long (10+b@GOTPCREL)+4 +.long 10 + b + 4@GOTPCREL