
The isUnset state lacks significance and should be treated as equivalent to an undefined symbol. Equated and common symbols seem to have subtle semantic distinctions in GAS, justifying the use of distinct `SymContents*` values. TODO: Ensure common symbols have a fragment, so that `isDefined()` returns true, rejecting `.comm c, 4, 4; .set c, 4`
6307 lines
202 KiB
C++
6307 lines
202 KiB
C++
//===- AsmParser.cpp - Parser for Assembly Files --------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This class implements a parser for assembly files similar to gas syntax.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/ADT/APFloat.h"
|
|
#include "llvm/ADT/APInt.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/SmallSet.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/ADT/StringMap.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/BinaryFormat/Dwarf.h"
|
|
#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
|
|
#include "llvm/MC/MCAsmInfo.h"
|
|
#include "llvm/MC/MCCodeView.h"
|
|
#include "llvm/MC/MCContext.h"
|
|
#include "llvm/MC/MCDirectives.h"
|
|
#include "llvm/MC/MCDwarf.h"
|
|
#include "llvm/MC/MCExpr.h"
|
|
#include "llvm/MC/MCInstPrinter.h"
|
|
#include "llvm/MC/MCInstrDesc.h"
|
|
#include "llvm/MC/MCInstrInfo.h"
|
|
#include "llvm/MC/MCParser/AsmCond.h"
|
|
#include "llvm/MC/MCParser/AsmLexer.h"
|
|
#include "llvm/MC/MCParser/MCAsmParser.h"
|
|
#include "llvm/MC/MCParser/MCAsmParserExtension.h"
|
|
#include "llvm/MC/MCParser/MCAsmParserUtils.h"
|
|
#include "llvm/MC/MCParser/MCParsedAsmOperand.h"
|
|
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
|
|
#include "llvm/MC/MCRegisterInfo.h"
|
|
#include "llvm/MC/MCSection.h"
|
|
#include "llvm/MC/MCStreamer.h"
|
|
#include "llvm/MC/MCSymbol.h"
|
|
#include "llvm/MC/MCSymbolMachO.h"
|
|
#include "llvm/MC/MCTargetOptions.h"
|
|
#include "llvm/MC/MCValue.h"
|
|
#include "llvm/Support/Casting.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/MD5.h"
|
|
#include "llvm/Support/MathExtras.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/SMLoc.h"
|
|
#include "llvm/Support/SourceMgr.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <cctype>
|
|
#include <climits>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <deque>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <tuple>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
using namespace llvm;
|
|
|
|
MCAsmParserSemaCallback::~MCAsmParserSemaCallback() = default;
|
|
|
|
namespace {
|
|
|
|
/// Helper types for tracking macro definitions.
|
|
typedef std::vector<AsmToken> MCAsmMacroArgument;
|
|
typedef std::vector<MCAsmMacroArgument> MCAsmMacroArguments;
|
|
|
|
/// Helper class for storing information about an active macro
|
|
/// instantiation.
|
|
struct MacroInstantiation {
|
|
/// The location of the instantiation.
|
|
SMLoc InstantiationLoc;
|
|
|
|
/// The buffer where parsing should resume upon instantiation completion.
|
|
unsigned ExitBuffer;
|
|
|
|
/// The location where parsing should resume upon instantiation completion.
|
|
SMLoc ExitLoc;
|
|
|
|
/// The depth of TheCondStack at the start of the instantiation.
|
|
size_t CondStackDepth;
|
|
};
|
|
|
|
struct ParseStatementInfo {
|
|
/// The parsed operands from the last parsed statement.
|
|
SmallVector<std::unique_ptr<MCParsedAsmOperand>, 8> ParsedOperands;
|
|
|
|
/// The opcode from the last parsed instruction.
|
|
unsigned Opcode = ~0U;
|
|
|
|
/// Was there an error parsing the inline assembly?
|
|
bool ParseError = false;
|
|
|
|
SmallVectorImpl<AsmRewrite> *AsmRewrites = nullptr;
|
|
|
|
ParseStatementInfo() = delete;
|
|
ParseStatementInfo(SmallVectorImpl<AsmRewrite> *rewrites)
|
|
: AsmRewrites(rewrites) {}
|
|
};
|
|
|
|
/// The concrete assembly parser instance.
|
|
class AsmParser : public MCAsmParser {
|
|
private:
|
|
SourceMgr::DiagHandlerTy SavedDiagHandler;
|
|
void *SavedDiagContext;
|
|
std::unique_ptr<MCAsmParserExtension> PlatformParser;
|
|
SMLoc StartTokLoc;
|
|
std::optional<SMLoc> CFIStartProcLoc;
|
|
|
|
/// This is the current buffer index we're lexing from as managed by the
|
|
/// SourceMgr object.
|
|
unsigned CurBuffer;
|
|
|
|
AsmCond TheCondState;
|
|
std::vector<AsmCond> TheCondStack;
|
|
|
|
/// maps directive names to handler methods in parser
|
|
/// extensions. Extensions register themselves in this map by calling
|
|
/// addDirectiveHandler.
|
|
StringMap<ExtensionDirectiveHandler> ExtensionDirectiveMap;
|
|
|
|
/// Stack of active macro instantiations.
|
|
std::vector<MacroInstantiation*> ActiveMacros;
|
|
|
|
/// List of bodies of anonymous macros.
|
|
std::deque<MCAsmMacro> MacroLikeBodies;
|
|
|
|
/// Boolean tracking whether macro substitution is enabled.
|
|
unsigned MacrosEnabledFlag : 1;
|
|
|
|
/// Keeps track of how many .macro's have been instantiated.
|
|
unsigned NumOfMacroInstantiations = 0;
|
|
|
|
/// The values from the last parsed cpp hash file line comment if any.
|
|
struct CppHashInfoTy {
|
|
StringRef Filename;
|
|
int64_t LineNumber;
|
|
SMLoc Loc;
|
|
unsigned Buf;
|
|
CppHashInfoTy() : LineNumber(0), Buf(0) {}
|
|
};
|
|
CppHashInfoTy CppHashInfo;
|
|
|
|
/// Have we seen any file line comment.
|
|
bool HadCppHashFilename = false;
|
|
|
|
/// List of forward directional labels for diagnosis at the end.
|
|
SmallVector<std::tuple<SMLoc, CppHashInfoTy, MCSymbol *>, 4> DirLabels;
|
|
|
|
SmallSet<StringRef, 2> LTODiscardSymbols;
|
|
|
|
/// AssemblerDialect. ~OU means unset value and use value provided by MAI.
|
|
unsigned AssemblerDialect = ~0U;
|
|
|
|
/// is Darwin compatibility enabled?
|
|
bool IsDarwin = false;
|
|
|
|
/// Are we parsing ms-style inline assembly?
|
|
bool ParsingMSInlineAsm = false;
|
|
|
|
/// Did we already inform the user about inconsistent MD5 usage?
|
|
bool ReportedInconsistentMD5 = false;
|
|
|
|
// Is alt macro mode enabled.
|
|
bool AltMacroMode = false;
|
|
|
|
protected:
|
|
virtual bool parseStatement(ParseStatementInfo &Info,
|
|
MCAsmParserSemaCallback *SI);
|
|
|
|
/// This routine uses the target specific ParseInstruction function to
|
|
/// parse an instruction into Operands, and then call the target specific
|
|
/// MatchAndEmit function to match and emit the instruction.
|
|
bool parseAndMatchAndEmitTargetInstruction(ParseStatementInfo &Info,
|
|
StringRef IDVal, AsmToken ID,
|
|
SMLoc IDLoc);
|
|
|
|
/// Should we emit DWARF describing this assembler source? (Returns false if
|
|
/// the source has .file directives, which means we don't want to generate
|
|
/// info describing the assembler source itself.)
|
|
bool enabledGenDwarfForAssembly();
|
|
|
|
public:
|
|
AsmParser(SourceMgr &SM, MCContext &Ctx, MCStreamer &Out,
|
|
const MCAsmInfo &MAI, unsigned CB);
|
|
AsmParser(const AsmParser &) = delete;
|
|
AsmParser &operator=(const AsmParser &) = delete;
|
|
~AsmParser() override;
|
|
|
|
bool Run(bool NoInitialTextSection, bool NoFinalize = false) override;
|
|
|
|
void addDirectiveHandler(StringRef Directive,
|
|
ExtensionDirectiveHandler Handler) override {
|
|
ExtensionDirectiveMap[Directive] = Handler;
|
|
}
|
|
|
|
void addAliasForDirective(StringRef Directive, StringRef Alias) override {
|
|
DirectiveKindMap[Directive.lower()] = DirectiveKindMap[Alias.lower()];
|
|
}
|
|
|
|
/// @name MCAsmParser Interface
|
|
/// {
|
|
|
|
CodeViewContext &getCVContext() { return Ctx.getCVContext(); }
|
|
|
|
unsigned getAssemblerDialect() override {
|
|
if (AssemblerDialect == ~0U)
|
|
return MAI.getAssemblerDialect();
|
|
else
|
|
return AssemblerDialect;
|
|
}
|
|
void setAssemblerDialect(unsigned i) override {
|
|
AssemblerDialect = i;
|
|
}
|
|
|
|
void Note(SMLoc L, const Twine &Msg, SMRange Range = std::nullopt) override;
|
|
bool Warning(SMLoc L, const Twine &Msg,
|
|
SMRange Range = std::nullopt) override;
|
|
bool printError(SMLoc L, const Twine &Msg,
|
|
SMRange Range = std::nullopt) override;
|
|
|
|
const AsmToken &Lex() override;
|
|
|
|
void setParsingMSInlineAsm(bool V) override {
|
|
ParsingMSInlineAsm = V;
|
|
// When parsing MS inline asm, we must lex 0b1101 and 0ABCH as binary and
|
|
// hex integer literals.
|
|
Lexer.setLexMasmIntegers(V);
|
|
}
|
|
bool isParsingMSInlineAsm() override { return ParsingMSInlineAsm; }
|
|
|
|
bool discardLTOSymbol(StringRef Name) const override {
|
|
return LTODiscardSymbols.contains(Name);
|
|
}
|
|
|
|
bool parseMSInlineAsm(std::string &AsmString, unsigned &NumOutputs,
|
|
unsigned &NumInputs,
|
|
SmallVectorImpl<std::pair<void *, bool>> &OpDecls,
|
|
SmallVectorImpl<std::string> &Constraints,
|
|
SmallVectorImpl<std::string> &Clobbers,
|
|
const MCInstrInfo *MII, MCInstPrinter *IP,
|
|
MCAsmParserSemaCallback &SI) override;
|
|
|
|
bool parseExpression(const MCExpr *&Res);
|
|
bool parseExpression(const MCExpr *&Res, SMLoc &EndLoc) override;
|
|
bool parsePrimaryExpr(const MCExpr *&Res, SMLoc &EndLoc,
|
|
AsmTypeInfo *TypeInfo) override;
|
|
bool parseParenExpression(const MCExpr *&Res, SMLoc &EndLoc) override;
|
|
bool parseAbsoluteExpression(int64_t &Res) override;
|
|
|
|
/// Parse a floating point expression using the float \p Semantics
|
|
/// and set \p Res to the value.
|
|
bool parseRealValue(const fltSemantics &Semantics, APInt &Res);
|
|
|
|
/// Parse an identifier or string (as a quoted identifier)
|
|
/// and set \p Res to the identifier contents.
|
|
bool parseIdentifier(StringRef &Res) override;
|
|
void eatToEndOfStatement() override;
|
|
|
|
bool checkForValidSection() override;
|
|
|
|
/// }
|
|
|
|
private:
|
|
bool parseCurlyBlockScope(SmallVectorImpl<AsmRewrite>& AsmStrRewrites);
|
|
bool parseCppHashLineFilenameComment(SMLoc L, bool SaveLocInfo = true);
|
|
|
|
void checkForBadMacro(SMLoc DirectiveLoc, StringRef Name, StringRef Body,
|
|
ArrayRef<MCAsmMacroParameter> Parameters);
|
|
bool expandMacro(raw_svector_ostream &OS, MCAsmMacro &Macro,
|
|
ArrayRef<MCAsmMacroParameter> Parameters,
|
|
ArrayRef<MCAsmMacroArgument> A, bool EnableAtPseudoVariable);
|
|
|
|
/// Are macros enabled in the parser?
|
|
bool areMacrosEnabled() {return MacrosEnabledFlag;}
|
|
|
|
/// Control a flag in the parser that enables or disables macros.
|
|
void setMacrosEnabled(bool Flag) {MacrosEnabledFlag = Flag;}
|
|
|
|
/// Are we inside a macro instantiation?
|
|
bool isInsideMacroInstantiation() {return !ActiveMacros.empty();}
|
|
|
|
/// Handle entry to macro instantiation.
|
|
///
|
|
/// \param M The macro.
|
|
/// \param NameLoc Instantiation location.
|
|
bool handleMacroEntry(MCAsmMacro *M, SMLoc NameLoc);
|
|
|
|
/// Handle exit from macro instantiation.
|
|
void handleMacroExit();
|
|
|
|
/// Extract AsmTokens for a macro argument.
|
|
bool parseMacroArgument(MCAsmMacroArgument &MA, bool Vararg);
|
|
|
|
/// Parse all macro arguments for a given macro.
|
|
bool parseMacroArguments(const MCAsmMacro *M, MCAsmMacroArguments &A);
|
|
|
|
void printMacroInstantiations();
|
|
void printMessage(SMLoc Loc, SourceMgr::DiagKind Kind, const Twine &Msg,
|
|
SMRange Range = std::nullopt) const {
|
|
ArrayRef<SMRange> Ranges(Range);
|
|
SrcMgr.PrintMessage(Loc, Kind, Msg, Ranges);
|
|
}
|
|
static void DiagHandler(const SMDiagnostic &Diag, void *Context);
|
|
|
|
/// Enter the specified file. This returns true on failure.
|
|
bool enterIncludeFile(const std::string &Filename);
|
|
|
|
/// Process the specified file for the .incbin directive.
|
|
/// This returns true on failure.
|
|
bool processIncbinFile(const std::string &Filename, int64_t Skip = 0,
|
|
const MCExpr *Count = nullptr, SMLoc Loc = SMLoc());
|
|
|
|
/// Reset the current lexer position to that given by \p Loc. The
|
|
/// current token is not set; clients should ensure Lex() is called
|
|
/// subsequently.
|
|
///
|
|
/// \param InBuffer If not 0, should be the known buffer id that contains the
|
|
/// location.
|
|
void jumpToLoc(SMLoc Loc, unsigned InBuffer = 0);
|
|
|
|
/// Parse up to the end of statement and a return the contents from the
|
|
/// current token until the end of the statement; the current token on exit
|
|
/// will be either the EndOfStatement or EOF.
|
|
StringRef parseStringToEndOfStatement() override;
|
|
|
|
/// Parse until the end of a statement or a comma is encountered,
|
|
/// return the contents from the current token up to the end or comma.
|
|
StringRef parseStringToComma();
|
|
|
|
enum class AssignmentKind {
|
|
Set,
|
|
Equiv,
|
|
Equal,
|
|
LTOSetConditional,
|
|
};
|
|
|
|
bool parseAssignment(StringRef Name, AssignmentKind Kind);
|
|
|
|
unsigned getBinOpPrecedence(AsmToken::TokenKind K,
|
|
MCBinaryExpr::Opcode &Kind);
|
|
|
|
bool parseBinOpRHS(unsigned Precedence, const MCExpr *&Res, SMLoc &EndLoc);
|
|
bool parseParenExpr(const MCExpr *&Res, SMLoc &EndLoc);
|
|
bool parseBracketExpr(const MCExpr *&Res, SMLoc &EndLoc);
|
|
|
|
bool parseRegisterOrRegisterNumber(int64_t &Register, SMLoc DirectiveLoc);
|
|
|
|
bool parseCVFunctionId(int64_t &FunctionId, StringRef DirectiveName);
|
|
bool parseCVFileId(int64_t &FileId, StringRef DirectiveName);
|
|
|
|
// Generic (target and platform independent) directive parsing.
|
|
enum DirectiveKind {
|
|
DK_NO_DIRECTIVE, // Placeholder
|
|
DK_SET,
|
|
DK_EQU,
|
|
DK_EQUIV,
|
|
DK_ASCII,
|
|
DK_ASCIZ,
|
|
DK_STRING,
|
|
DK_BYTE,
|
|
DK_SHORT,
|
|
DK_RELOC,
|
|
DK_VALUE,
|
|
DK_2BYTE,
|
|
DK_LONG,
|
|
DK_INT,
|
|
DK_4BYTE,
|
|
DK_QUAD,
|
|
DK_8BYTE,
|
|
DK_OCTA,
|
|
DK_DC,
|
|
DK_DC_A,
|
|
DK_DC_B,
|
|
DK_DC_D,
|
|
DK_DC_L,
|
|
DK_DC_S,
|
|
DK_DC_W,
|
|
DK_DC_X,
|
|
DK_DCB,
|
|
DK_DCB_B,
|
|
DK_DCB_D,
|
|
DK_DCB_L,
|
|
DK_DCB_S,
|
|
DK_DCB_W,
|
|
DK_DCB_X,
|
|
DK_DS,
|
|
DK_DS_B,
|
|
DK_DS_D,
|
|
DK_DS_L,
|
|
DK_DS_P,
|
|
DK_DS_S,
|
|
DK_DS_W,
|
|
DK_DS_X,
|
|
DK_SINGLE,
|
|
DK_FLOAT,
|
|
DK_DOUBLE,
|
|
DK_ALIGN,
|
|
DK_ALIGN32,
|
|
DK_BALIGN,
|
|
DK_BALIGNW,
|
|
DK_BALIGNL,
|
|
DK_P2ALIGN,
|
|
DK_P2ALIGNW,
|
|
DK_P2ALIGNL,
|
|
DK_ORG,
|
|
DK_FILL,
|
|
DK_ENDR,
|
|
DK_ZERO,
|
|
DK_EXTERN,
|
|
DK_GLOBL,
|
|
DK_GLOBAL,
|
|
DK_LAZY_REFERENCE,
|
|
DK_NO_DEAD_STRIP,
|
|
DK_SYMBOL_RESOLVER,
|
|
DK_PRIVATE_EXTERN,
|
|
DK_REFERENCE,
|
|
DK_WEAK_DEFINITION,
|
|
DK_WEAK_REFERENCE,
|
|
DK_WEAK_DEF_CAN_BE_HIDDEN,
|
|
DK_COLD,
|
|
DK_COMM,
|
|
DK_COMMON,
|
|
DK_LCOMM,
|
|
DK_ABORT,
|
|
DK_INCLUDE,
|
|
DK_INCBIN,
|
|
DK_CODE16,
|
|
DK_CODE16GCC,
|
|
DK_REPT,
|
|
DK_IRP,
|
|
DK_IRPC,
|
|
DK_IF,
|
|
DK_IFEQ,
|
|
DK_IFGE,
|
|
DK_IFGT,
|
|
DK_IFLE,
|
|
DK_IFLT,
|
|
DK_IFNE,
|
|
DK_IFB,
|
|
DK_IFNB,
|
|
DK_IFC,
|
|
DK_IFEQS,
|
|
DK_IFNC,
|
|
DK_IFNES,
|
|
DK_IFDEF,
|
|
DK_IFNDEF,
|
|
DK_IFNOTDEF,
|
|
DK_ELSEIF,
|
|
DK_ELSE,
|
|
DK_ENDIF,
|
|
DK_SPACE,
|
|
DK_SKIP,
|
|
DK_FILE,
|
|
DK_LINE,
|
|
DK_LOC,
|
|
DK_LOC_LABEL,
|
|
DK_STABS,
|
|
DK_CV_FILE,
|
|
DK_CV_FUNC_ID,
|
|
DK_CV_INLINE_SITE_ID,
|
|
DK_CV_LOC,
|
|
DK_CV_LINETABLE,
|
|
DK_CV_INLINE_LINETABLE,
|
|
DK_CV_DEF_RANGE,
|
|
DK_CV_STRINGTABLE,
|
|
DK_CV_STRING,
|
|
DK_CV_FILECHECKSUMS,
|
|
DK_CV_FILECHECKSUM_OFFSET,
|
|
DK_CV_FPO_DATA,
|
|
DK_CFI_SECTIONS,
|
|
DK_CFI_STARTPROC,
|
|
DK_CFI_ENDPROC,
|
|
DK_CFI_DEF_CFA,
|
|
DK_CFI_DEF_CFA_OFFSET,
|
|
DK_CFI_ADJUST_CFA_OFFSET,
|
|
DK_CFI_DEF_CFA_REGISTER,
|
|
DK_CFI_LLVM_DEF_ASPACE_CFA,
|
|
DK_CFI_OFFSET,
|
|
DK_CFI_REL_OFFSET,
|
|
DK_CFI_PERSONALITY,
|
|
DK_CFI_LSDA,
|
|
DK_CFI_REMEMBER_STATE,
|
|
DK_CFI_RESTORE_STATE,
|
|
DK_CFI_SAME_VALUE,
|
|
DK_CFI_RESTORE,
|
|
DK_CFI_ESCAPE,
|
|
DK_CFI_RETURN_COLUMN,
|
|
DK_CFI_SIGNAL_FRAME,
|
|
DK_CFI_UNDEFINED,
|
|
DK_CFI_REGISTER,
|
|
DK_CFI_WINDOW_SAVE,
|
|
DK_CFI_LABEL,
|
|
DK_CFI_B_KEY_FRAME,
|
|
DK_CFI_VAL_OFFSET,
|
|
DK_MACROS_ON,
|
|
DK_MACROS_OFF,
|
|
DK_ALTMACRO,
|
|
DK_NOALTMACRO,
|
|
DK_MACRO,
|
|
DK_EXITM,
|
|
DK_ENDM,
|
|
DK_ENDMACRO,
|
|
DK_PURGEM,
|
|
DK_SLEB128,
|
|
DK_ULEB128,
|
|
DK_ERR,
|
|
DK_ERROR,
|
|
DK_WARNING,
|
|
DK_PRINT,
|
|
DK_ADDRSIG,
|
|
DK_ADDRSIG_SYM,
|
|
DK_PSEUDO_PROBE,
|
|
DK_LTO_DISCARD,
|
|
DK_LTO_SET_CONDITIONAL,
|
|
DK_CFI_MTE_TAGGED_FRAME,
|
|
DK_MEMTAG,
|
|
DK_END
|
|
};
|
|
|
|
/// Maps directive name --> DirectiveKind enum, for
|
|
/// directives parsed by this class.
|
|
StringMap<DirectiveKind> DirectiveKindMap;
|
|
|
|
// Codeview def_range type parsing.
|
|
enum CVDefRangeType {
|
|
CVDR_DEFRANGE = 0, // Placeholder
|
|
CVDR_DEFRANGE_REGISTER,
|
|
CVDR_DEFRANGE_FRAMEPOINTER_REL,
|
|
CVDR_DEFRANGE_SUBFIELD_REGISTER,
|
|
CVDR_DEFRANGE_REGISTER_REL
|
|
};
|
|
|
|
/// Maps Codeview def_range types --> CVDefRangeType enum, for
|
|
/// Codeview def_range types parsed by this class.
|
|
StringMap<CVDefRangeType> CVDefRangeTypeMap;
|
|
|
|
// ".ascii", ".asciz", ".string"
|
|
bool parseDirectiveAscii(StringRef IDVal, bool ZeroTerminated);
|
|
bool parseDirectiveReloc(SMLoc DirectiveLoc); // ".reloc"
|
|
bool parseDirectiveValue(StringRef IDVal,
|
|
unsigned Size); // ".byte", ".long", ...
|
|
bool parseDirectiveOctaValue(StringRef IDVal); // ".octa", ...
|
|
bool parseDirectiveRealValue(StringRef IDVal,
|
|
const fltSemantics &); // ".single", ...
|
|
bool parseDirectiveFill(); // ".fill"
|
|
bool parseDirectiveZero(); // ".zero"
|
|
// ".set", ".equ", ".equiv", ".lto_set_conditional"
|
|
bool parseDirectiveSet(StringRef IDVal, AssignmentKind Kind);
|
|
bool parseDirectiveOrg(); // ".org"
|
|
// ".align{,32}", ".p2align{,w,l}"
|
|
bool parseDirectiveAlign(bool IsPow2, uint8_t ValueSize);
|
|
|
|
// ".file", ".line", ".loc", ".loc_label", ".stabs"
|
|
bool parseDirectiveFile(SMLoc DirectiveLoc);
|
|
bool parseDirectiveLine();
|
|
bool parseDirectiveLoc();
|
|
bool parseDirectiveLocLabel(SMLoc DirectiveLoc);
|
|
bool parseDirectiveStabs();
|
|
|
|
// ".cv_file", ".cv_func_id", ".cv_inline_site_id", ".cv_loc", ".cv_linetable",
|
|
// ".cv_inline_linetable", ".cv_def_range", ".cv_string"
|
|
bool parseDirectiveCVFile();
|
|
bool parseDirectiveCVFuncId();
|
|
bool parseDirectiveCVInlineSiteId();
|
|
bool parseDirectiveCVLoc();
|
|
bool parseDirectiveCVLinetable();
|
|
bool parseDirectiveCVInlineLinetable();
|
|
bool parseDirectiveCVDefRange();
|
|
bool parseDirectiveCVString();
|
|
bool parseDirectiveCVStringTable();
|
|
bool parseDirectiveCVFileChecksums();
|
|
bool parseDirectiveCVFileChecksumOffset();
|
|
bool parseDirectiveCVFPOData();
|
|
|
|
// .cfi directives
|
|
bool parseDirectiveCFIRegister(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFIWindowSave(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFISections();
|
|
bool parseDirectiveCFIStartProc();
|
|
bool parseDirectiveCFIEndProc();
|
|
bool parseDirectiveCFIDefCfaOffset(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFIDefCfa(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFIAdjustCfaOffset(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFIDefCfaRegister(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFILLVMDefAspaceCfa(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFIOffset(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFIRelOffset(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFIPersonalityOrLsda(bool IsPersonality);
|
|
bool parseDirectiveCFIRememberState(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFIRestoreState(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFISameValue(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFIRestore(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFIEscape(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFIReturnColumn(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFISignalFrame(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFIUndefined(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFILabel(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFIValOffset(SMLoc DirectiveLoc);
|
|
|
|
// macro directives
|
|
bool parseDirectivePurgeMacro(SMLoc DirectiveLoc);
|
|
bool parseDirectiveExitMacro(StringRef Directive);
|
|
bool parseDirectiveEndMacro(StringRef Directive);
|
|
bool parseDirectiveMacro(SMLoc DirectiveLoc);
|
|
bool parseDirectiveMacrosOnOff(StringRef Directive);
|
|
// alternate macro mode directives
|
|
bool parseDirectiveAltmacro(StringRef Directive);
|
|
|
|
// ".space", ".skip"
|
|
bool parseDirectiveSpace(StringRef IDVal);
|
|
|
|
// ".dcb"
|
|
bool parseDirectiveDCB(StringRef IDVal, unsigned Size);
|
|
bool parseDirectiveRealDCB(StringRef IDVal, const fltSemantics &);
|
|
// ".ds"
|
|
bool parseDirectiveDS(StringRef IDVal, unsigned Size);
|
|
|
|
// .sleb128 (Signed=true) and .uleb128 (Signed=false)
|
|
bool parseDirectiveLEB128(bool Signed);
|
|
|
|
/// Parse a directive like ".globl" which
|
|
/// accepts a single symbol (which should be a label or an external).
|
|
bool parseDirectiveSymbolAttribute(MCSymbolAttr Attr);
|
|
|
|
bool parseDirectiveComm(bool IsLocal); // ".comm" and ".lcomm"
|
|
|
|
bool parseDirectiveAbort(SMLoc DirectiveLoc); // ".abort"
|
|
bool parseDirectiveInclude(); // ".include"
|
|
bool parseDirectiveIncbin(); // ".incbin"
|
|
|
|
// ".if", ".ifeq", ".ifge", ".ifgt" , ".ifle", ".iflt" or ".ifne"
|
|
bool parseDirectiveIf(SMLoc DirectiveLoc, DirectiveKind DirKind);
|
|
// ".ifb" or ".ifnb", depending on ExpectBlank.
|
|
bool parseDirectiveIfb(SMLoc DirectiveLoc, bool ExpectBlank);
|
|
// ".ifc" or ".ifnc", depending on ExpectEqual.
|
|
bool parseDirectiveIfc(SMLoc DirectiveLoc, bool ExpectEqual);
|
|
// ".ifeqs" or ".ifnes", depending on ExpectEqual.
|
|
bool parseDirectiveIfeqs(SMLoc DirectiveLoc, bool ExpectEqual);
|
|
// ".ifdef" or ".ifndef", depending on expect_defined
|
|
bool parseDirectiveIfdef(SMLoc DirectiveLoc, bool expect_defined);
|
|
bool parseDirectiveElseIf(SMLoc DirectiveLoc); // ".elseif"
|
|
bool parseDirectiveElse(SMLoc DirectiveLoc); // ".else"
|
|
bool parseDirectiveEndIf(SMLoc DirectiveLoc); // .endif
|
|
bool parseEscapedString(std::string &Data) override;
|
|
bool parseAngleBracketString(std::string &Data) override;
|
|
|
|
// Macro-like directives
|
|
MCAsmMacro *parseMacroLikeBody(SMLoc DirectiveLoc);
|
|
void instantiateMacroLikeBody(MCAsmMacro *M, SMLoc DirectiveLoc,
|
|
raw_svector_ostream &OS);
|
|
bool parseDirectiveRept(SMLoc DirectiveLoc, StringRef Directive);
|
|
bool parseDirectiveIrp(SMLoc DirectiveLoc); // ".irp"
|
|
bool parseDirectiveIrpc(SMLoc DirectiveLoc); // ".irpc"
|
|
bool parseDirectiveEndr(SMLoc DirectiveLoc); // ".endr"
|
|
|
|
// "_emit" or "__emit"
|
|
bool parseDirectiveMSEmit(SMLoc DirectiveLoc, ParseStatementInfo &Info,
|
|
size_t Len);
|
|
|
|
// "align"
|
|
bool parseDirectiveMSAlign(SMLoc DirectiveLoc, ParseStatementInfo &Info);
|
|
|
|
// "end"
|
|
bool parseDirectiveEnd(SMLoc DirectiveLoc);
|
|
|
|
// ".err" or ".error"
|
|
bool parseDirectiveError(SMLoc DirectiveLoc, bool WithMessage);
|
|
|
|
// ".warning"
|
|
bool parseDirectiveWarning(SMLoc DirectiveLoc);
|
|
|
|
// .print <double-quotes-string>
|
|
bool parseDirectivePrint(SMLoc DirectiveLoc);
|
|
|
|
// .pseudoprobe
|
|
bool parseDirectivePseudoProbe();
|
|
|
|
// ".lto_discard"
|
|
bool parseDirectiveLTODiscard();
|
|
|
|
// Directives to support address-significance tables.
|
|
bool parseDirectiveAddrsig();
|
|
bool parseDirectiveAddrsigSym();
|
|
|
|
void initializeDirectiveKindMap();
|
|
void initializeCVDefRangeTypeMap();
|
|
};
|
|
|
|
class HLASMAsmParser final : public AsmParser {
|
|
private:
|
|
AsmLexer &Lexer;
|
|
MCStreamer &Out;
|
|
|
|
void lexLeadingSpaces() {
|
|
while (Lexer.is(AsmToken::Space))
|
|
Lexer.Lex();
|
|
}
|
|
|
|
bool parseAsHLASMLabel(ParseStatementInfo &Info, MCAsmParserSemaCallback *SI);
|
|
bool parseAsMachineInstruction(ParseStatementInfo &Info,
|
|
MCAsmParserSemaCallback *SI);
|
|
|
|
public:
|
|
HLASMAsmParser(SourceMgr &SM, MCContext &Ctx, MCStreamer &Out,
|
|
const MCAsmInfo &MAI, unsigned CB = 0)
|
|
: AsmParser(SM, Ctx, Out, MAI, CB), Lexer(getLexer()), Out(Out) {
|
|
Lexer.setSkipSpace(false);
|
|
Lexer.setAllowHashInIdentifier(true);
|
|
Lexer.setLexHLASMIntegers(true);
|
|
Lexer.setLexHLASMStrings(true);
|
|
}
|
|
|
|
~HLASMAsmParser() { Lexer.setSkipSpace(true); }
|
|
|
|
bool parseStatement(ParseStatementInfo &Info,
|
|
MCAsmParserSemaCallback *SI) override;
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
namespace llvm {
|
|
|
|
extern cl::opt<unsigned> AsmMacroMaxNestingDepth;
|
|
|
|
extern MCAsmParserExtension *createDarwinAsmParser();
|
|
extern MCAsmParserExtension *createELFAsmParser();
|
|
extern MCAsmParserExtension *createCOFFAsmParser();
|
|
extern MCAsmParserExtension *createGOFFAsmParser();
|
|
extern MCAsmParserExtension *createXCOFFAsmParser();
|
|
extern MCAsmParserExtension *createWasmAsmParser();
|
|
|
|
} // end namespace llvm
|
|
|
|
AsmParser::AsmParser(SourceMgr &SM, MCContext &Ctx, MCStreamer &Out,
|
|
const MCAsmInfo &MAI, unsigned CB = 0)
|
|
: MCAsmParser(Ctx, Out, SM, MAI), CurBuffer(CB ? CB : SM.getMainFileID()),
|
|
MacrosEnabledFlag(true) {
|
|
HadError = false;
|
|
// Save the old handler.
|
|
SavedDiagHandler = SrcMgr.getDiagHandler();
|
|
SavedDiagContext = SrcMgr.getDiagContext();
|
|
// Set our own handler which calls the saved handler.
|
|
SrcMgr.setDiagHandler(DiagHandler, this);
|
|
Lexer.setBuffer(SrcMgr.getMemoryBuffer(CurBuffer)->getBuffer());
|
|
// Make MCStreamer aware of the StartTokLoc for locations in diagnostics.
|
|
Out.setStartTokLocPtr(&StartTokLoc);
|
|
|
|
// Initialize the platform / file format parser.
|
|
switch (Ctx.getObjectFileType()) {
|
|
case MCContext::IsCOFF:
|
|
PlatformParser.reset(createCOFFAsmParser());
|
|
break;
|
|
case MCContext::IsMachO:
|
|
PlatformParser.reset(createDarwinAsmParser());
|
|
IsDarwin = true;
|
|
break;
|
|
case MCContext::IsELF:
|
|
PlatformParser.reset(createELFAsmParser());
|
|
break;
|
|
case MCContext::IsGOFF:
|
|
PlatformParser.reset(createGOFFAsmParser());
|
|
break;
|
|
case MCContext::IsSPIRV:
|
|
report_fatal_error(
|
|
"Need to implement createSPIRVAsmParser for SPIRV format.");
|
|
break;
|
|
case MCContext::IsWasm:
|
|
PlatformParser.reset(createWasmAsmParser());
|
|
break;
|
|
case MCContext::IsXCOFF:
|
|
PlatformParser.reset(createXCOFFAsmParser());
|
|
break;
|
|
case MCContext::IsDXContainer:
|
|
report_fatal_error("DXContainer is not supported yet");
|
|
break;
|
|
}
|
|
|
|
PlatformParser->Initialize(*this);
|
|
initializeDirectiveKindMap();
|
|
initializeCVDefRangeTypeMap();
|
|
}
|
|
|
|
AsmParser::~AsmParser() {
|
|
assert((HadError || ActiveMacros.empty()) &&
|
|
"Unexpected active macro instantiation!");
|
|
|
|
// Remove MCStreamer's reference to the parser SMLoc.
|
|
Out.setStartTokLocPtr(nullptr);
|
|
// Restore the saved diagnostics handler and context for use during
|
|
// finalization.
|
|
SrcMgr.setDiagHandler(SavedDiagHandler, SavedDiagContext);
|
|
}
|
|
|
|
void AsmParser::printMacroInstantiations() {
|
|
// Print the active macro instantiation stack.
|
|
for (MacroInstantiation *M : reverse(ActiveMacros))
|
|
printMessage(M->InstantiationLoc, SourceMgr::DK_Note,
|
|
"while in macro instantiation");
|
|
}
|
|
|
|
void AsmParser::Note(SMLoc L, const Twine &Msg, SMRange Range) {
|
|
printPendingErrors();
|
|
printMessage(L, SourceMgr::DK_Note, Msg, Range);
|
|
printMacroInstantiations();
|
|
}
|
|
|
|
bool AsmParser::Warning(SMLoc L, const Twine &Msg, SMRange Range) {
|
|
if(getTargetParser().getTargetOptions().MCNoWarn)
|
|
return false;
|
|
if (getTargetParser().getTargetOptions().MCFatalWarnings)
|
|
return Error(L, Msg, Range);
|
|
printMessage(L, SourceMgr::DK_Warning, Msg, Range);
|
|
printMacroInstantiations();
|
|
return false;
|
|
}
|
|
|
|
bool AsmParser::printError(SMLoc L, const Twine &Msg, SMRange Range) {
|
|
HadError = true;
|
|
printMessage(L, SourceMgr::DK_Error, Msg, Range);
|
|
printMacroInstantiations();
|
|
return true;
|
|
}
|
|
|
|
bool AsmParser::enterIncludeFile(const std::string &Filename) {
|
|
std::string IncludedFile;
|
|
unsigned NewBuf =
|
|
SrcMgr.AddIncludeFile(Filename, Lexer.getLoc(), IncludedFile);
|
|
if (!NewBuf)
|
|
return true;
|
|
|
|
CurBuffer = NewBuf;
|
|
Lexer.setBuffer(SrcMgr.getMemoryBuffer(CurBuffer)->getBuffer());
|
|
return false;
|
|
}
|
|
|
|
/// Process the specified .incbin file by searching for it in the include paths
|
|
/// then just emitting the byte contents of the file to the streamer. This
|
|
/// returns true on failure.
|
|
bool AsmParser::processIncbinFile(const std::string &Filename, int64_t Skip,
|
|
const MCExpr *Count, SMLoc Loc) {
|
|
std::string IncludedFile;
|
|
unsigned NewBuf =
|
|
SrcMgr.AddIncludeFile(Filename, Lexer.getLoc(), IncludedFile);
|
|
if (!NewBuf)
|
|
return true;
|
|
|
|
// Pick up the bytes from the file and emit them.
|
|
StringRef Bytes = SrcMgr.getMemoryBuffer(NewBuf)->getBuffer();
|
|
Bytes = Bytes.drop_front(Skip);
|
|
if (Count) {
|
|
int64_t Res;
|
|
if (!Count->evaluateAsAbsolute(Res, getStreamer().getAssemblerPtr()))
|
|
return Error(Loc, "expected absolute expression");
|
|
if (Res < 0)
|
|
return Warning(Loc, "negative count has no effect");
|
|
Bytes = Bytes.take_front(Res);
|
|
}
|
|
getStreamer().emitBytes(Bytes);
|
|
return false;
|
|
}
|
|
|
|
void AsmParser::jumpToLoc(SMLoc Loc, unsigned InBuffer) {
|
|
CurBuffer = InBuffer ? InBuffer : SrcMgr.FindBufferContainingLoc(Loc);
|
|
Lexer.setBuffer(SrcMgr.getMemoryBuffer(CurBuffer)->getBuffer(),
|
|
Loc.getPointer());
|
|
}
|
|
|
|
const AsmToken &AsmParser::Lex() {
|
|
if (Lexer.getTok().is(AsmToken::Error))
|
|
Error(Lexer.getErrLoc(), Lexer.getErr());
|
|
|
|
// if it's a end of statement with a comment in it
|
|
if (getTok().is(AsmToken::EndOfStatement)) {
|
|
// if this is a line comment output it.
|
|
if (!getTok().getString().empty() && getTok().getString().front() != '\n' &&
|
|
getTok().getString().front() != '\r' && MAI.preserveAsmComments())
|
|
Out.addExplicitComment(Twine(getTok().getString()));
|
|
}
|
|
|
|
const AsmToken *tok = &Lexer.Lex();
|
|
|
|
// Parse comments here to be deferred until end of next statement.
|
|
while (tok->is(AsmToken::Comment)) {
|
|
if (MAI.preserveAsmComments())
|
|
Out.addExplicitComment(Twine(tok->getString()));
|
|
tok = &Lexer.Lex();
|
|
}
|
|
|
|
if (tok->is(AsmToken::Eof)) {
|
|
// If this is the end of an included file, pop the parent file off the
|
|
// include stack.
|
|
SMLoc ParentIncludeLoc = SrcMgr.getParentIncludeLoc(CurBuffer);
|
|
if (ParentIncludeLoc != SMLoc()) {
|
|
jumpToLoc(ParentIncludeLoc);
|
|
return Lex();
|
|
}
|
|
}
|
|
|
|
return *tok;
|
|
}
|
|
|
|
bool AsmParser::enabledGenDwarfForAssembly() {
|
|
// Check whether the user specified -g.
|
|
if (!getContext().getGenDwarfForAssembly())
|
|
return false;
|
|
// If we haven't encountered any .file directives (which would imply that
|
|
// the assembler source was produced with debug info already) then emit one
|
|
// describing the assembler source file itself.
|
|
if (getContext().getGenDwarfFileNumber() == 0) {
|
|
const MCDwarfFile &RootFile =
|
|
getContext().getMCDwarfLineTable(/*CUID=*/0).getRootFile();
|
|
getContext().setGenDwarfFileNumber(getStreamer().emitDwarfFileDirective(
|
|
/*CUID=*/0, getContext().getCompilationDir(), RootFile.Name,
|
|
RootFile.Checksum, RootFile.Source));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool AsmParser::Run(bool NoInitialTextSection, bool NoFinalize) {
|
|
LTODiscardSymbols.clear();
|
|
|
|
// Create the initial section, if requested.
|
|
if (!NoInitialTextSection)
|
|
Out.initSections(false, getTargetParser().getSTI());
|
|
|
|
// Prime the lexer.
|
|
Lex();
|
|
|
|
HadError = false;
|
|
AsmCond StartingCondState = TheCondState;
|
|
SmallVector<AsmRewrite, 4> AsmStrRewrites;
|
|
|
|
// If we are generating dwarf for assembly source files save the initial text
|
|
// section. (Don't use enabledGenDwarfForAssembly() here, as we aren't
|
|
// emitting any actual debug info yet and haven't had a chance to parse any
|
|
// embedded .file directives.)
|
|
if (getContext().getGenDwarfForAssembly()) {
|
|
MCSection *Sec = getStreamer().getCurrentSectionOnly();
|
|
if (!Sec->getBeginSymbol()) {
|
|
MCSymbol *SectionStartSym = getContext().createTempSymbol();
|
|
getStreamer().emitLabel(SectionStartSym);
|
|
Sec->setBeginSymbol(SectionStartSym);
|
|
}
|
|
bool InsertResult = getContext().addGenDwarfSection(Sec);
|
|
assert(InsertResult && ".text section should not have debug info yet");
|
|
(void)InsertResult;
|
|
}
|
|
|
|
getTargetParser().onBeginOfFile();
|
|
|
|
// While we have input, parse each statement.
|
|
while (Lexer.isNot(AsmToken::Eof)) {
|
|
ParseStatementInfo Info(&AsmStrRewrites);
|
|
bool HasError = parseStatement(Info, nullptr);
|
|
|
|
// If we have a Lexer Error we are on an Error Token. Load in Lexer Error
|
|
// for printing ErrMsg via Lex() only if no (presumably better) parser error
|
|
// exists.
|
|
if (HasError && !hasPendingError() && Lexer.getTok().is(AsmToken::Error))
|
|
Lex();
|
|
|
|
// parseStatement returned true so may need to emit an error.
|
|
printPendingErrors();
|
|
|
|
// Skipping to the next line if needed.
|
|
if (HasError && !getLexer().justConsumedEOL())
|
|
eatToEndOfStatement();
|
|
}
|
|
|
|
getTargetParser().onEndOfFile();
|
|
printPendingErrors();
|
|
|
|
// All errors should have been emitted.
|
|
assert(!hasPendingError() && "unexpected error from parseStatement");
|
|
|
|
if (TheCondState.TheCond != StartingCondState.TheCond ||
|
|
TheCondState.Ignore != StartingCondState.Ignore)
|
|
printError(getTok().getLoc(), "unmatched .ifs or .elses");
|
|
// Check to see there are no empty DwarfFile slots.
|
|
const auto &LineTables = getContext().getMCDwarfLineTables();
|
|
if (!LineTables.empty()) {
|
|
unsigned Index = 0;
|
|
for (const auto &File : LineTables.begin()->second.getMCDwarfFiles()) {
|
|
if (File.Name.empty() && Index != 0)
|
|
printError(getTok().getLoc(), "unassigned file number: " +
|
|
Twine(Index) +
|
|
" for .file directives");
|
|
++Index;
|
|
}
|
|
}
|
|
|
|
// Check to see that all assembler local symbols were actually defined.
|
|
// Targets that don't do subsections via symbols may not want this, though,
|
|
// so conservatively exclude them. Only do this if we're finalizing, though,
|
|
// as otherwise we won't necessarily have seen everything yet.
|
|
if (!NoFinalize) {
|
|
if (MAI.hasSubsectionsViaSymbols()) {
|
|
for (const auto &TableEntry : getContext().getSymbols()) {
|
|
MCSymbol *Sym = TableEntry.getValue().Symbol;
|
|
// Variable symbols may not be marked as defined, so check those
|
|
// explicitly. If we know it's a variable, we have a definition for
|
|
// the purposes of this check.
|
|
if (Sym && Sym->isTemporary() && !Sym->isVariable() &&
|
|
!Sym->isDefined())
|
|
// FIXME: We would really like to refer back to where the symbol was
|
|
// first referenced for a source location. We need to add something
|
|
// to track that. Currently, we just point to the end of the file.
|
|
printError(getTok().getLoc(), "assembler local symbol '" +
|
|
Sym->getName() + "' not defined");
|
|
}
|
|
}
|
|
|
|
// Temporary symbols like the ones for directional jumps don't go in the
|
|
// symbol table. They also need to be diagnosed in all (final) cases.
|
|
for (std::tuple<SMLoc, CppHashInfoTy, MCSymbol *> &LocSym : DirLabels) {
|
|
if (std::get<2>(LocSym)->isUndefined()) {
|
|
// Reset the state of any "# line file" directives we've seen to the
|
|
// context as it was at the diagnostic site.
|
|
CppHashInfo = std::get<1>(LocSym);
|
|
printError(std::get<0>(LocSym), "directional label undefined");
|
|
}
|
|
}
|
|
}
|
|
// Finalize the output stream if there are no errors and if the client wants
|
|
// us to.
|
|
if (!HadError && !NoFinalize) {
|
|
if (auto *TS = Out.getTargetStreamer())
|
|
TS->emitConstantPools();
|
|
|
|
Out.finish(Lexer.getLoc());
|
|
}
|
|
|
|
return HadError || getContext().hadError();
|
|
}
|
|
|
|
bool AsmParser::checkForValidSection() {
|
|
if (!ParsingMSInlineAsm && !getStreamer().getCurrentFragment()) {
|
|
Out.initSections(false, getTargetParser().getSTI());
|
|
return Error(getTok().getLoc(),
|
|
"expected section directive before assembly directive");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Throw away the rest of the line for testing purposes.
|
|
void AsmParser::eatToEndOfStatement() {
|
|
while (Lexer.isNot(AsmToken::EndOfStatement) && Lexer.isNot(AsmToken::Eof))
|
|
Lexer.Lex();
|
|
|
|
// Eat EOL.
|
|
if (Lexer.is(AsmToken::EndOfStatement))
|
|
Lexer.Lex();
|
|
}
|
|
|
|
StringRef AsmParser::parseStringToEndOfStatement() {
|
|
const char *Start = getTok().getLoc().getPointer();
|
|
|
|
while (Lexer.isNot(AsmToken::EndOfStatement) && Lexer.isNot(AsmToken::Eof))
|
|
Lexer.Lex();
|
|
|
|
const char *End = getTok().getLoc().getPointer();
|
|
return StringRef(Start, End - Start);
|
|
}
|
|
|
|
StringRef AsmParser::parseStringToComma() {
|
|
const char *Start = getTok().getLoc().getPointer();
|
|
|
|
while (Lexer.isNot(AsmToken::EndOfStatement) &&
|
|
Lexer.isNot(AsmToken::Comma) && Lexer.isNot(AsmToken::Eof))
|
|
Lexer.Lex();
|
|
|
|
const char *End = getTok().getLoc().getPointer();
|
|
return StringRef(Start, End - Start);
|
|
}
|
|
|
|
/// Parse a paren expression and return it.
|
|
/// NOTE: This assumes the leading '(' has already been consumed.
|
|
///
|
|
/// parenexpr ::= expr)
|
|
///
|
|
bool AsmParser::parseParenExpr(const MCExpr *&Res, SMLoc &EndLoc) {
|
|
if (parseExpression(Res))
|
|
return true;
|
|
EndLoc = Lexer.getTok().getEndLoc();
|
|
return parseRParen();
|
|
}
|
|
|
|
/// Parse a bracket expression and return it.
|
|
/// NOTE: This assumes the leading '[' has already been consumed.
|
|
///
|
|
/// bracketexpr ::= expr]
|
|
///
|
|
bool AsmParser::parseBracketExpr(const MCExpr *&Res, SMLoc &EndLoc) {
|
|
if (parseExpression(Res))
|
|
return true;
|
|
EndLoc = getTok().getEndLoc();
|
|
if (parseToken(AsmToken::RBrac, "expected ']' in brackets expression"))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/// Parse a primary expression and return it.
|
|
/// primaryexpr ::= (parenexpr
|
|
/// primaryexpr ::= symbol
|
|
/// primaryexpr ::= number
|
|
/// primaryexpr ::= '.'
|
|
/// primaryexpr ::= ~,+,- primaryexpr
|
|
bool AsmParser::parsePrimaryExpr(const MCExpr *&Res, SMLoc &EndLoc,
|
|
AsmTypeInfo *TypeInfo) {
|
|
SMLoc FirstTokenLoc = getLexer().getLoc();
|
|
AsmToken::TokenKind FirstTokenKind = Lexer.getKind();
|
|
switch (FirstTokenKind) {
|
|
default:
|
|
return TokError("unknown token in expression");
|
|
// If we have an error assume that we've already handled it.
|
|
case AsmToken::Error:
|
|
return true;
|
|
case AsmToken::Exclaim:
|
|
Lex(); // Eat the operator.
|
|
if (parsePrimaryExpr(Res, EndLoc, TypeInfo))
|
|
return true;
|
|
Res = MCUnaryExpr::createLNot(Res, getContext(), FirstTokenLoc);
|
|
return false;
|
|
case AsmToken::Dollar:
|
|
case AsmToken::Star:
|
|
case AsmToken::At:
|
|
case AsmToken::String:
|
|
case AsmToken::Identifier: {
|
|
StringRef Identifier;
|
|
if (parseIdentifier(Identifier)) {
|
|
// We may have failed but '$'|'*' may be a valid token in context of
|
|
// the current PC.
|
|
if (getTok().is(AsmToken::Dollar) || getTok().is(AsmToken::Star)) {
|
|
bool ShouldGenerateTempSymbol = false;
|
|
if ((getTok().is(AsmToken::Dollar) && MAI.getDollarIsPC()) ||
|
|
(getTok().is(AsmToken::Star) && MAI.isHLASM()))
|
|
ShouldGenerateTempSymbol = true;
|
|
|
|
if (!ShouldGenerateTempSymbol)
|
|
return Error(FirstTokenLoc, "invalid token in expression");
|
|
|
|
// Eat the '$'|'*' token.
|
|
Lex();
|
|
// This is either a '$'|'*' reference, which references the current PC.
|
|
// Emit a temporary label to the streamer and refer to it.
|
|
MCSymbol *Sym = Ctx.createTempSymbol();
|
|
Out.emitLabel(Sym);
|
|
Res = MCSymbolRefExpr::create(Sym, getContext());
|
|
EndLoc = FirstTokenLoc;
|
|
return false;
|
|
}
|
|
}
|
|
// Parse an optional relocation specifier.
|
|
std::pair<StringRef, StringRef> Split;
|
|
if (MAI.useAtForSpecifier()) {
|
|
if (FirstTokenKind == AsmToken::String) {
|
|
if (Lexer.is(AsmToken::At)) {
|
|
Lex(); // eat @
|
|
SMLoc AtLoc = getLexer().getLoc();
|
|
StringRef VName;
|
|
if (parseIdentifier(VName))
|
|
return Error(AtLoc, "expected symbol variant after '@'");
|
|
|
|
Split = std::make_pair(Identifier, VName);
|
|
}
|
|
} else if (Lexer.getAllowAtInIdentifier()) {
|
|
Split = Identifier.split('@');
|
|
}
|
|
} else if (MAI.useParensForSpecifier() &&
|
|
parseOptionalToken(AsmToken::LParen)) {
|
|
StringRef VName;
|
|
parseIdentifier(VName);
|
|
if (parseRParen())
|
|
return true;
|
|
Split = std::make_pair(Identifier, VName);
|
|
}
|
|
|
|
EndLoc = SMLoc::getFromPointer(Identifier.end());
|
|
|
|
// This is a symbol reference.
|
|
StringRef SymbolName = Identifier;
|
|
if (SymbolName.empty())
|
|
return Error(getLexer().getLoc(), "expected a symbol reference");
|
|
|
|
// Lookup the @specifier if used.
|
|
uint16_t Spec = 0;
|
|
if (!Split.second.empty()) {
|
|
auto MaybeSpecifier = MAI.getSpecifierForName(Split.second);
|
|
if (MaybeSpecifier) {
|
|
SymbolName = Split.first;
|
|
Spec = *MaybeSpecifier;
|
|
} else if (!MAI.doesAllowAtInName()) {
|
|
return Error(SMLoc::getFromPointer(Split.second.begin()),
|
|
"invalid variant '" + Split.second + "'");
|
|
}
|
|
}
|
|
|
|
MCSymbol *Sym = getContext().getInlineAsmLabel(SymbolName);
|
|
if (!Sym)
|
|
Sym = getContext().getOrCreateSymbol(MAI.isHLASM() ? SymbolName.upper()
|
|
: SymbolName);
|
|
|
|
// If this is an absolute variable reference, substitute it now to preserve
|
|
// semantics in the face of reassignment.
|
|
if (Sym->isVariable()) {
|
|
auto V = Sym->getVariableValue();
|
|
bool DoInline = isa<MCConstantExpr>(V) && !Spec;
|
|
if (auto TV = dyn_cast<MCTargetExpr>(V))
|
|
DoInline = TV->inlineAssignedExpr();
|
|
if (DoInline) {
|
|
if (Spec)
|
|
return Error(EndLoc, "unexpected modifier on variable reference");
|
|
Res = Sym->getVariableValue();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Otherwise create a symbol ref.
|
|
Res = MCSymbolRefExpr::create(Sym, Spec, getContext(), FirstTokenLoc);
|
|
return false;
|
|
}
|
|
case AsmToken::BigNum:
|
|
return TokError("literal value out of range for directive");
|
|
case AsmToken::Integer: {
|
|
SMLoc Loc = getTok().getLoc();
|
|
int64_t IntVal = getTok().getIntVal();
|
|
Res = MCConstantExpr::create(IntVal, getContext());
|
|
EndLoc = Lexer.getTok().getEndLoc();
|
|
Lex(); // Eat token.
|
|
// Look for 'b' or 'f' following an Integer as a directional label
|
|
if (Lexer.getKind() == AsmToken::Identifier) {
|
|
StringRef IDVal = getTok().getString();
|
|
// Lookup the symbol variant if used.
|
|
std::pair<StringRef, StringRef> Split = IDVal.split('@');
|
|
uint16_t Spec = 0;
|
|
if (Split.first.size() != IDVal.size()) {
|
|
auto MaybeSpec = MAI.getSpecifierForName(Split.second);
|
|
if (!MaybeSpec)
|
|
return TokError("invalid variant '" + Split.second + "'");
|
|
IDVal = Split.first;
|
|
Spec = *MaybeSpec;
|
|
}
|
|
if (IDVal == "f" || IDVal == "b") {
|
|
MCSymbol *Sym =
|
|
Ctx.getDirectionalLocalSymbol(IntVal, IDVal == "b");
|
|
Res = MCSymbolRefExpr::create(Sym, Spec, getContext(), Loc);
|
|
if (IDVal == "b" && Sym->isUndefined())
|
|
return Error(Loc, "directional label undefined");
|
|
DirLabels.push_back(std::make_tuple(Loc, CppHashInfo, Sym));
|
|
EndLoc = Lexer.getTok().getEndLoc();
|
|
Lex(); // Eat identifier.
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
case AsmToken::Real: {
|
|
APFloat RealVal(APFloat::IEEEdouble(), getTok().getString());
|
|
uint64_t IntVal = RealVal.bitcastToAPInt().getZExtValue();
|
|
Res = MCConstantExpr::create(IntVal, getContext());
|
|
EndLoc = Lexer.getTok().getEndLoc();
|
|
Lex(); // Eat token.
|
|
return false;
|
|
}
|
|
case AsmToken::Dot: {
|
|
if (MAI.isHLASM())
|
|
return TokError("cannot use . as current PC");
|
|
|
|
// This is a '.' reference, which references the current PC. Emit a
|
|
// temporary label to the streamer and refer to it.
|
|
MCSymbol *Sym = Ctx.createTempSymbol();
|
|
Out.emitLabel(Sym);
|
|
Res = MCSymbolRefExpr::create(Sym, getContext());
|
|
EndLoc = Lexer.getTok().getEndLoc();
|
|
Lex(); // Eat identifier.
|
|
return false;
|
|
}
|
|
case AsmToken::LParen:
|
|
Lex(); // Eat the '('.
|
|
return parseParenExpr(Res, EndLoc);
|
|
case AsmToken::LBrac:
|
|
if (!PlatformParser->HasBracketExpressions())
|
|
return TokError("brackets expression not supported on this target");
|
|
Lex(); // Eat the '['.
|
|
return parseBracketExpr(Res, EndLoc);
|
|
case AsmToken::Minus:
|
|
Lex(); // Eat the operator.
|
|
if (parsePrimaryExpr(Res, EndLoc, TypeInfo))
|
|
return true;
|
|
Res = MCUnaryExpr::createMinus(Res, getContext(), FirstTokenLoc);
|
|
return false;
|
|
case AsmToken::Plus:
|
|
Lex(); // Eat the operator.
|
|
if (parsePrimaryExpr(Res, EndLoc, TypeInfo))
|
|
return true;
|
|
Res = MCUnaryExpr::createPlus(Res, getContext(), FirstTokenLoc);
|
|
return false;
|
|
case AsmToken::Tilde:
|
|
Lex(); // Eat the operator.
|
|
if (parsePrimaryExpr(Res, EndLoc, TypeInfo))
|
|
return true;
|
|
Res = MCUnaryExpr::createNot(Res, getContext(), FirstTokenLoc);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool AsmParser::parseExpression(const MCExpr *&Res) {
|
|
SMLoc EndLoc;
|
|
return parseExpression(Res, EndLoc);
|
|
}
|
|
|
|
const MCExpr *MCAsmParser::applySpecifier(const MCExpr *E, uint32_t Spec) {
|
|
// Ask the target implementation about this expression first.
|
|
const MCExpr *NewE = getTargetParser().applySpecifier(E, Spec, Ctx);
|
|
if (NewE)
|
|
return NewE;
|
|
// Recurse over the given expression, rebuilding it to apply the given variant
|
|
// if there is exactly one symbol.
|
|
switch (E->getKind()) {
|
|
case MCExpr::Specifier:
|
|
llvm_unreachable("cannot apply another specifier to MCSpecifierExpr");
|
|
case MCExpr::Target:
|
|
case MCExpr::Constant:
|
|
return nullptr;
|
|
|
|
case MCExpr::SymbolRef: {
|
|
const MCSymbolRefExpr *SRE = cast<MCSymbolRefExpr>(E);
|
|
|
|
if (SRE->getSpecifier()) {
|
|
TokError("invalid variant on expression '" + getTok().getIdentifier() +
|
|
"' (already modified)");
|
|
return E;
|
|
}
|
|
|
|
return MCSymbolRefExpr::create(&SRE->getSymbol(), Spec, getContext(),
|
|
SRE->getLoc());
|
|
}
|
|
|
|
case MCExpr::Unary: {
|
|
const MCUnaryExpr *UE = cast<MCUnaryExpr>(E);
|
|
const MCExpr *Sub = applySpecifier(UE->getSubExpr(), Spec);
|
|
if (!Sub)
|
|
return nullptr;
|
|
return MCUnaryExpr::create(UE->getOpcode(), Sub, getContext(),
|
|
UE->getLoc());
|
|
}
|
|
|
|
case MCExpr::Binary: {
|
|
const MCBinaryExpr *BE = cast<MCBinaryExpr>(E);
|
|
const MCExpr *LHS = applySpecifier(BE->getLHS(), Spec);
|
|
const MCExpr *RHS = applySpecifier(BE->getRHS(), Spec);
|
|
|
|
if (!LHS && !RHS)
|
|
return nullptr;
|
|
|
|
if (!LHS)
|
|
LHS = BE->getLHS();
|
|
if (!RHS)
|
|
RHS = BE->getRHS();
|
|
|
|
return MCBinaryExpr::create(BE->getOpcode(), LHS, RHS, getContext(),
|
|
BE->getLoc());
|
|
}
|
|
}
|
|
|
|
llvm_unreachable("Invalid expression kind!");
|
|
}
|
|
|
|
/// This function checks if the next token is <string> type or arithmetic.
|
|
/// string that begin with character '<' must end with character '>'.
|
|
/// otherwise it is arithmetics.
|
|
/// If the function returns a 'true' value,
|
|
/// the End argument will be filled with the last location pointed to the '>'
|
|
/// character.
|
|
|
|
/// There is a gap between the AltMacro's documentation and the single quote
|
|
/// implementation. GCC does not fully support this feature and so we will not
|
|
/// support it.
|
|
/// TODO: Adding single quote as a string.
|
|
static bool isAngleBracketString(SMLoc &StrLoc, SMLoc &EndLoc) {
|
|
assert((StrLoc.getPointer() != nullptr) &&
|
|
"Argument to the function cannot be a NULL value");
|
|
const char *CharPtr = StrLoc.getPointer();
|
|
while ((*CharPtr != '>') && (*CharPtr != '\n') && (*CharPtr != '\r') &&
|
|
(*CharPtr != '\0')) {
|
|
if (*CharPtr == '!')
|
|
CharPtr++;
|
|
CharPtr++;
|
|
}
|
|
if (*CharPtr == '>') {
|
|
EndLoc = StrLoc.getFromPointer(CharPtr + 1);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// creating a string without the escape characters '!'.
|
|
static std::string angleBracketString(StringRef AltMacroStr) {
|
|
std::string Res;
|
|
for (size_t Pos = 0; Pos < AltMacroStr.size(); Pos++) {
|
|
if (AltMacroStr[Pos] == '!')
|
|
Pos++;
|
|
Res += AltMacroStr[Pos];
|
|
}
|
|
return Res;
|
|
}
|
|
|
|
bool MCAsmParser::parseAtSpecifier(const MCExpr *&Res, SMLoc &EndLoc) {
|
|
if (parseOptionalToken(AsmToken::At)) {
|
|
if (getLexer().isNot(AsmToken::Identifier))
|
|
return TokError("expected specifier following '@'");
|
|
|
|
auto Spec = MAI.getSpecifierForName(getTok().getIdentifier());
|
|
if (!Spec)
|
|
return TokError("invalid specifier '@" + getTok().getIdentifier() + "'");
|
|
|
|
const MCExpr *ModifiedRes = applySpecifier(Res, *Spec);
|
|
if (ModifiedRes)
|
|
Res = ModifiedRes;
|
|
Lex();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Parse an expression and return it.
|
|
///
|
|
/// expr ::= expr &&,|| expr -> lowest.
|
|
/// expr ::= expr |,^,&,! expr
|
|
/// expr ::= expr ==,!=,<>,<,<=,>,>= expr
|
|
/// expr ::= expr <<,>> expr
|
|
/// expr ::= expr +,- expr
|
|
/// expr ::= expr *,/,% expr -> highest.
|
|
/// expr ::= primaryexpr
|
|
///
|
|
bool AsmParser::parseExpression(const MCExpr *&Res, SMLoc &EndLoc) {
|
|
// Parse the expression.
|
|
Res = nullptr;
|
|
auto &TS = getTargetParser();
|
|
if (TS.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.getAllowAtInIdentifier() && parseOptionalToken(AsmToken::At)) {
|
|
if (Lexer.isNot(AsmToken::Identifier))
|
|
return TokError("unexpected symbol modifier following '@'");
|
|
|
|
auto Spec = MAI.getSpecifierForName(getTok().getIdentifier());
|
|
if (!Spec)
|
|
return TokError("invalid variant '" + getTok().getIdentifier() + "'");
|
|
|
|
const MCExpr *ModifiedRes = applySpecifier(Res, *Spec);
|
|
if (!ModifiedRes) {
|
|
return TokError("invalid modifier '" + getTok().getIdentifier() +
|
|
"' (no symbols present)");
|
|
}
|
|
|
|
Res = ModifiedRes;
|
|
Lex();
|
|
}
|
|
|
|
// Try to constant fold it up front, if possible. Do not exploit
|
|
// assembler here.
|
|
int64_t Value;
|
|
if (Res->evaluateAsAbsolute(Value))
|
|
Res = MCConstantExpr::create(Value, getContext());
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AsmParser::parseParenExpression(const MCExpr *&Res, SMLoc &EndLoc) {
|
|
Res = nullptr;
|
|
return parseParenExpr(Res, EndLoc) || parseBinOpRHS(1, Res, EndLoc);
|
|
}
|
|
|
|
bool AsmParser::parseAbsoluteExpression(int64_t &Res) {
|
|
const MCExpr *Expr;
|
|
|
|
SMLoc StartLoc = Lexer.getLoc();
|
|
if (parseExpression(Expr))
|
|
return true;
|
|
|
|
if (!Expr->evaluateAsAbsolute(Res, getStreamer().getAssemblerPtr()))
|
|
return Error(StartLoc, "expected absolute expression");
|
|
|
|
return false;
|
|
}
|
|
|
|
static unsigned getDarwinBinOpPrecedence(AsmToken::TokenKind K,
|
|
MCBinaryExpr::Opcode &Kind,
|
|
bool ShouldUseLogicalShr) {
|
|
switch (K) {
|
|
default:
|
|
return 0; // not a binop.
|
|
|
|
// Lowest Precedence: &&, ||
|
|
case AsmToken::AmpAmp:
|
|
Kind = MCBinaryExpr::LAnd;
|
|
return 1;
|
|
case AsmToken::PipePipe:
|
|
Kind = MCBinaryExpr::LOr;
|
|
return 1;
|
|
|
|
// Low Precedence: |, &, ^
|
|
case AsmToken::Pipe:
|
|
Kind = MCBinaryExpr::Or;
|
|
return 2;
|
|
case AsmToken::Caret:
|
|
Kind = MCBinaryExpr::Xor;
|
|
return 2;
|
|
case AsmToken::Amp:
|
|
Kind = MCBinaryExpr::And;
|
|
return 2;
|
|
|
|
// Low Intermediate Precedence: ==, !=, <>, <, <=, >, >=
|
|
case AsmToken::EqualEqual:
|
|
Kind = MCBinaryExpr::EQ;
|
|
return 3;
|
|
case AsmToken::ExclaimEqual:
|
|
case AsmToken::LessGreater:
|
|
Kind = MCBinaryExpr::NE;
|
|
return 3;
|
|
case AsmToken::Less:
|
|
Kind = MCBinaryExpr::LT;
|
|
return 3;
|
|
case AsmToken::LessEqual:
|
|
Kind = MCBinaryExpr::LTE;
|
|
return 3;
|
|
case AsmToken::Greater:
|
|
Kind = MCBinaryExpr::GT;
|
|
return 3;
|
|
case AsmToken::GreaterEqual:
|
|
Kind = MCBinaryExpr::GTE;
|
|
return 3;
|
|
|
|
// Intermediate Precedence: <<, >>
|
|
case AsmToken::LessLess:
|
|
Kind = MCBinaryExpr::Shl;
|
|
return 4;
|
|
case AsmToken::GreaterGreater:
|
|
Kind = ShouldUseLogicalShr ? MCBinaryExpr::LShr : MCBinaryExpr::AShr;
|
|
return 4;
|
|
|
|
// High Intermediate Precedence: +, -
|
|
case AsmToken::Plus:
|
|
Kind = MCBinaryExpr::Add;
|
|
return 5;
|
|
case AsmToken::Minus:
|
|
Kind = MCBinaryExpr::Sub;
|
|
return 5;
|
|
|
|
// Highest Precedence: *, /, %
|
|
case AsmToken::Star:
|
|
Kind = MCBinaryExpr::Mul;
|
|
return 6;
|
|
case AsmToken::Slash:
|
|
Kind = MCBinaryExpr::Div;
|
|
return 6;
|
|
case AsmToken::Percent:
|
|
Kind = MCBinaryExpr::Mod;
|
|
return 6;
|
|
}
|
|
}
|
|
|
|
static unsigned getGNUBinOpPrecedence(const MCAsmInfo &MAI,
|
|
AsmToken::TokenKind K,
|
|
MCBinaryExpr::Opcode &Kind,
|
|
bool ShouldUseLogicalShr) {
|
|
switch (K) {
|
|
default:
|
|
return 0; // not a binop.
|
|
|
|
// Lowest Precedence: &&, ||
|
|
case AsmToken::AmpAmp:
|
|
Kind = MCBinaryExpr::LAnd;
|
|
return 2;
|
|
case AsmToken::PipePipe:
|
|
Kind = MCBinaryExpr::LOr;
|
|
return 1;
|
|
|
|
// Low Precedence: ==, !=, <>, <, <=, >, >=
|
|
case AsmToken::EqualEqual:
|
|
Kind = MCBinaryExpr::EQ;
|
|
return 3;
|
|
case AsmToken::ExclaimEqual:
|
|
case AsmToken::LessGreater:
|
|
Kind = MCBinaryExpr::NE;
|
|
return 3;
|
|
case AsmToken::Less:
|
|
Kind = MCBinaryExpr::LT;
|
|
return 3;
|
|
case AsmToken::LessEqual:
|
|
Kind = MCBinaryExpr::LTE;
|
|
return 3;
|
|
case AsmToken::Greater:
|
|
Kind = MCBinaryExpr::GT;
|
|
return 3;
|
|
case AsmToken::GreaterEqual:
|
|
Kind = MCBinaryExpr::GTE;
|
|
return 3;
|
|
|
|
// Low Intermediate Precedence: +, -
|
|
case AsmToken::Plus:
|
|
Kind = MCBinaryExpr::Add;
|
|
return 4;
|
|
case AsmToken::Minus:
|
|
Kind = MCBinaryExpr::Sub;
|
|
return 4;
|
|
|
|
// High Intermediate Precedence: |, !, &, ^
|
|
//
|
|
case AsmToken::Pipe:
|
|
Kind = MCBinaryExpr::Or;
|
|
return 5;
|
|
case AsmToken::Exclaim:
|
|
// Hack to support ARM compatible aliases (implied 'sp' operand in 'srs*'
|
|
// instructions like 'srsda #31!') and not parse ! as an infix operator.
|
|
if (MAI.getCommentString() == "@")
|
|
return 0;
|
|
Kind = MCBinaryExpr::OrNot;
|
|
return 5;
|
|
case AsmToken::Caret:
|
|
Kind = MCBinaryExpr::Xor;
|
|
return 5;
|
|
case AsmToken::Amp:
|
|
Kind = MCBinaryExpr::And;
|
|
return 5;
|
|
|
|
// Highest Precedence: *, /, %, <<, >>
|
|
case AsmToken::Star:
|
|
Kind = MCBinaryExpr::Mul;
|
|
return 6;
|
|
case AsmToken::Slash:
|
|
Kind = MCBinaryExpr::Div;
|
|
return 6;
|
|
case AsmToken::Percent:
|
|
Kind = MCBinaryExpr::Mod;
|
|
return 6;
|
|
case AsmToken::LessLess:
|
|
Kind = MCBinaryExpr::Shl;
|
|
return 6;
|
|
case AsmToken::GreaterGreater:
|
|
Kind = ShouldUseLogicalShr ? MCBinaryExpr::LShr : MCBinaryExpr::AShr;
|
|
return 6;
|
|
}
|
|
}
|
|
|
|
unsigned AsmParser::getBinOpPrecedence(AsmToken::TokenKind K,
|
|
MCBinaryExpr::Opcode &Kind) {
|
|
bool ShouldUseLogicalShr = MAI.shouldUseLogicalShr();
|
|
return IsDarwin ? getDarwinBinOpPrecedence(K, Kind, ShouldUseLogicalShr)
|
|
: getGNUBinOpPrecedence(MAI, K, Kind, ShouldUseLogicalShr);
|
|
}
|
|
|
|
/// Parse all binary operators with precedence >= 'Precedence'.
|
|
/// Res contains the LHS of the expression on input.
|
|
bool AsmParser::parseBinOpRHS(unsigned Precedence, const MCExpr *&Res,
|
|
SMLoc &EndLoc) {
|
|
SMLoc StartLoc = Lexer.getLoc();
|
|
while (true) {
|
|
MCBinaryExpr::Opcode Kind = MCBinaryExpr::Add;
|
|
unsigned TokPrec = getBinOpPrecedence(Lexer.getKind(), Kind);
|
|
|
|
// If the next token is lower precedence than we are allowed to eat, return
|
|
// successfully with what we ate already.
|
|
if (TokPrec < Precedence)
|
|
return false;
|
|
|
|
Lex();
|
|
|
|
// Eat the next primary expression.
|
|
const MCExpr *RHS;
|
|
if (getTargetParser().parsePrimaryExpr(RHS, EndLoc))
|
|
return true;
|
|
|
|
// If BinOp binds less tightly with RHS than the operator after RHS, let
|
|
// the pending operator take RHS as its LHS.
|
|
MCBinaryExpr::Opcode Dummy;
|
|
unsigned NextTokPrec = getBinOpPrecedence(Lexer.getKind(), Dummy);
|
|
if (TokPrec < NextTokPrec && parseBinOpRHS(TokPrec + 1, RHS, EndLoc))
|
|
return true;
|
|
|
|
// Merge LHS and RHS according to operator.
|
|
Res = MCBinaryExpr::create(Kind, Res, RHS, getContext(), StartLoc);
|
|
}
|
|
}
|
|
|
|
/// ParseStatement:
|
|
/// ::= EndOfStatement
|
|
/// ::= Label* Directive ...Operands... EndOfStatement
|
|
/// ::= Label* Identifier OperandList* EndOfStatement
|
|
bool AsmParser::parseStatement(ParseStatementInfo &Info,
|
|
MCAsmParserSemaCallback *SI) {
|
|
assert(!hasPendingError() && "parseStatement started with pending error");
|
|
// Eat initial spaces and comments
|
|
while (Lexer.is(AsmToken::Space))
|
|
Lex();
|
|
if (Lexer.is(AsmToken::EndOfStatement)) {
|
|
// if this is a line comment we can drop it safely
|
|
if (getTok().getString().empty() || getTok().getString().front() == '\r' ||
|
|
getTok().getString().front() == '\n')
|
|
Out.addBlankLine();
|
|
Lex();
|
|
return false;
|
|
}
|
|
// Statements always start with an identifier.
|
|
AsmToken ID = getTok();
|
|
SMLoc IDLoc = ID.getLoc();
|
|
StringRef IDVal;
|
|
int64_t LocalLabelVal = -1;
|
|
StartTokLoc = ID.getLoc();
|
|
if (Lexer.is(AsmToken::HashDirective))
|
|
return parseCppHashLineFilenameComment(IDLoc,
|
|
!isInsideMacroInstantiation());
|
|
|
|
// Allow an integer followed by a ':' as a directional local label.
|
|
if (Lexer.is(AsmToken::Integer)) {
|
|
LocalLabelVal = getTok().getIntVal();
|
|
if (LocalLabelVal < 0) {
|
|
if (!TheCondState.Ignore) {
|
|
Lex(); // always eat a token
|
|
return Error(IDLoc, "unexpected token at start of statement");
|
|
}
|
|
IDVal = "";
|
|
} else {
|
|
IDVal = getTok().getString();
|
|
Lex(); // Consume the integer token to be used as an identifier token.
|
|
if (Lexer.getKind() != AsmToken::Colon) {
|
|
if (!TheCondState.Ignore) {
|
|
Lex(); // always eat a token
|
|
return Error(IDLoc, "unexpected token at start of statement");
|
|
}
|
|
}
|
|
}
|
|
} else if (Lexer.is(AsmToken::Dot)) {
|
|
// Treat '.' as a valid identifier in this context.
|
|
Lex();
|
|
IDVal = ".";
|
|
} else if (getTargetParser().tokenIsStartOfStatement(ID.getKind())) {
|
|
Lex();
|
|
IDVal = ID.getString();
|
|
} else if (parseIdentifier(IDVal)) {
|
|
if (!TheCondState.Ignore) {
|
|
Lex(); // always eat a token
|
|
return Error(IDLoc, "unexpected token at start of statement");
|
|
}
|
|
IDVal = "";
|
|
}
|
|
|
|
// Handle conditional assembly here before checking for skipping. We
|
|
// have to do this so that .endif isn't skipped in a ".if 0" block for
|
|
// example.
|
|
StringMap<DirectiveKind>::const_iterator DirKindIt =
|
|
DirectiveKindMap.find(IDVal.lower());
|
|
DirectiveKind DirKind = (DirKindIt == DirectiveKindMap.end())
|
|
? DK_NO_DIRECTIVE
|
|
: DirKindIt->getValue();
|
|
switch (DirKind) {
|
|
default:
|
|
break;
|
|
case DK_IF:
|
|
case DK_IFEQ:
|
|
case DK_IFGE:
|
|
case DK_IFGT:
|
|
case DK_IFLE:
|
|
case DK_IFLT:
|
|
case DK_IFNE:
|
|
return parseDirectiveIf(IDLoc, DirKind);
|
|
case DK_IFB:
|
|
return parseDirectiveIfb(IDLoc, true);
|
|
case DK_IFNB:
|
|
return parseDirectiveIfb(IDLoc, false);
|
|
case DK_IFC:
|
|
return parseDirectiveIfc(IDLoc, true);
|
|
case DK_IFEQS:
|
|
return parseDirectiveIfeqs(IDLoc, true);
|
|
case DK_IFNC:
|
|
return parseDirectiveIfc(IDLoc, false);
|
|
case DK_IFNES:
|
|
return parseDirectiveIfeqs(IDLoc, false);
|
|
case DK_IFDEF:
|
|
return parseDirectiveIfdef(IDLoc, true);
|
|
case DK_IFNDEF:
|
|
case DK_IFNOTDEF:
|
|
return parseDirectiveIfdef(IDLoc, false);
|
|
case DK_ELSEIF:
|
|
return parseDirectiveElseIf(IDLoc);
|
|
case DK_ELSE:
|
|
return parseDirectiveElse(IDLoc);
|
|
case DK_ENDIF:
|
|
return parseDirectiveEndIf(IDLoc);
|
|
}
|
|
|
|
// Ignore the statement if in the middle of inactive conditional
|
|
// (e.g. ".if 0").
|
|
if (TheCondState.Ignore) {
|
|
eatToEndOfStatement();
|
|
return false;
|
|
}
|
|
|
|
// FIXME: Recurse on local labels?
|
|
|
|
// Check for a label.
|
|
// ::= identifier ':'
|
|
// ::= number ':'
|
|
if (Lexer.is(AsmToken::Colon) && getTargetParser().isLabel(ID)) {
|
|
if (checkForValidSection())
|
|
return true;
|
|
|
|
Lex(); // Consume the ':'.
|
|
|
|
// Diagnose attempt to use '.' as a label.
|
|
if (IDVal == ".")
|
|
return Error(IDLoc, "invalid use of pseudo-symbol '.' as a label");
|
|
|
|
// Diagnose attempt to use a variable as a label.
|
|
//
|
|
// FIXME: Diagnostics. Note the location of the definition as a label.
|
|
// FIXME: This doesn't diagnose assignment to a symbol which has been
|
|
// implicitly marked as external.
|
|
MCSymbol *Sym;
|
|
if (LocalLabelVal == -1) {
|
|
if (ParsingMSInlineAsm && SI) {
|
|
StringRef RewrittenLabel =
|
|
SI->LookupInlineAsmLabel(IDVal, getSourceManager(), IDLoc, true);
|
|
assert(!RewrittenLabel.empty() &&
|
|
"We should have an internal name here.");
|
|
Info.AsmRewrites->emplace_back(AOK_Label, IDLoc, IDVal.size(),
|
|
RewrittenLabel);
|
|
IDVal = RewrittenLabel;
|
|
}
|
|
Sym = getContext().getOrCreateSymbol(IDVal);
|
|
} else
|
|
Sym = Ctx.createDirectionalLocalSymbol(LocalLabelVal);
|
|
// End of Labels should be treated as end of line for lexing
|
|
// purposes but that information is not available to the Lexer who
|
|
// does not understand Labels. This may cause us to see a Hash
|
|
// here instead of a preprocessor line comment.
|
|
if (getTok().is(AsmToken::Hash)) {
|
|
StringRef CommentStr = parseStringToEndOfStatement();
|
|
Lexer.Lex();
|
|
Lexer.UnLex(AsmToken(AsmToken::EndOfStatement, CommentStr));
|
|
}
|
|
|
|
// Consume any end of statement token, if present, to avoid spurious
|
|
// addBlankLine calls().
|
|
if (getTok().is(AsmToken::EndOfStatement)) {
|
|
Lex();
|
|
}
|
|
|
|
if (MAI.hasSubsectionsViaSymbols() && CFIStartProcLoc &&
|
|
Sym->isExternal() && !static_cast<MCSymbolMachO *>(Sym)->isAltEntry())
|
|
return Error(StartTokLoc, "non-private labels cannot appear between "
|
|
".cfi_startproc / .cfi_endproc pairs") &&
|
|
Error(*CFIStartProcLoc, "previous .cfi_startproc was here");
|
|
|
|
if (discardLTOSymbol(IDVal))
|
|
return false;
|
|
|
|
getTargetParser().doBeforeLabelEmit(Sym, IDLoc);
|
|
|
|
// Emit the label.
|
|
if (!getTargetParser().isParsingMSInlineAsm())
|
|
Out.emitLabel(Sym, IDLoc);
|
|
|
|
// If we are generating dwarf for assembly source files then gather the
|
|
// info to make a dwarf label entry for this label if needed.
|
|
if (enabledGenDwarfForAssembly())
|
|
MCGenDwarfLabelEntry::Make(Sym, &getStreamer(), getSourceManager(),
|
|
IDLoc);
|
|
|
|
getTargetParser().onLabelParsed(Sym);
|
|
|
|
return false;
|
|
}
|
|
|
|
// Check for an assignment statement.
|
|
// ::= identifier '='
|
|
if (Lexer.is(AsmToken::Equal) && getTargetParser().equalIsAsmAssignment()) {
|
|
Lex();
|
|
return parseAssignment(IDVal, AssignmentKind::Equal);
|
|
}
|
|
|
|
// If macros are enabled, check to see if this is a macro instantiation.
|
|
if (areMacrosEnabled())
|
|
if (MCAsmMacro *M = getContext().lookupMacro(IDVal))
|
|
return handleMacroEntry(M, IDLoc);
|
|
|
|
// Otherwise, we have a normal instruction or directive.
|
|
|
|
// Directives start with "."
|
|
if (IDVal.starts_with(".") && IDVal != ".") {
|
|
// There are several entities interested in parsing directives:
|
|
//
|
|
// 1. The target-specific assembly parser. Some directives are target
|
|
// specific or may potentially behave differently on certain targets.
|
|
// 2. Asm parser extensions. For example, platform-specific parsers
|
|
// (like the ELF parser) register themselves as extensions.
|
|
// 3. The generic directive parser implemented by this class. These are
|
|
// all the directives that behave in a target and platform independent
|
|
// manner, or at least have a default behavior that's shared between
|
|
// all targets and platforms.
|
|
|
|
getTargetParser().flushPendingInstructions(getStreamer());
|
|
|
|
ParseStatus TPDirectiveReturn = getTargetParser().parseDirective(ID);
|
|
assert(TPDirectiveReturn.isFailure() == hasPendingError() &&
|
|
"Should only return Failure iff there was an error");
|
|
if (TPDirectiveReturn.isFailure())
|
|
return true;
|
|
if (TPDirectiveReturn.isSuccess())
|
|
return false;
|
|
|
|
// Next, check the extension directive map to see if any extension has
|
|
// registered itself to parse this directive.
|
|
std::pair<MCAsmParserExtension *, DirectiveHandler> Handler =
|
|
ExtensionDirectiveMap.lookup(IDVal);
|
|
if (Handler.first)
|
|
return (*Handler.second)(Handler.first, IDVal, IDLoc);
|
|
|
|
// Finally, if no one else is interested in this directive, it must be
|
|
// generic and familiar to this class.
|
|
switch (DirKind) {
|
|
default:
|
|
break;
|
|
case DK_SET:
|
|
case DK_EQU:
|
|
return parseDirectiveSet(IDVal, AssignmentKind::Set);
|
|
case DK_EQUIV:
|
|
return parseDirectiveSet(IDVal, AssignmentKind::Equiv);
|
|
case DK_LTO_SET_CONDITIONAL:
|
|
return parseDirectiveSet(IDVal, AssignmentKind::LTOSetConditional);
|
|
case DK_ASCII:
|
|
return parseDirectiveAscii(IDVal, false);
|
|
case DK_ASCIZ:
|
|
case DK_STRING:
|
|
return parseDirectiveAscii(IDVal, true);
|
|
case DK_BYTE:
|
|
case DK_DC_B:
|
|
return parseDirectiveValue(IDVal, 1);
|
|
case DK_DC:
|
|
case DK_DC_W:
|
|
case DK_SHORT:
|
|
case DK_VALUE:
|
|
case DK_2BYTE:
|
|
return parseDirectiveValue(IDVal, 2);
|
|
case DK_LONG:
|
|
case DK_INT:
|
|
case DK_4BYTE:
|
|
case DK_DC_L:
|
|
return parseDirectiveValue(IDVal, 4);
|
|
case DK_QUAD:
|
|
case DK_8BYTE:
|
|
return parseDirectiveValue(IDVal, 8);
|
|
case DK_DC_A:
|
|
return parseDirectiveValue(
|
|
IDVal, getContext().getAsmInfo()->getCodePointerSize());
|
|
case DK_OCTA:
|
|
return parseDirectiveOctaValue(IDVal);
|
|
case DK_SINGLE:
|
|
case DK_FLOAT:
|
|
case DK_DC_S:
|
|
return parseDirectiveRealValue(IDVal, APFloat::IEEEsingle());
|
|
case DK_DOUBLE:
|
|
case DK_DC_D:
|
|
return parseDirectiveRealValue(IDVal, APFloat::IEEEdouble());
|
|
case DK_ALIGN: {
|
|
bool IsPow2 = !getContext().getAsmInfo()->getAlignmentIsInBytes();
|
|
return parseDirectiveAlign(IsPow2, /*ExprSize=*/1);
|
|
}
|
|
case DK_ALIGN32: {
|
|
bool IsPow2 = !getContext().getAsmInfo()->getAlignmentIsInBytes();
|
|
return parseDirectiveAlign(IsPow2, /*ExprSize=*/4);
|
|
}
|
|
case DK_BALIGN:
|
|
return parseDirectiveAlign(/*IsPow2=*/false, /*ExprSize=*/1);
|
|
case DK_BALIGNW:
|
|
return parseDirectiveAlign(/*IsPow2=*/false, /*ExprSize=*/2);
|
|
case DK_BALIGNL:
|
|
return parseDirectiveAlign(/*IsPow2=*/false, /*ExprSize=*/4);
|
|
case DK_P2ALIGN:
|
|
return parseDirectiveAlign(/*IsPow2=*/true, /*ExprSize=*/1);
|
|
case DK_P2ALIGNW:
|
|
return parseDirectiveAlign(/*IsPow2=*/true, /*ExprSize=*/2);
|
|
case DK_P2ALIGNL:
|
|
return parseDirectiveAlign(/*IsPow2=*/true, /*ExprSize=*/4);
|
|
case DK_ORG:
|
|
return parseDirectiveOrg();
|
|
case DK_FILL:
|
|
return parseDirectiveFill();
|
|
case DK_ZERO:
|
|
return parseDirectiveZero();
|
|
case DK_EXTERN:
|
|
eatToEndOfStatement(); // .extern is the default, ignore it.
|
|
return false;
|
|
case DK_GLOBL:
|
|
case DK_GLOBAL:
|
|
return parseDirectiveSymbolAttribute(MCSA_Global);
|
|
case DK_LAZY_REFERENCE:
|
|
return parseDirectiveSymbolAttribute(MCSA_LazyReference);
|
|
case DK_NO_DEAD_STRIP:
|
|
return parseDirectiveSymbolAttribute(MCSA_NoDeadStrip);
|
|
case DK_SYMBOL_RESOLVER:
|
|
return parseDirectiveSymbolAttribute(MCSA_SymbolResolver);
|
|
case DK_PRIVATE_EXTERN:
|
|
return parseDirectiveSymbolAttribute(MCSA_PrivateExtern);
|
|
case DK_REFERENCE:
|
|
return parseDirectiveSymbolAttribute(MCSA_Reference);
|
|
case DK_WEAK_DEFINITION:
|
|
return parseDirectiveSymbolAttribute(MCSA_WeakDefinition);
|
|
case DK_WEAK_REFERENCE:
|
|
return parseDirectiveSymbolAttribute(MCSA_WeakReference);
|
|
case DK_WEAK_DEF_CAN_BE_HIDDEN:
|
|
return parseDirectiveSymbolAttribute(MCSA_WeakDefAutoPrivate);
|
|
case DK_COLD:
|
|
return parseDirectiveSymbolAttribute(MCSA_Cold);
|
|
case DK_COMM:
|
|
case DK_COMMON:
|
|
return parseDirectiveComm(/*IsLocal=*/false);
|
|
case DK_LCOMM:
|
|
return parseDirectiveComm(/*IsLocal=*/true);
|
|
case DK_ABORT:
|
|
return parseDirectiveAbort(IDLoc);
|
|
case DK_INCLUDE:
|
|
return parseDirectiveInclude();
|
|
case DK_INCBIN:
|
|
return parseDirectiveIncbin();
|
|
case DK_CODE16:
|
|
case DK_CODE16GCC:
|
|
return TokError(Twine(IDVal) +
|
|
" not currently supported for this target");
|
|
case DK_REPT:
|
|
return parseDirectiveRept(IDLoc, IDVal);
|
|
case DK_IRP:
|
|
return parseDirectiveIrp(IDLoc);
|
|
case DK_IRPC:
|
|
return parseDirectiveIrpc(IDLoc);
|
|
case DK_ENDR:
|
|
return parseDirectiveEndr(IDLoc);
|
|
case DK_SLEB128:
|
|
return parseDirectiveLEB128(true);
|
|
case DK_ULEB128:
|
|
return parseDirectiveLEB128(false);
|
|
case DK_SPACE:
|
|
case DK_SKIP:
|
|
return parseDirectiveSpace(IDVal);
|
|
case DK_FILE:
|
|
return parseDirectiveFile(IDLoc);
|
|
case DK_LINE:
|
|
return parseDirectiveLine();
|
|
case DK_LOC:
|
|
return parseDirectiveLoc();
|
|
case DK_LOC_LABEL:
|
|
return parseDirectiveLocLabel(IDLoc);
|
|
case DK_STABS:
|
|
return parseDirectiveStabs();
|
|
case DK_CV_FILE:
|
|
return parseDirectiveCVFile();
|
|
case DK_CV_FUNC_ID:
|
|
return parseDirectiveCVFuncId();
|
|
case DK_CV_INLINE_SITE_ID:
|
|
return parseDirectiveCVInlineSiteId();
|
|
case DK_CV_LOC:
|
|
return parseDirectiveCVLoc();
|
|
case DK_CV_LINETABLE:
|
|
return parseDirectiveCVLinetable();
|
|
case DK_CV_INLINE_LINETABLE:
|
|
return parseDirectiveCVInlineLinetable();
|
|
case DK_CV_DEF_RANGE:
|
|
return parseDirectiveCVDefRange();
|
|
case DK_CV_STRING:
|
|
return parseDirectiveCVString();
|
|
case DK_CV_STRINGTABLE:
|
|
return parseDirectiveCVStringTable();
|
|
case DK_CV_FILECHECKSUMS:
|
|
return parseDirectiveCVFileChecksums();
|
|
case DK_CV_FILECHECKSUM_OFFSET:
|
|
return parseDirectiveCVFileChecksumOffset();
|
|
case DK_CV_FPO_DATA:
|
|
return parseDirectiveCVFPOData();
|
|
case DK_CFI_SECTIONS:
|
|
return parseDirectiveCFISections();
|
|
case DK_CFI_STARTPROC:
|
|
return parseDirectiveCFIStartProc();
|
|
case DK_CFI_ENDPROC:
|
|
return parseDirectiveCFIEndProc();
|
|
case DK_CFI_DEF_CFA:
|
|
return parseDirectiveCFIDefCfa(IDLoc);
|
|
case DK_CFI_DEF_CFA_OFFSET:
|
|
return parseDirectiveCFIDefCfaOffset(IDLoc);
|
|
case DK_CFI_ADJUST_CFA_OFFSET:
|
|
return parseDirectiveCFIAdjustCfaOffset(IDLoc);
|
|
case DK_CFI_DEF_CFA_REGISTER:
|
|
return parseDirectiveCFIDefCfaRegister(IDLoc);
|
|
case DK_CFI_LLVM_DEF_ASPACE_CFA:
|
|
return parseDirectiveCFILLVMDefAspaceCfa(IDLoc);
|
|
case DK_CFI_OFFSET:
|
|
return parseDirectiveCFIOffset(IDLoc);
|
|
case DK_CFI_REL_OFFSET:
|
|
return parseDirectiveCFIRelOffset(IDLoc);
|
|
case DK_CFI_PERSONALITY:
|
|
return parseDirectiveCFIPersonalityOrLsda(true);
|
|
case DK_CFI_LSDA:
|
|
return parseDirectiveCFIPersonalityOrLsda(false);
|
|
case DK_CFI_REMEMBER_STATE:
|
|
return parseDirectiveCFIRememberState(IDLoc);
|
|
case DK_CFI_RESTORE_STATE:
|
|
return parseDirectiveCFIRestoreState(IDLoc);
|
|
case DK_CFI_SAME_VALUE:
|
|
return parseDirectiveCFISameValue(IDLoc);
|
|
case DK_CFI_RESTORE:
|
|
return parseDirectiveCFIRestore(IDLoc);
|
|
case DK_CFI_ESCAPE:
|
|
return parseDirectiveCFIEscape(IDLoc);
|
|
case DK_CFI_RETURN_COLUMN:
|
|
return parseDirectiveCFIReturnColumn(IDLoc);
|
|
case DK_CFI_SIGNAL_FRAME:
|
|
return parseDirectiveCFISignalFrame(IDLoc);
|
|
case DK_CFI_UNDEFINED:
|
|
return parseDirectiveCFIUndefined(IDLoc);
|
|
case DK_CFI_REGISTER:
|
|
return parseDirectiveCFIRegister(IDLoc);
|
|
case DK_CFI_WINDOW_SAVE:
|
|
return parseDirectiveCFIWindowSave(IDLoc);
|
|
case DK_CFI_LABEL:
|
|
return parseDirectiveCFILabel(IDLoc);
|
|
case DK_CFI_VAL_OFFSET:
|
|
return parseDirectiveCFIValOffset(IDLoc);
|
|
case DK_MACROS_ON:
|
|
case DK_MACROS_OFF:
|
|
return parseDirectiveMacrosOnOff(IDVal);
|
|
case DK_MACRO:
|
|
return parseDirectiveMacro(IDLoc);
|
|
case DK_ALTMACRO:
|
|
case DK_NOALTMACRO:
|
|
return parseDirectiveAltmacro(IDVal);
|
|
case DK_EXITM:
|
|
return parseDirectiveExitMacro(IDVal);
|
|
case DK_ENDM:
|
|
case DK_ENDMACRO:
|
|
return parseDirectiveEndMacro(IDVal);
|
|
case DK_PURGEM:
|
|
return parseDirectivePurgeMacro(IDLoc);
|
|
case DK_END:
|
|
return parseDirectiveEnd(IDLoc);
|
|
case DK_ERR:
|
|
return parseDirectiveError(IDLoc, false);
|
|
case DK_ERROR:
|
|
return parseDirectiveError(IDLoc, true);
|
|
case DK_WARNING:
|
|
return parseDirectiveWarning(IDLoc);
|
|
case DK_RELOC:
|
|
return parseDirectiveReloc(IDLoc);
|
|
case DK_DCB:
|
|
case DK_DCB_W:
|
|
return parseDirectiveDCB(IDVal, 2);
|
|
case DK_DCB_B:
|
|
return parseDirectiveDCB(IDVal, 1);
|
|
case DK_DCB_D:
|
|
return parseDirectiveRealDCB(IDVal, APFloat::IEEEdouble());
|
|
case DK_DCB_L:
|
|
return parseDirectiveDCB(IDVal, 4);
|
|
case DK_DCB_S:
|
|
return parseDirectiveRealDCB(IDVal, APFloat::IEEEsingle());
|
|
case DK_DC_X:
|
|
case DK_DCB_X:
|
|
return TokError(Twine(IDVal) +
|
|
" not currently supported for this target");
|
|
case DK_DS:
|
|
case DK_DS_W:
|
|
return parseDirectiveDS(IDVal, 2);
|
|
case DK_DS_B:
|
|
return parseDirectiveDS(IDVal, 1);
|
|
case DK_DS_D:
|
|
return parseDirectiveDS(IDVal, 8);
|
|
case DK_DS_L:
|
|
case DK_DS_S:
|
|
return parseDirectiveDS(IDVal, 4);
|
|
case DK_DS_P:
|
|
case DK_DS_X:
|
|
return parseDirectiveDS(IDVal, 12);
|
|
case DK_PRINT:
|
|
return parseDirectivePrint(IDLoc);
|
|
case DK_ADDRSIG:
|
|
return parseDirectiveAddrsig();
|
|
case DK_ADDRSIG_SYM:
|
|
return parseDirectiveAddrsigSym();
|
|
case DK_PSEUDO_PROBE:
|
|
return parseDirectivePseudoProbe();
|
|
case DK_LTO_DISCARD:
|
|
return parseDirectiveLTODiscard();
|
|
case DK_MEMTAG:
|
|
return parseDirectiveSymbolAttribute(MCSA_Memtag);
|
|
}
|
|
|
|
return Error(IDLoc, "unknown directive");
|
|
}
|
|
|
|
// __asm _emit or __asm __emit
|
|
if (ParsingMSInlineAsm && (IDVal == "_emit" || IDVal == "__emit" ||
|
|
IDVal == "_EMIT" || IDVal == "__EMIT"))
|
|
return parseDirectiveMSEmit(IDLoc, Info, IDVal.size());
|
|
|
|
// __asm align
|
|
if (ParsingMSInlineAsm && (IDVal == "align" || IDVal == "ALIGN"))
|
|
return parseDirectiveMSAlign(IDLoc, Info);
|
|
|
|
if (ParsingMSInlineAsm && (IDVal == "even" || IDVal == "EVEN"))
|
|
Info.AsmRewrites->emplace_back(AOK_EVEN, IDLoc, 4);
|
|
if (checkForValidSection())
|
|
return true;
|
|
|
|
return parseAndMatchAndEmitTargetInstruction(Info, IDVal, ID, IDLoc);
|
|
}
|
|
|
|
bool AsmParser::parseAndMatchAndEmitTargetInstruction(ParseStatementInfo &Info,
|
|
StringRef IDVal,
|
|
AsmToken ID,
|
|
SMLoc IDLoc) {
|
|
// Canonicalize the opcode to lower case.
|
|
std::string OpcodeStr = IDVal.lower();
|
|
ParseInstructionInfo IInfo(Info.AsmRewrites);
|
|
bool ParseHadError = getTargetParser().parseInstruction(IInfo, OpcodeStr, ID,
|
|
Info.ParsedOperands);
|
|
Info.ParseError = ParseHadError;
|
|
|
|
// Dump the parsed representation, if requested.
|
|
if (getShowParsedOperands()) {
|
|
SmallString<256> Str;
|
|
raw_svector_ostream OS(Str);
|
|
OS << "parsed instruction: [";
|
|
for (unsigned i = 0; i != Info.ParsedOperands.size(); ++i) {
|
|
if (i != 0)
|
|
OS << ", ";
|
|
Info.ParsedOperands[i]->print(OS, MAI);
|
|
}
|
|
OS << "]";
|
|
|
|
printMessage(IDLoc, SourceMgr::DK_Note, OS.str());
|
|
}
|
|
|
|
// Fail even if ParseInstruction erroneously returns false.
|
|
if (hasPendingError() || ParseHadError)
|
|
return true;
|
|
|
|
// If we are generating dwarf for the current section then generate a .loc
|
|
// directive for the instruction.
|
|
if (!ParseHadError && enabledGenDwarfForAssembly() &&
|
|
getContext().getGenDwarfSectionSyms().count(
|
|
getStreamer().getCurrentSectionOnly())) {
|
|
unsigned Line;
|
|
if (ActiveMacros.empty())
|
|
Line = SrcMgr.FindLineNumber(IDLoc, CurBuffer);
|
|
else
|
|
Line = SrcMgr.FindLineNumber(ActiveMacros.front()->InstantiationLoc,
|
|
ActiveMacros.front()->ExitBuffer);
|
|
|
|
// If we previously parsed a cpp hash file line comment then make sure the
|
|
// current Dwarf File is for the CppHashFilename if not then emit the
|
|
// Dwarf File table for it and adjust the line number for the .loc.
|
|
if (!CppHashInfo.Filename.empty()) {
|
|
unsigned FileNumber = getStreamer().emitDwarfFileDirective(
|
|
0, StringRef(), CppHashInfo.Filename);
|
|
getContext().setGenDwarfFileNumber(FileNumber);
|
|
|
|
unsigned CppHashLocLineNo =
|
|
SrcMgr.FindLineNumber(CppHashInfo.Loc, CppHashInfo.Buf);
|
|
Line = CppHashInfo.LineNumber - 1 + (Line - CppHashLocLineNo);
|
|
}
|
|
|
|
getStreamer().emitDwarfLocDirective(
|
|
getContext().getGenDwarfFileNumber(), Line, 0,
|
|
DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0, 0, 0,
|
|
StringRef());
|
|
}
|
|
|
|
// If parsing succeeded, match the instruction.
|
|
if (!ParseHadError) {
|
|
uint64_t ErrorInfo;
|
|
if (getTargetParser().matchAndEmitInstruction(
|
|
IDLoc, Info.Opcode, Info.ParsedOperands, Out, ErrorInfo,
|
|
getTargetParser().isParsingMSInlineAsm()))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Parse and erase curly braces marking block start/end
|
|
bool
|
|
AsmParser::parseCurlyBlockScope(SmallVectorImpl<AsmRewrite> &AsmStrRewrites) {
|
|
// Identify curly brace marking block start/end
|
|
if (Lexer.isNot(AsmToken::LCurly) && Lexer.isNot(AsmToken::RCurly))
|
|
return false;
|
|
|
|
SMLoc StartLoc = Lexer.getLoc();
|
|
Lex(); // Eat the brace
|
|
if (Lexer.is(AsmToken::EndOfStatement))
|
|
Lex(); // Eat EndOfStatement following the brace
|
|
|
|
// Erase the block start/end brace from the output asm string
|
|
AsmStrRewrites.emplace_back(AOK_Skip, StartLoc, Lexer.getLoc().getPointer() -
|
|
StartLoc.getPointer());
|
|
return true;
|
|
}
|
|
|
|
/// parseCppHashLineFilenameComment as this:
|
|
/// ::= # number "filename"
|
|
bool AsmParser::parseCppHashLineFilenameComment(SMLoc L, bool SaveLocInfo) {
|
|
Lex(); // Eat the hash token.
|
|
// Lexer only ever emits HashDirective if it fully formed if it's
|
|
// done the checking already so this is an internal error.
|
|
assert(getTok().is(AsmToken::Integer) &&
|
|
"Lexing Cpp line comment: Expected Integer");
|
|
int64_t LineNumber = getTok().getIntVal();
|
|
Lex();
|
|
assert(getTok().is(AsmToken::String) &&
|
|
"Lexing Cpp line comment: Expected String");
|
|
StringRef Filename = getTok().getString();
|
|
Lex();
|
|
|
|
if (!SaveLocInfo)
|
|
return false;
|
|
|
|
// Get rid of the enclosing quotes.
|
|
Filename = Filename.substr(1, Filename.size() - 2);
|
|
|
|
// Save the SMLoc, Filename and LineNumber for later use by diagnostics
|
|
// and possibly DWARF file info.
|
|
CppHashInfo.Loc = L;
|
|
CppHashInfo.Filename = Filename;
|
|
CppHashInfo.LineNumber = LineNumber;
|
|
CppHashInfo.Buf = CurBuffer;
|
|
if (!HadCppHashFilename) {
|
|
HadCppHashFilename = true;
|
|
// If we haven't encountered any .file directives, then the first #line
|
|
// directive describes the "root" file and directory of the compilation
|
|
// unit.
|
|
if (getContext().getGenDwarfForAssembly() &&
|
|
getContext().getGenDwarfFileNumber() == 0) {
|
|
// It's preprocessed, so there is no checksum, and of course no source
|
|
// directive.
|
|
getContext().setMCLineTableRootFile(
|
|
/*CUID=*/0, getContext().getCompilationDir(), Filename,
|
|
/*Cksum=*/std::nullopt, /*Source=*/std::nullopt);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// will use the last parsed cpp hash line filename comment
|
|
/// for the Filename and LineNo if any in the diagnostic.
|
|
void AsmParser::DiagHandler(const SMDiagnostic &Diag, void *Context) {
|
|
auto *Parser = static_cast<AsmParser *>(Context);
|
|
raw_ostream &OS = errs();
|
|
|
|
const SourceMgr &DiagSrcMgr = *Diag.getSourceMgr();
|
|
SMLoc DiagLoc = Diag.getLoc();
|
|
unsigned DiagBuf = DiagSrcMgr.FindBufferContainingLoc(DiagLoc);
|
|
unsigned CppHashBuf =
|
|
Parser->SrcMgr.FindBufferContainingLoc(Parser->CppHashInfo.Loc);
|
|
|
|
// Like SourceMgr::printMessage() we need to print the include stack if any
|
|
// before printing the message.
|
|
unsigned DiagCurBuffer = DiagSrcMgr.FindBufferContainingLoc(DiagLoc);
|
|
if (!Parser->SavedDiagHandler && DiagCurBuffer &&
|
|
DiagCurBuffer != DiagSrcMgr.getMainFileID()) {
|
|
SMLoc ParentIncludeLoc = DiagSrcMgr.getParentIncludeLoc(DiagCurBuffer);
|
|
DiagSrcMgr.PrintIncludeStack(ParentIncludeLoc, OS);
|
|
}
|
|
|
|
// If we have not parsed a cpp hash line filename comment or the source
|
|
// manager changed or buffer changed (like in a nested include) then just
|
|
// print the normal diagnostic using its Filename and LineNo.
|
|
if (!Parser->CppHashInfo.LineNumber || DiagBuf != CppHashBuf) {
|
|
if (Parser->SavedDiagHandler)
|
|
Parser->SavedDiagHandler(Diag, Parser->SavedDiagContext);
|
|
else
|
|
Parser->getContext().diagnose(Diag);
|
|
return;
|
|
}
|
|
|
|
// Use the CppHashFilename and calculate a line number based on the
|
|
// CppHashInfo.Loc and CppHashInfo.LineNumber relative to this Diag's SMLoc
|
|
// for the diagnostic.
|
|
const std::string &Filename = std::string(Parser->CppHashInfo.Filename);
|
|
|
|
int DiagLocLineNo = DiagSrcMgr.FindLineNumber(DiagLoc, DiagBuf);
|
|
int CppHashLocLineNo =
|
|
Parser->SrcMgr.FindLineNumber(Parser->CppHashInfo.Loc, CppHashBuf);
|
|
int LineNo =
|
|
Parser->CppHashInfo.LineNumber - 1 + (DiagLocLineNo - CppHashLocLineNo);
|
|
|
|
SMDiagnostic NewDiag(*Diag.getSourceMgr(), Diag.getLoc(), Filename, LineNo,
|
|
Diag.getColumnNo(), Diag.getKind(), Diag.getMessage(),
|
|
Diag.getLineContents(), Diag.getRanges());
|
|
|
|
if (Parser->SavedDiagHandler)
|
|
Parser->SavedDiagHandler(Diag, Parser->SavedDiagContext);
|
|
else
|
|
Parser->getContext().diagnose(NewDiag);
|
|
}
|
|
|
|
// FIXME: This is mostly duplicated from the function in AsmLexer.cpp. The
|
|
// difference being that that function accepts '@' as part of identifiers and
|
|
// we can't do that. AsmLexer.cpp should probably be changed to handle
|
|
// '@' as a special case when needed.
|
|
static bool isIdentifierChar(char c) {
|
|
return isalnum(static_cast<unsigned char>(c)) || c == '_' || c == '$' ||
|
|
c == '.';
|
|
}
|
|
|
|
bool AsmParser::expandMacro(raw_svector_ostream &OS, MCAsmMacro &Macro,
|
|
ArrayRef<MCAsmMacroParameter> Parameters,
|
|
ArrayRef<MCAsmMacroArgument> A,
|
|
bool EnableAtPseudoVariable) {
|
|
unsigned NParameters = Parameters.size();
|
|
auto expandArg = [&](unsigned Index) {
|
|
bool HasVararg = NParameters ? Parameters.back().Vararg : false;
|
|
bool VarargParameter = HasVararg && Index == (NParameters - 1);
|
|
for (const AsmToken &Token : A[Index])
|
|
// For altmacro mode, you can write '%expr'.
|
|
// The prefix '%' evaluates the expression 'expr'
|
|
// and uses the result as a string (e.g. replace %(1+2) with the
|
|
// string "3").
|
|
// Here, we identify the integer token which is the result of the
|
|
// absolute expression evaluation and replace it with its string
|
|
// representation.
|
|
if (AltMacroMode && Token.getString().front() == '%' &&
|
|
Token.is(AsmToken::Integer))
|
|
// Emit an integer value to the buffer.
|
|
OS << Token.getIntVal();
|
|
// Only Token that was validated as a string and begins with '<'
|
|
// is considered altMacroString!!!
|
|
else if (AltMacroMode && Token.getString().front() == '<' &&
|
|
Token.is(AsmToken::String)) {
|
|
OS << angleBracketString(Token.getStringContents());
|
|
}
|
|
// We expect no quotes around the string's contents when
|
|
// parsing for varargs.
|
|
else if (Token.isNot(AsmToken::String) || VarargParameter)
|
|
OS << Token.getString();
|
|
else
|
|
OS << Token.getStringContents();
|
|
};
|
|
|
|
// A macro without parameters is handled differently on Darwin:
|
|
// gas accepts no arguments and does no substitutions
|
|
StringRef Body = Macro.Body;
|
|
size_t I = 0, End = Body.size();
|
|
while (I != End) {
|
|
if (Body[I] == '\\' && I + 1 != End) {
|
|
// Check for \@ and \+ pseudo variables.
|
|
if (EnableAtPseudoVariable && Body[I + 1] == '@') {
|
|
OS << NumOfMacroInstantiations;
|
|
I += 2;
|
|
continue;
|
|
}
|
|
if (Body[I + 1] == '+') {
|
|
OS << Macro.Count;
|
|
I += 2;
|
|
continue;
|
|
}
|
|
if (Body[I + 1] == '(' && Body[I + 2] == ')') {
|
|
I += 3;
|
|
continue;
|
|
}
|
|
|
|
size_t Pos = ++I;
|
|
while (I != End && isIdentifierChar(Body[I]))
|
|
++I;
|
|
StringRef Argument(Body.data() + Pos, I - Pos);
|
|
if (AltMacroMode && I != End && Body[I] == '&')
|
|
++I;
|
|
unsigned Index = 0;
|
|
for (; Index < NParameters; ++Index)
|
|
if (Parameters[Index].Name == Argument)
|
|
break;
|
|
if (Index == NParameters)
|
|
OS << '\\' << Argument;
|
|
else
|
|
expandArg(Index);
|
|
continue;
|
|
}
|
|
|
|
// In Darwin mode, $ is used for macro expansion, not considered an
|
|
// identifier char.
|
|
if (Body[I] == '$' && I + 1 != End && IsDarwin && !NParameters) {
|
|
// This macro has no parameters, look for $0, $1, etc.
|
|
switch (Body[I + 1]) {
|
|
// $$ => $
|
|
case '$':
|
|
OS << '$';
|
|
I += 2;
|
|
continue;
|
|
// $n => number of arguments
|
|
case 'n':
|
|
OS << A.size();
|
|
I += 2;
|
|
continue;
|
|
default: {
|
|
if (!isDigit(Body[I + 1]))
|
|
break;
|
|
// $[0-9] => argument
|
|
// Missing arguments are ignored.
|
|
unsigned Index = Body[I + 1] - '0';
|
|
if (Index < A.size())
|
|
for (const AsmToken &Token : A[Index])
|
|
OS << Token.getString();
|
|
I += 2;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!isIdentifierChar(Body[I]) || IsDarwin) {
|
|
OS << Body[I++];
|
|
continue;
|
|
}
|
|
|
|
const size_t Start = I;
|
|
while (++I && isIdentifierChar(Body[I])) {
|
|
}
|
|
StringRef Token(Body.data() + Start, I - Start);
|
|
if (AltMacroMode) {
|
|
unsigned Index = 0;
|
|
for (; Index != NParameters; ++Index)
|
|
if (Parameters[Index].Name == Token)
|
|
break;
|
|
if (Index != NParameters) {
|
|
expandArg(Index);
|
|
if (I != End && Body[I] == '&')
|
|
++I;
|
|
continue;
|
|
}
|
|
}
|
|
OS << Token;
|
|
}
|
|
|
|
++Macro.Count;
|
|
return false;
|
|
}
|
|
|
|
static bool isOperator(AsmToken::TokenKind kind) {
|
|
switch (kind) {
|
|
default:
|
|
return false;
|
|
case AsmToken::Plus:
|
|
case AsmToken::Minus:
|
|
case AsmToken::Tilde:
|
|
case AsmToken::Slash:
|
|
case AsmToken::Star:
|
|
case AsmToken::Dot:
|
|
case AsmToken::Equal:
|
|
case AsmToken::EqualEqual:
|
|
case AsmToken::Pipe:
|
|
case AsmToken::PipePipe:
|
|
case AsmToken::Caret:
|
|
case AsmToken::Amp:
|
|
case AsmToken::AmpAmp:
|
|
case AsmToken::Exclaim:
|
|
case AsmToken::ExclaimEqual:
|
|
case AsmToken::Less:
|
|
case AsmToken::LessEqual:
|
|
case AsmToken::LessLess:
|
|
case AsmToken::LessGreater:
|
|
case AsmToken::Greater:
|
|
case AsmToken::GreaterEqual:
|
|
case AsmToken::GreaterGreater:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
class AsmLexerSkipSpaceRAII {
|
|
public:
|
|
AsmLexerSkipSpaceRAII(AsmLexer &Lexer, bool SkipSpace) : Lexer(Lexer) {
|
|
Lexer.setSkipSpace(SkipSpace);
|
|
}
|
|
|
|
~AsmLexerSkipSpaceRAII() {
|
|
Lexer.setSkipSpace(true);
|
|
}
|
|
|
|
private:
|
|
AsmLexer &Lexer;
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
bool AsmParser::parseMacroArgument(MCAsmMacroArgument &MA, bool Vararg) {
|
|
|
|
if (Vararg) {
|
|
if (Lexer.isNot(AsmToken::EndOfStatement)) {
|
|
StringRef Str = parseStringToEndOfStatement();
|
|
MA.emplace_back(AsmToken::String, Str);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
unsigned ParenLevel = 0;
|
|
|
|
// Darwin doesn't use spaces to delmit arguments.
|
|
AsmLexerSkipSpaceRAII ScopedSkipSpace(Lexer, IsDarwin);
|
|
|
|
bool SpaceEaten;
|
|
|
|
while (true) {
|
|
SpaceEaten = false;
|
|
if (Lexer.is(AsmToken::Eof) || Lexer.is(AsmToken::Equal))
|
|
return TokError("unexpected token in macro instantiation");
|
|
|
|
if (ParenLevel == 0) {
|
|
|
|
if (Lexer.is(AsmToken::Comma))
|
|
break;
|
|
|
|
if (parseOptionalToken(AsmToken::Space))
|
|
SpaceEaten = true;
|
|
|
|
// Spaces can delimit parameters, but could also be part an expression.
|
|
// If the token after a space is an operator, add the token and the next
|
|
// one into this argument
|
|
if (!IsDarwin) {
|
|
if (isOperator(Lexer.getKind())) {
|
|
MA.push_back(getTok());
|
|
Lexer.Lex();
|
|
|
|
// Whitespace after an operator can be ignored.
|
|
parseOptionalToken(AsmToken::Space);
|
|
continue;
|
|
}
|
|
}
|
|
if (SpaceEaten)
|
|
break;
|
|
}
|
|
|
|
// handleMacroEntry relies on not advancing the lexer here
|
|
// to be able to fill in the remaining default parameter values
|
|
if (Lexer.is(AsmToken::EndOfStatement))
|
|
break;
|
|
|
|
// Adjust the current parentheses level.
|
|
if (Lexer.is(AsmToken::LParen))
|
|
++ParenLevel;
|
|
else if (Lexer.is(AsmToken::RParen) && ParenLevel)
|
|
--ParenLevel;
|
|
|
|
// Append the token to the current argument list.
|
|
MA.push_back(getTok());
|
|
Lexer.Lex();
|
|
}
|
|
|
|
if (ParenLevel != 0)
|
|
return TokError("unbalanced parentheses in macro argument");
|
|
return false;
|
|
}
|
|
|
|
// Parse the macro instantiation arguments.
|
|
bool AsmParser::parseMacroArguments(const MCAsmMacro *M,
|
|
MCAsmMacroArguments &A) {
|
|
const unsigned NParameters = M ? M->Parameters.size() : 0;
|
|
bool NamedParametersFound = false;
|
|
SmallVector<SMLoc, 4> FALocs;
|
|
|
|
A.resize(NParameters);
|
|
FALocs.resize(NParameters);
|
|
|
|
// Parse two kinds of macro invocations:
|
|
// - macros defined without any parameters accept an arbitrary number of them
|
|
// - macros defined with parameters accept at most that many of them
|
|
bool HasVararg = NParameters ? M->Parameters.back().Vararg : false;
|
|
for (unsigned Parameter = 0; !NParameters || Parameter < NParameters;
|
|
++Parameter) {
|
|
SMLoc IDLoc = Lexer.getLoc();
|
|
MCAsmMacroParameter FA;
|
|
|
|
if (Lexer.is(AsmToken::Identifier) && Lexer.peekTok().is(AsmToken::Equal)) {
|
|
if (parseIdentifier(FA.Name))
|
|
return Error(IDLoc, "invalid argument identifier for formal argument");
|
|
|
|
if (Lexer.isNot(AsmToken::Equal))
|
|
return TokError("expected '=' after formal parameter identifier");
|
|
|
|
Lex();
|
|
|
|
NamedParametersFound = true;
|
|
}
|
|
bool Vararg = HasVararg && Parameter == (NParameters - 1);
|
|
|
|
if (NamedParametersFound && FA.Name.empty())
|
|
return Error(IDLoc, "cannot mix positional and keyword arguments");
|
|
|
|
SMLoc StrLoc = Lexer.getLoc();
|
|
SMLoc EndLoc;
|
|
if (AltMacroMode && Lexer.is(AsmToken::Percent)) {
|
|
const MCExpr *AbsoluteExp;
|
|
int64_t Value;
|
|
/// Eat '%'
|
|
Lex();
|
|
if (parseExpression(AbsoluteExp, EndLoc))
|
|
return false;
|
|
if (!AbsoluteExp->evaluateAsAbsolute(Value,
|
|
getStreamer().getAssemblerPtr()))
|
|
return Error(StrLoc, "expected absolute expression");
|
|
const char *StrChar = StrLoc.getPointer();
|
|
const char *EndChar = EndLoc.getPointer();
|
|
AsmToken newToken(AsmToken::Integer,
|
|
StringRef(StrChar, EndChar - StrChar), Value);
|
|
FA.Value.push_back(newToken);
|
|
} else if (AltMacroMode && Lexer.is(AsmToken::Less) &&
|
|
isAngleBracketString(StrLoc, EndLoc)) {
|
|
const char *StrChar = StrLoc.getPointer();
|
|
const char *EndChar = EndLoc.getPointer();
|
|
jumpToLoc(EndLoc, CurBuffer);
|
|
/// Eat from '<' to '>'
|
|
Lex();
|
|
AsmToken newToken(AsmToken::String,
|
|
StringRef(StrChar, EndChar - StrChar));
|
|
FA.Value.push_back(newToken);
|
|
} else if(parseMacroArgument(FA.Value, Vararg))
|
|
return true;
|
|
|
|
unsigned PI = Parameter;
|
|
if (!FA.Name.empty()) {
|
|
unsigned FAI = 0;
|
|
for (FAI = 0; FAI < NParameters; ++FAI)
|
|
if (M->Parameters[FAI].Name == FA.Name)
|
|
break;
|
|
|
|
if (FAI >= NParameters) {
|
|
assert(M && "expected macro to be defined");
|
|
return Error(IDLoc, "parameter named '" + FA.Name +
|
|
"' does not exist for macro '" + M->Name + "'");
|
|
}
|
|
PI = FAI;
|
|
}
|
|
|
|
if (!FA.Value.empty()) {
|
|
if (A.size() <= PI)
|
|
A.resize(PI + 1);
|
|
A[PI] = FA.Value;
|
|
|
|
if (FALocs.size() <= PI)
|
|
FALocs.resize(PI + 1);
|
|
|
|
FALocs[PI] = Lexer.getLoc();
|
|
}
|
|
|
|
// At the end of the statement, fill in remaining arguments that have
|
|
// default values. If there aren't any, then the next argument is
|
|
// required but missing
|
|
if (Lexer.is(AsmToken::EndOfStatement)) {
|
|
bool Failure = false;
|
|
for (unsigned FAI = 0; FAI < NParameters; ++FAI) {
|
|
if (A[FAI].empty()) {
|
|
if (M->Parameters[FAI].Required) {
|
|
Error(FALocs[FAI].isValid() ? FALocs[FAI] : Lexer.getLoc(),
|
|
"missing value for required parameter "
|
|
"'" + M->Parameters[FAI].Name + "' in macro '" + M->Name + "'");
|
|
Failure = true;
|
|
}
|
|
|
|
if (!M->Parameters[FAI].Value.empty())
|
|
A[FAI] = M->Parameters[FAI].Value;
|
|
}
|
|
}
|
|
return Failure;
|
|
}
|
|
|
|
parseOptionalToken(AsmToken::Comma);
|
|
}
|
|
|
|
return TokError("too many positional arguments");
|
|
}
|
|
|
|
bool AsmParser::handleMacroEntry(MCAsmMacro *M, SMLoc NameLoc) {
|
|
// Arbitrarily limit macro nesting depth (default matches 'as'). We can
|
|
// eliminate this, although we should protect against infinite loops.
|
|
unsigned MaxNestingDepth = AsmMacroMaxNestingDepth;
|
|
if (ActiveMacros.size() == MaxNestingDepth) {
|
|
std::ostringstream MaxNestingDepthError;
|
|
MaxNestingDepthError << "macros cannot be nested more than "
|
|
<< MaxNestingDepth << " levels deep."
|
|
<< " Use -asm-macro-max-nesting-depth to increase "
|
|
"this limit.";
|
|
return TokError(MaxNestingDepthError.str());
|
|
}
|
|
|
|
MCAsmMacroArguments A;
|
|
if (parseMacroArguments(M, A))
|
|
return true;
|
|
|
|
// Macro instantiation is lexical, unfortunately. We construct a new buffer
|
|
// to hold the macro body with substitutions.
|
|
SmallString<256> Buf;
|
|
raw_svector_ostream OS(Buf);
|
|
|
|
if ((!IsDarwin || M->Parameters.size()) && M->Parameters.size() != A.size())
|
|
return Error(getTok().getLoc(), "Wrong number of arguments");
|
|
if (expandMacro(OS, *M, M->Parameters, A, true))
|
|
return true;
|
|
|
|
// We include the .endmacro in the buffer as our cue to exit the macro
|
|
// instantiation.
|
|
OS << ".endmacro\n";
|
|
|
|
std::unique_ptr<MemoryBuffer> Instantiation =
|
|
MemoryBuffer::getMemBufferCopy(OS.str(), "<instantiation>");
|
|
|
|
// Create the macro instantiation object and add to the current macro
|
|
// instantiation stack.
|
|
MacroInstantiation *MI = new MacroInstantiation{
|
|
NameLoc, CurBuffer, getTok().getLoc(), TheCondStack.size()};
|
|
ActiveMacros.push_back(MI);
|
|
|
|
++NumOfMacroInstantiations;
|
|
|
|
// Jump to the macro instantiation and prime the lexer.
|
|
CurBuffer = SrcMgr.AddNewSourceBuffer(std::move(Instantiation), SMLoc());
|
|
Lexer.setBuffer(SrcMgr.getMemoryBuffer(CurBuffer)->getBuffer());
|
|
Lex();
|
|
|
|
return false;
|
|
}
|
|
|
|
void AsmParser::handleMacroExit() {
|
|
// Jump to the EndOfStatement we should return to, and consume it.
|
|
jumpToLoc(ActiveMacros.back()->ExitLoc, ActiveMacros.back()->ExitBuffer);
|
|
Lex();
|
|
// If .endm/.endr is followed by \n instead of a comment, consume it so that
|
|
// we don't print an excess \n.
|
|
if (getTok().is(AsmToken::EndOfStatement))
|
|
Lex();
|
|
|
|
// Pop the instantiation entry.
|
|
delete ActiveMacros.back();
|
|
ActiveMacros.pop_back();
|
|
}
|
|
|
|
bool AsmParser::parseAssignment(StringRef Name, AssignmentKind Kind) {
|
|
MCSymbol *Sym;
|
|
const MCExpr *Value;
|
|
SMLoc ExprLoc = getTok().getLoc();
|
|
bool AllowRedef =
|
|
Kind == AssignmentKind::Set || Kind == AssignmentKind::Equal;
|
|
if (MCParserUtils::parseAssignmentExpression(Name, AllowRedef, *this, Sym,
|
|
Value))
|
|
return true;
|
|
|
|
if (!Sym) {
|
|
// In the case where we parse an expression starting with a '.', we will
|
|
// not generate an error, nor will we create a symbol. In this case we
|
|
// should just return out.
|
|
return false;
|
|
}
|
|
|
|
if (discardLTOSymbol(Name))
|
|
return false;
|
|
|
|
// Do the assignment.
|
|
switch (Kind) {
|
|
case AssignmentKind::Equal:
|
|
Out.emitAssignment(Sym, Value);
|
|
break;
|
|
case AssignmentKind::Set:
|
|
case AssignmentKind::Equiv:
|
|
Out.emitAssignment(Sym, Value);
|
|
Out.emitSymbolAttribute(Sym, MCSA_NoDeadStrip);
|
|
break;
|
|
case AssignmentKind::LTOSetConditional:
|
|
if (Value->getKind() != MCExpr::SymbolRef)
|
|
return Error(ExprLoc, "expected identifier");
|
|
|
|
Out.emitConditionalAssignment(Sym, Value);
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseIdentifier:
|
|
/// ::= identifier
|
|
/// ::= string
|
|
bool AsmParser::parseIdentifier(StringRef &Res) {
|
|
// The assembler has relaxed rules for accepting identifiers, in particular we
|
|
// allow things like '.globl $foo' and '.def @feat.00', which would normally be
|
|
// separate tokens. At this level, we have already lexed so we cannot (currently)
|
|
// handle this as a context dependent token, instead we detect adjacent tokens
|
|
// and return the combined identifier.
|
|
if (Lexer.is(AsmToken::Dollar) || Lexer.is(AsmToken::At)) {
|
|
SMLoc PrefixLoc = getLexer().getLoc();
|
|
|
|
// Consume the prefix character, and check for a following identifier.
|
|
|
|
AsmToken Buf[1];
|
|
Lexer.peekTokens(Buf, false);
|
|
|
|
if (Buf[0].isNot(AsmToken::Identifier) && Buf[0].isNot(AsmToken::Integer))
|
|
return true;
|
|
|
|
// We have a '$' or '@' followed by an identifier or integer token, make
|
|
// sure they are adjacent.
|
|
if (PrefixLoc.getPointer() + 1 != Buf[0].getLoc().getPointer())
|
|
return true;
|
|
|
|
// eat $ or @
|
|
Lexer.Lex(); // Lexer's Lex guarantees consecutive token.
|
|
// Construct the joined identifier and consume the token.
|
|
Res = StringRef(PrefixLoc.getPointer(), getTok().getString().size() + 1);
|
|
Lex(); // Parser Lex to maintain invariants.
|
|
return false;
|
|
}
|
|
|
|
if (Lexer.isNot(AsmToken::Identifier) && Lexer.isNot(AsmToken::String))
|
|
return true;
|
|
|
|
Res = getTok().getIdentifier();
|
|
|
|
Lex(); // Consume the identifier token.
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveSet:
|
|
/// ::= .equ identifier ',' expression
|
|
/// ::= .equiv identifier ',' expression
|
|
/// ::= .set identifier ',' expression
|
|
/// ::= .lto_set_conditional identifier ',' expression
|
|
bool AsmParser::parseDirectiveSet(StringRef IDVal, AssignmentKind Kind) {
|
|
StringRef Name;
|
|
if (check(parseIdentifier(Name), "expected identifier") || parseComma() ||
|
|
parseAssignment(Name, Kind))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool AsmParser::parseEscapedString(std::string &Data) {
|
|
if (check(getTok().isNot(AsmToken::String), "expected string"))
|
|
return true;
|
|
|
|
Data = "";
|
|
StringRef Str = getTok().getStringContents();
|
|
for (unsigned i = 0, e = Str.size(); i != e; ++i) {
|
|
if (Str[i] != '\\') {
|
|
if ((Str[i] == '\n') || (Str[i] == '\r')) {
|
|
// Don't double-warn for Windows newlines.
|
|
if ((Str[i] == '\n') && (i > 0) && (Str[i - 1] == '\r'))
|
|
continue;
|
|
|
|
SMLoc NewlineLoc = SMLoc::getFromPointer(Str.data() + i);
|
|
if (Warning(NewlineLoc, "unterminated string; newline inserted"))
|
|
return true;
|
|
}
|
|
Data += Str[i];
|
|
continue;
|
|
}
|
|
|
|
// Recognize escaped characters. Note that this escape semantics currently
|
|
// loosely follows Darwin 'as'.
|
|
++i;
|
|
if (i == e)
|
|
return TokError("unexpected backslash at end of string");
|
|
|
|
// Recognize hex sequences similarly to GNU 'as'.
|
|
if (Str[i] == 'x' || Str[i] == 'X') {
|
|
size_t length = Str.size();
|
|
if (i + 1 >= length || !isHexDigit(Str[i + 1]))
|
|
return TokError("invalid hexadecimal escape sequence");
|
|
|
|
// Consume hex characters. GNU 'as' reads all hexadecimal characters and
|
|
// then truncates to the lower 16 bits. Seems reasonable.
|
|
unsigned Value = 0;
|
|
while (i + 1 < length && isHexDigit(Str[i + 1]))
|
|
Value = Value * 16 + hexDigitValue(Str[++i]);
|
|
|
|
Data += (unsigned char)(Value & 0xFF);
|
|
continue;
|
|
}
|
|
|
|
// Recognize octal sequences.
|
|
if ((unsigned)(Str[i] - '0') <= 7) {
|
|
// Consume up to three octal characters.
|
|
unsigned Value = Str[i] - '0';
|
|
|
|
if (i + 1 != e && ((unsigned)(Str[i + 1] - '0')) <= 7) {
|
|
++i;
|
|
Value = Value * 8 + (Str[i] - '0');
|
|
|
|
if (i + 1 != e && ((unsigned)(Str[i + 1] - '0')) <= 7) {
|
|
++i;
|
|
Value = Value * 8 + (Str[i] - '0');
|
|
}
|
|
}
|
|
|
|
if (Value > 255)
|
|
return TokError("invalid octal escape sequence (out of range)");
|
|
|
|
Data += (unsigned char)Value;
|
|
continue;
|
|
}
|
|
|
|
// Otherwise recognize individual escapes.
|
|
switch (Str[i]) {
|
|
default:
|
|
// Just reject invalid escape sequences for now.
|
|
return TokError("invalid escape sequence (unrecognized character)");
|
|
|
|
case 'b': Data += '\b'; break;
|
|
case 'f': Data += '\f'; break;
|
|
case 'n': Data += '\n'; break;
|
|
case 'r': Data += '\r'; break;
|
|
case 't': Data += '\t'; break;
|
|
case '"': Data += '"'; break;
|
|
case '\\': Data += '\\'; break;
|
|
}
|
|
}
|
|
|
|
Lex();
|
|
return false;
|
|
}
|
|
|
|
bool AsmParser::parseAngleBracketString(std::string &Data) {
|
|
SMLoc EndLoc, StartLoc = getTok().getLoc();
|
|
if (isAngleBracketString(StartLoc, EndLoc)) {
|
|
const char *StartChar = StartLoc.getPointer() + 1;
|
|
const char *EndChar = EndLoc.getPointer() - 1;
|
|
jumpToLoc(EndLoc, CurBuffer);
|
|
/// Eat from '<' to '>'
|
|
Lex();
|
|
|
|
Data = angleBracketString(StringRef(StartChar, EndChar - StartChar));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// parseDirectiveAscii:
|
|
// ::= .ascii [ "string"+ ( , "string"+ )* ]
|
|
/// ::= ( .asciz | .string ) [ "string" ( , "string" )* ]
|
|
bool AsmParser::parseDirectiveAscii(StringRef IDVal, bool ZeroTerminated) {
|
|
auto parseOp = [&]() -> bool {
|
|
std::string Data;
|
|
if (checkForValidSection())
|
|
return true;
|
|
// Only support spaces as separators for .ascii directive for now. See the
|
|
// discusssion at https://reviews.llvm.org/D91460 for more details.
|
|
do {
|
|
if (parseEscapedString(Data))
|
|
return true;
|
|
getStreamer().emitBytes(Data);
|
|
} while (!ZeroTerminated && getTok().is(AsmToken::String));
|
|
if (ZeroTerminated)
|
|
getStreamer().emitBytes(StringRef("\0", 1));
|
|
return false;
|
|
};
|
|
|
|
return parseMany(parseOp);
|
|
}
|
|
|
|
/// parseDirectiveReloc
|
|
/// ::= .reloc expression , identifier [ , expression ]
|
|
bool AsmParser::parseDirectiveReloc(SMLoc DirectiveLoc) {
|
|
const MCExpr *Offset;
|
|
const MCExpr *Expr = nullptr;
|
|
|
|
if (parseExpression(Offset))
|
|
return true;
|
|
if (parseComma() ||
|
|
check(getTok().isNot(AsmToken::Identifier), "expected relocation name"))
|
|
return true;
|
|
|
|
SMLoc NameLoc = Lexer.getTok().getLoc();
|
|
StringRef Name = Lexer.getTok().getIdentifier();
|
|
Lex();
|
|
|
|
if (Lexer.is(AsmToken::Comma)) {
|
|
Lex();
|
|
SMLoc ExprLoc = Lexer.getLoc();
|
|
if (parseExpression(Expr))
|
|
return true;
|
|
|
|
MCValue Value;
|
|
if (!Expr->evaluateAsRelocatable(Value, nullptr))
|
|
return Error(ExprLoc, "expression must be relocatable");
|
|
}
|
|
|
|
if (parseEOL())
|
|
return true;
|
|
|
|
getStreamer().emitRelocDirective(*Offset, Name, Expr, NameLoc);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveValue
|
|
/// ::= (.byte | .short | ... ) [ expression (, expression)* ]
|
|
bool AsmParser::parseDirectiveValue(StringRef IDVal, unsigned Size) {
|
|
auto parseOp = [&]() -> bool {
|
|
const MCExpr *Value;
|
|
SMLoc ExprLoc = getLexer().getLoc();
|
|
if (checkForValidSection() || getTargetParser().parseDataExpr(Value))
|
|
return true;
|
|
// Special case constant expressions to match code generator.
|
|
if (const MCConstantExpr *MCE = dyn_cast<MCConstantExpr>(Value)) {
|
|
assert(Size <= 8 && "Invalid size");
|
|
uint64_t IntValue = MCE->getValue();
|
|
if (!isUIntN(8 * Size, IntValue) && !isIntN(8 * Size, IntValue))
|
|
return Error(ExprLoc, "out of range literal value");
|
|
getStreamer().emitIntValue(IntValue, Size);
|
|
} else
|
|
getStreamer().emitValue(Value, Size, ExprLoc);
|
|
return false;
|
|
};
|
|
|
|
return parseMany(parseOp);
|
|
}
|
|
|
|
static bool parseHexOcta(AsmParser &Asm, uint64_t &hi, uint64_t &lo) {
|
|
if (Asm.getTok().isNot(AsmToken::Integer) &&
|
|
Asm.getTok().isNot(AsmToken::BigNum))
|
|
return Asm.TokError("unknown token in expression");
|
|
SMLoc ExprLoc = Asm.getTok().getLoc();
|
|
APInt IntValue = Asm.getTok().getAPIntVal();
|
|
Asm.Lex();
|
|
if (!IntValue.isIntN(128))
|
|
return Asm.Error(ExprLoc, "out of range literal value");
|
|
if (!IntValue.isIntN(64)) {
|
|
hi = IntValue.getHiBits(IntValue.getBitWidth() - 64).getZExtValue();
|
|
lo = IntValue.getLoBits(64).getZExtValue();
|
|
} else {
|
|
hi = 0;
|
|
lo = IntValue.getZExtValue();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// ParseDirectiveOctaValue
|
|
/// ::= .octa [ hexconstant (, hexconstant)* ]
|
|
|
|
bool AsmParser::parseDirectiveOctaValue(StringRef IDVal) {
|
|
auto parseOp = [&]() -> bool {
|
|
if (checkForValidSection())
|
|
return true;
|
|
uint64_t hi, lo;
|
|
if (parseHexOcta(*this, hi, lo))
|
|
return true;
|
|
if (MAI.isLittleEndian()) {
|
|
getStreamer().emitInt64(lo);
|
|
getStreamer().emitInt64(hi);
|
|
} else {
|
|
getStreamer().emitInt64(hi);
|
|
getStreamer().emitInt64(lo);
|
|
}
|
|
return false;
|
|
};
|
|
|
|
return parseMany(parseOp);
|
|
}
|
|
|
|
bool AsmParser::parseRealValue(const fltSemantics &Semantics, APInt &Res) {
|
|
// We don't truly support arithmetic on floating point expressions, so we
|
|
// have to manually parse unary prefixes.
|
|
bool IsNeg = false;
|
|
if (getLexer().is(AsmToken::Minus)) {
|
|
Lexer.Lex();
|
|
IsNeg = true;
|
|
} else if (getLexer().is(AsmToken::Plus))
|
|
Lexer.Lex();
|
|
|
|
if (Lexer.is(AsmToken::Error))
|
|
return TokError(Lexer.getErr());
|
|
if (Lexer.isNot(AsmToken::Integer) && Lexer.isNot(AsmToken::Real) &&
|
|
Lexer.isNot(AsmToken::Identifier))
|
|
return TokError("unexpected token in directive");
|
|
|
|
// Convert to an APFloat.
|
|
APFloat Value(Semantics);
|
|
StringRef IDVal = getTok().getString();
|
|
if (getLexer().is(AsmToken::Identifier)) {
|
|
if (!IDVal.compare_insensitive("infinity") ||
|
|
!IDVal.compare_insensitive("inf"))
|
|
Value = APFloat::getInf(Semantics);
|
|
else if (!IDVal.compare_insensitive("nan"))
|
|
Value = APFloat::getNaN(Semantics, false, ~0);
|
|
else
|
|
return TokError("invalid floating point literal");
|
|
} else if (errorToBool(
|
|
Value.convertFromString(IDVal, APFloat::rmNearestTiesToEven)
|
|
.takeError()))
|
|
return TokError("invalid floating point literal");
|
|
if (IsNeg)
|
|
Value.changeSign();
|
|
|
|
// Consume the numeric token.
|
|
Lex();
|
|
|
|
Res = Value.bitcastToAPInt();
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveRealValue
|
|
/// ::= (.single | .double) [ expression (, expression)* ]
|
|
bool AsmParser::parseDirectiveRealValue(StringRef IDVal,
|
|
const fltSemantics &Semantics) {
|
|
auto parseOp = [&]() -> bool {
|
|
APInt AsInt;
|
|
if (checkForValidSection() || parseRealValue(Semantics, AsInt))
|
|
return true;
|
|
getStreamer().emitIntValue(AsInt.getLimitedValue(),
|
|
AsInt.getBitWidth() / 8);
|
|
return false;
|
|
};
|
|
|
|
return parseMany(parseOp);
|
|
}
|
|
|
|
/// parseDirectiveZero
|
|
/// ::= .zero expression
|
|
bool AsmParser::parseDirectiveZero() {
|
|
SMLoc NumBytesLoc = Lexer.getLoc();
|
|
const MCExpr *NumBytes;
|
|
if (checkForValidSection() || parseExpression(NumBytes))
|
|
return true;
|
|
|
|
int64_t Val = 0;
|
|
if (getLexer().is(AsmToken::Comma)) {
|
|
Lex();
|
|
if (parseAbsoluteExpression(Val))
|
|
return true;
|
|
}
|
|
|
|
if (parseEOL())
|
|
return true;
|
|
getStreamer().emitFill(*NumBytes, Val, NumBytesLoc);
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveFill
|
|
/// ::= .fill expression [ , expression [ , expression ] ]
|
|
bool AsmParser::parseDirectiveFill() {
|
|
SMLoc NumValuesLoc = Lexer.getLoc();
|
|
const MCExpr *NumValues;
|
|
if (checkForValidSection() || parseExpression(NumValues))
|
|
return true;
|
|
|
|
int64_t FillSize = 1;
|
|
int64_t FillExpr = 0;
|
|
|
|
SMLoc SizeLoc, ExprLoc;
|
|
|
|
if (parseOptionalToken(AsmToken::Comma)) {
|
|
SizeLoc = getTok().getLoc();
|
|
if (parseAbsoluteExpression(FillSize))
|
|
return true;
|
|
if (parseOptionalToken(AsmToken::Comma)) {
|
|
ExprLoc = getTok().getLoc();
|
|
if (parseAbsoluteExpression(FillExpr))
|
|
return true;
|
|
}
|
|
}
|
|
if (parseEOL())
|
|
return true;
|
|
|
|
if (FillSize < 0) {
|
|
Warning(SizeLoc, "'.fill' directive with negative size has no effect");
|
|
return false;
|
|
}
|
|
if (FillSize > 8) {
|
|
Warning(SizeLoc, "'.fill' directive with size greater than 8 has been truncated to 8");
|
|
FillSize = 8;
|
|
}
|
|
|
|
if (!isUInt<32>(FillExpr) && FillSize > 4)
|
|
Warning(ExprLoc, "'.fill' directive pattern has been truncated to 32-bits");
|
|
|
|
getStreamer().emitFill(*NumValues, FillSize, FillExpr, NumValuesLoc);
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveOrg
|
|
/// ::= .org expression [ , expression ]
|
|
bool AsmParser::parseDirectiveOrg() {
|
|
const MCExpr *Offset;
|
|
SMLoc OffsetLoc = Lexer.getLoc();
|
|
if (checkForValidSection() || parseExpression(Offset))
|
|
return true;
|
|
|
|
// Parse optional fill expression.
|
|
int64_t FillExpr = 0;
|
|
if (parseOptionalToken(AsmToken::Comma))
|
|
if (parseAbsoluteExpression(FillExpr))
|
|
return true;
|
|
if (parseEOL())
|
|
return true;
|
|
|
|
getStreamer().emitValueToOffset(Offset, FillExpr, OffsetLoc);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveAlign
|
|
/// ::= {.align, ...} expression [ , expression [ , expression ]]
|
|
bool AsmParser::parseDirectiveAlign(bool IsPow2, uint8_t ValueSize) {
|
|
SMLoc AlignmentLoc = getLexer().getLoc();
|
|
int64_t Alignment;
|
|
SMLoc MaxBytesLoc;
|
|
bool HasFillExpr = false;
|
|
int64_t FillExpr = 0;
|
|
int64_t MaxBytesToFill = 0;
|
|
SMLoc FillExprLoc;
|
|
|
|
auto parseAlign = [&]() -> bool {
|
|
if (parseAbsoluteExpression(Alignment))
|
|
return true;
|
|
if (parseOptionalToken(AsmToken::Comma)) {
|
|
// The fill expression can be omitted while specifying a maximum number of
|
|
// alignment bytes, e.g:
|
|
// .align 3,,4
|
|
if (getTok().isNot(AsmToken::Comma)) {
|
|
HasFillExpr = true;
|
|
if (parseTokenLoc(FillExprLoc) || parseAbsoluteExpression(FillExpr))
|
|
return true;
|
|
}
|
|
if (parseOptionalToken(AsmToken::Comma))
|
|
if (parseTokenLoc(MaxBytesLoc) ||
|
|
parseAbsoluteExpression(MaxBytesToFill))
|
|
return true;
|
|
}
|
|
return parseEOL();
|
|
};
|
|
|
|
if (checkForValidSection())
|
|
return true;
|
|
// Ignore empty '.p2align' directives for GNU-as compatibility
|
|
if (IsPow2 && (ValueSize == 1) && getTok().is(AsmToken::EndOfStatement)) {
|
|
Warning(AlignmentLoc, "p2align directive with no operand(s) is ignored");
|
|
return parseEOL();
|
|
}
|
|
if (parseAlign())
|
|
return true;
|
|
|
|
// Always emit an alignment here even if we thrown an error.
|
|
bool ReturnVal = false;
|
|
|
|
// Compute alignment in bytes.
|
|
if (IsPow2) {
|
|
// FIXME: Diagnose overflow.
|
|
if (Alignment >= 32) {
|
|
ReturnVal |= Error(AlignmentLoc, "invalid alignment value");
|
|
Alignment = 31;
|
|
}
|
|
|
|
Alignment = 1ULL << Alignment;
|
|
} else {
|
|
// Reject alignments that aren't either a power of two or zero,
|
|
// for gas compatibility. Alignment of zero is silently rounded
|
|
// up to one.
|
|
if (Alignment == 0)
|
|
Alignment = 1;
|
|
else if (!isPowerOf2_64(Alignment)) {
|
|
ReturnVal |= Error(AlignmentLoc, "alignment must be a power of 2");
|
|
Alignment = llvm::bit_floor<uint64_t>(Alignment);
|
|
}
|
|
if (!isUInt<32>(Alignment)) {
|
|
ReturnVal |= Error(AlignmentLoc, "alignment must be smaller than 2**32");
|
|
Alignment = 1u << 31;
|
|
}
|
|
}
|
|
|
|
// Diagnose non-sensical max bytes to align.
|
|
if (MaxBytesLoc.isValid()) {
|
|
if (MaxBytesToFill < 1) {
|
|
ReturnVal |= Error(MaxBytesLoc,
|
|
"alignment directive can never be satisfied in this "
|
|
"many bytes, ignoring maximum bytes expression");
|
|
MaxBytesToFill = 0;
|
|
}
|
|
|
|
if (MaxBytesToFill >= Alignment) {
|
|
Warning(MaxBytesLoc, "maximum bytes expression exceeds alignment and "
|
|
"has no effect");
|
|
MaxBytesToFill = 0;
|
|
}
|
|
}
|
|
|
|
const MCSection *Section = getStreamer().getCurrentSectionOnly();
|
|
assert(Section && "must have section to emit alignment");
|
|
|
|
if (HasFillExpr && FillExpr != 0 && Section->isBssSection()) {
|
|
ReturnVal |=
|
|
Warning(FillExprLoc, "ignoring non-zero fill value in BSS section '" +
|
|
Section->getName() + "'");
|
|
FillExpr = 0;
|
|
}
|
|
|
|
// Check whether we should use optimal code alignment for this .align
|
|
// directive.
|
|
if (MAI.useCodeAlign(*Section) && !HasFillExpr) {
|
|
getStreamer().emitCodeAlignment(
|
|
Align(Alignment), &getTargetParser().getSTI(), MaxBytesToFill);
|
|
} else {
|
|
// FIXME: Target specific behavior about how the "extra" bytes are filled.
|
|
getStreamer().emitValueToAlignment(Align(Alignment), FillExpr, ValueSize,
|
|
MaxBytesToFill);
|
|
}
|
|
|
|
return ReturnVal;
|
|
}
|
|
|
|
/// parseDirectiveFile
|
|
/// ::= .file filename
|
|
/// ::= .file number [directory] filename [md5 checksum] [source source-text]
|
|
bool AsmParser::parseDirectiveFile(SMLoc DirectiveLoc) {
|
|
// FIXME: I'm not sure what this is.
|
|
int64_t FileNumber = -1;
|
|
if (getLexer().is(AsmToken::Integer)) {
|
|
FileNumber = getTok().getIntVal();
|
|
Lex();
|
|
|
|
if (FileNumber < 0)
|
|
return TokError("negative file number");
|
|
}
|
|
|
|
std::string Path;
|
|
|
|
// Usually the directory and filename together, otherwise just the directory.
|
|
// Allow the strings to have escaped octal character sequence.
|
|
if (parseEscapedString(Path))
|
|
return true;
|
|
|
|
StringRef Directory;
|
|
StringRef Filename;
|
|
std::string FilenameData;
|
|
if (getLexer().is(AsmToken::String)) {
|
|
if (check(FileNumber == -1,
|
|
"explicit path specified, but no file number") ||
|
|
parseEscapedString(FilenameData))
|
|
return true;
|
|
Filename = FilenameData;
|
|
Directory = Path;
|
|
} else {
|
|
Filename = Path;
|
|
}
|
|
|
|
uint64_t MD5Hi, MD5Lo;
|
|
bool HasMD5 = false;
|
|
|
|
std::optional<StringRef> Source;
|
|
bool HasSource = false;
|
|
std::string SourceString;
|
|
|
|
while (!parseOptionalToken(AsmToken::EndOfStatement)) {
|
|
StringRef Keyword;
|
|
if (check(getTok().isNot(AsmToken::Identifier),
|
|
"unexpected token in '.file' directive") ||
|
|
parseIdentifier(Keyword))
|
|
return true;
|
|
if (Keyword == "md5") {
|
|
HasMD5 = true;
|
|
if (check(FileNumber == -1,
|
|
"MD5 checksum specified, but no file number") ||
|
|
parseHexOcta(*this, MD5Hi, MD5Lo))
|
|
return true;
|
|
} else if (Keyword == "source") {
|
|
HasSource = true;
|
|
if (check(FileNumber == -1,
|
|
"source specified, but no file number") ||
|
|
check(getTok().isNot(AsmToken::String),
|
|
"unexpected token in '.file' directive") ||
|
|
parseEscapedString(SourceString))
|
|
return true;
|
|
} else {
|
|
return TokError("unexpected token in '.file' directive");
|
|
}
|
|
}
|
|
|
|
if (FileNumber == -1) {
|
|
// Ignore the directive if there is no number and the target doesn't support
|
|
// numberless .file directives. This allows some portability of assembler
|
|
// between different object file formats.
|
|
if (getContext().getAsmInfo()->hasSingleParameterDotFile())
|
|
getStreamer().emitFileDirective(Filename);
|
|
} else {
|
|
// In case there is a -g option as well as debug info from directive .file,
|
|
// we turn off the -g option, directly use the existing debug info instead.
|
|
// Throw away any implicit file table for the assembler source.
|
|
if (Ctx.getGenDwarfForAssembly()) {
|
|
Ctx.getMCDwarfLineTable(0).resetFileTable();
|
|
Ctx.setGenDwarfForAssembly(false);
|
|
}
|
|
|
|
std::optional<MD5::MD5Result> CKMem;
|
|
if (HasMD5) {
|
|
MD5::MD5Result Sum;
|
|
for (unsigned i = 0; i != 8; ++i) {
|
|
Sum[i] = uint8_t(MD5Hi >> ((7 - i) * 8));
|
|
Sum[i + 8] = uint8_t(MD5Lo >> ((7 - i) * 8));
|
|
}
|
|
CKMem = Sum;
|
|
}
|
|
if (HasSource) {
|
|
char *SourceBuf = static_cast<char *>(Ctx.allocate(SourceString.size()));
|
|
memcpy(SourceBuf, SourceString.data(), SourceString.size());
|
|
Source = StringRef(SourceBuf, SourceString.size());
|
|
}
|
|
if (FileNumber == 0) {
|
|
// Upgrade to Version 5 for assembly actions like clang -c a.s.
|
|
if (Ctx.getDwarfVersion() < 5)
|
|
Ctx.setDwarfVersion(5);
|
|
getStreamer().emitDwarfFile0Directive(Directory, Filename, CKMem, Source);
|
|
} else {
|
|
Expected<unsigned> FileNumOrErr = getStreamer().tryEmitDwarfFileDirective(
|
|
FileNumber, Directory, Filename, CKMem, Source);
|
|
if (!FileNumOrErr)
|
|
return Error(DirectiveLoc, toString(FileNumOrErr.takeError()));
|
|
}
|
|
// Alert the user if there are some .file directives with MD5 and some not.
|
|
// But only do that once.
|
|
if (!ReportedInconsistentMD5 && !Ctx.isDwarfMD5UsageConsistent(0)) {
|
|
ReportedInconsistentMD5 = true;
|
|
return Warning(DirectiveLoc, "inconsistent use of MD5 checksums");
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveLine
|
|
/// ::= .line [number]
|
|
bool AsmParser::parseDirectiveLine() {
|
|
parseOptionalToken(AsmToken::Integer);
|
|
return parseEOL();
|
|
}
|
|
|
|
/// parseDirectiveLoc
|
|
/// ::= .loc FileNumber [LineNumber] [ColumnPos] [basic_block] [prologue_end]
|
|
/// [epilogue_begin] [is_stmt VALUE] [isa VALUE]
|
|
/// The first number is a file number, must have been previously assigned with
|
|
/// a .file directive, the second number is the line number and optionally the
|
|
/// third number is a column position (zero if not specified). The remaining
|
|
/// optional items are .loc sub-directives.
|
|
bool AsmParser::parseDirectiveLoc() {
|
|
int64_t FileNumber = 0, LineNumber = 0;
|
|
SMLoc Loc = getTok().getLoc();
|
|
if (parseIntToken(FileNumber) ||
|
|
check(FileNumber < 1 && Ctx.getDwarfVersion() < 5, Loc,
|
|
"file number less than one in '.loc' directive") ||
|
|
check(!getContext().isValidDwarfFileNumber(FileNumber), Loc,
|
|
"unassigned file number in '.loc' directive"))
|
|
return true;
|
|
|
|
// optional
|
|
if (getLexer().is(AsmToken::Integer)) {
|
|
LineNumber = getTok().getIntVal();
|
|
if (LineNumber < 0)
|
|
return TokError("line number less than zero in '.loc' directive");
|
|
Lex();
|
|
}
|
|
|
|
int64_t ColumnPos = 0;
|
|
if (getLexer().is(AsmToken::Integer)) {
|
|
ColumnPos = getTok().getIntVal();
|
|
if (ColumnPos < 0)
|
|
return TokError("column position less than zero in '.loc' directive");
|
|
Lex();
|
|
}
|
|
|
|
auto PrevFlags = getContext().getCurrentDwarfLoc().getFlags();
|
|
unsigned Flags = PrevFlags & DWARF2_FLAG_IS_STMT;
|
|
unsigned Isa = 0;
|
|
int64_t Discriminator = 0;
|
|
|
|
auto parseLocOp = [&]() -> bool {
|
|
StringRef Name;
|
|
SMLoc Loc = getTok().getLoc();
|
|
if (parseIdentifier(Name))
|
|
return TokError("unexpected token in '.loc' directive");
|
|
|
|
if (Name == "basic_block")
|
|
Flags |= DWARF2_FLAG_BASIC_BLOCK;
|
|
else if (Name == "prologue_end")
|
|
Flags |= DWARF2_FLAG_PROLOGUE_END;
|
|
else if (Name == "epilogue_begin")
|
|
Flags |= DWARF2_FLAG_EPILOGUE_BEGIN;
|
|
else if (Name == "is_stmt") {
|
|
Loc = getTok().getLoc();
|
|
const MCExpr *Value;
|
|
if (parseExpression(Value))
|
|
return true;
|
|
// The expression must be the constant 0 or 1.
|
|
if (const MCConstantExpr *MCE = dyn_cast<MCConstantExpr>(Value)) {
|
|
int Value = MCE->getValue();
|
|
if (Value == 0)
|
|
Flags &= ~DWARF2_FLAG_IS_STMT;
|
|
else if (Value == 1)
|
|
Flags |= DWARF2_FLAG_IS_STMT;
|
|
else
|
|
return Error(Loc, "is_stmt value not 0 or 1");
|
|
} else {
|
|
return Error(Loc, "is_stmt value not the constant value of 0 or 1");
|
|
}
|
|
} else if (Name == "isa") {
|
|
Loc = getTok().getLoc();
|
|
const MCExpr *Value;
|
|
if (parseExpression(Value))
|
|
return true;
|
|
// The expression must be a constant greater or equal to 0.
|
|
if (const MCConstantExpr *MCE = dyn_cast<MCConstantExpr>(Value)) {
|
|
int Value = MCE->getValue();
|
|
if (Value < 0)
|
|
return Error(Loc, "isa number less than zero");
|
|
Isa = Value;
|
|
} else {
|
|
return Error(Loc, "isa number not a constant value");
|
|
}
|
|
} else if (Name == "discriminator") {
|
|
if (parseAbsoluteExpression(Discriminator))
|
|
return true;
|
|
} else {
|
|
return Error(Loc, "unknown sub-directive in '.loc' directive");
|
|
}
|
|
return false;
|
|
};
|
|
|
|
if (parseMany(parseLocOp, false /*hasComma*/))
|
|
return true;
|
|
|
|
getStreamer().emitDwarfLocDirective(FileNumber, LineNumber, ColumnPos, Flags,
|
|
Isa, Discriminator, StringRef());
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveLoc
|
|
/// ::= .loc_label label
|
|
bool AsmParser::parseDirectiveLocLabel(SMLoc DirectiveLoc) {
|
|
StringRef Name;
|
|
DirectiveLoc = Lexer.getLoc();
|
|
if (parseIdentifier(Name))
|
|
return TokError("expected identifier");
|
|
if (parseEOL())
|
|
return true;
|
|
getStreamer().emitDwarfLocLabelDirective(DirectiveLoc, Name);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveStabs
|
|
/// ::= .stabs string, number, number, number
|
|
bool AsmParser::parseDirectiveStabs() {
|
|
return TokError("unsupported directive '.stabs'");
|
|
}
|
|
|
|
/// parseDirectiveCVFile
|
|
/// ::= .cv_file number filename [checksum] [checksumkind]
|
|
bool AsmParser::parseDirectiveCVFile() {
|
|
SMLoc FileNumberLoc = getTok().getLoc();
|
|
int64_t FileNumber;
|
|
std::string Filename;
|
|
std::string Checksum;
|
|
int64_t ChecksumKind = 0;
|
|
|
|
if (parseIntToken(FileNumber, "expected file number") ||
|
|
check(FileNumber < 1, FileNumberLoc, "file number less than one") ||
|
|
check(getTok().isNot(AsmToken::String),
|
|
"unexpected token in '.cv_file' directive") ||
|
|
parseEscapedString(Filename))
|
|
return true;
|
|
if (!parseOptionalToken(AsmToken::EndOfStatement)) {
|
|
if (check(getTok().isNot(AsmToken::String),
|
|
"unexpected token in '.cv_file' directive") ||
|
|
parseEscapedString(Checksum) ||
|
|
parseIntToken(ChecksumKind,
|
|
"expected checksum kind in '.cv_file' directive") ||
|
|
parseEOL())
|
|
return true;
|
|
}
|
|
|
|
Checksum = fromHex(Checksum);
|
|
void *CKMem = Ctx.allocate(Checksum.size(), 1);
|
|
memcpy(CKMem, Checksum.data(), Checksum.size());
|
|
ArrayRef<uint8_t> ChecksumAsBytes(reinterpret_cast<const uint8_t *>(CKMem),
|
|
Checksum.size());
|
|
|
|
if (!getStreamer().emitCVFileDirective(FileNumber, Filename, ChecksumAsBytes,
|
|
static_cast<uint8_t>(ChecksumKind)))
|
|
return Error(FileNumberLoc, "file number already allocated");
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AsmParser::parseCVFunctionId(int64_t &FunctionId,
|
|
StringRef DirectiveName) {
|
|
SMLoc Loc;
|
|
return parseTokenLoc(Loc) ||
|
|
parseIntToken(FunctionId, "expected function id") ||
|
|
check(FunctionId < 0 || FunctionId >= UINT_MAX, Loc,
|
|
"expected function id within range [0, UINT_MAX)");
|
|
}
|
|
|
|
bool AsmParser::parseCVFileId(int64_t &FileNumber, StringRef DirectiveName) {
|
|
SMLoc Loc;
|
|
return parseTokenLoc(Loc) ||
|
|
parseIntToken(FileNumber, "expected file number") ||
|
|
check(FileNumber < 1, Loc,
|
|
"file number less than one in '" + DirectiveName +
|
|
"' directive") ||
|
|
check(!getCVContext().isValidFileNumber(FileNumber), Loc,
|
|
"unassigned file number in '" + DirectiveName + "' directive");
|
|
}
|
|
|
|
/// parseDirectiveCVFuncId
|
|
/// ::= .cv_func_id FunctionId
|
|
///
|
|
/// Introduces a function ID that can be used with .cv_loc.
|
|
bool AsmParser::parseDirectiveCVFuncId() {
|
|
SMLoc FunctionIdLoc = getTok().getLoc();
|
|
int64_t FunctionId;
|
|
|
|
if (parseCVFunctionId(FunctionId, ".cv_func_id") || parseEOL())
|
|
return true;
|
|
|
|
if (!getStreamer().emitCVFuncIdDirective(FunctionId))
|
|
return Error(FunctionIdLoc, "function id already allocated");
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCVInlineSiteId
|
|
/// ::= .cv_inline_site_id FunctionId
|
|
/// "within" IAFunc
|
|
/// "inlined_at" IAFile IALine [IACol]
|
|
///
|
|
/// Introduces a function ID that can be used with .cv_loc. Includes "inlined
|
|
/// at" source location information for use in the line table of the caller,
|
|
/// whether the caller is a real function or another inlined call site.
|
|
bool AsmParser::parseDirectiveCVInlineSiteId() {
|
|
SMLoc FunctionIdLoc = getTok().getLoc();
|
|
int64_t FunctionId;
|
|
int64_t IAFunc;
|
|
int64_t IAFile;
|
|
int64_t IALine;
|
|
int64_t IACol = 0;
|
|
|
|
// FunctionId
|
|
if (parseCVFunctionId(FunctionId, ".cv_inline_site_id"))
|
|
return true;
|
|
|
|
// "within"
|
|
if (check((getLexer().isNot(AsmToken::Identifier) ||
|
|
getTok().getIdentifier() != "within"),
|
|
"expected 'within' identifier in '.cv_inline_site_id' directive"))
|
|
return true;
|
|
Lex();
|
|
|
|
// IAFunc
|
|
if (parseCVFunctionId(IAFunc, ".cv_inline_site_id"))
|
|
return true;
|
|
|
|
// "inlined_at"
|
|
if (check((getLexer().isNot(AsmToken::Identifier) ||
|
|
getTok().getIdentifier() != "inlined_at"),
|
|
"expected 'inlined_at' identifier in '.cv_inline_site_id' "
|
|
"directive") )
|
|
return true;
|
|
Lex();
|
|
|
|
// IAFile IALine
|
|
if (parseCVFileId(IAFile, ".cv_inline_site_id") ||
|
|
parseIntToken(IALine, "expected line number after 'inlined_at'"))
|
|
return true;
|
|
|
|
// [IACol]
|
|
if (getLexer().is(AsmToken::Integer)) {
|
|
IACol = getTok().getIntVal();
|
|
Lex();
|
|
}
|
|
|
|
if (parseEOL())
|
|
return true;
|
|
|
|
if (!getStreamer().emitCVInlineSiteIdDirective(FunctionId, IAFunc, IAFile,
|
|
IALine, IACol, FunctionIdLoc))
|
|
return Error(FunctionIdLoc, "function id already allocated");
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCVLoc
|
|
/// ::= .cv_loc FunctionId FileNumber [LineNumber] [ColumnPos] [prologue_end]
|
|
/// [is_stmt VALUE]
|
|
/// The first number is a file number, must have been previously assigned with
|
|
/// a .file directive, the second number is the line number and optionally the
|
|
/// third number is a column position (zero if not specified). The remaining
|
|
/// optional items are .loc sub-directives.
|
|
bool AsmParser::parseDirectiveCVLoc() {
|
|
SMLoc DirectiveLoc = getTok().getLoc();
|
|
int64_t FunctionId, FileNumber;
|
|
if (parseCVFunctionId(FunctionId, ".cv_loc") ||
|
|
parseCVFileId(FileNumber, ".cv_loc"))
|
|
return true;
|
|
|
|
int64_t LineNumber = 0;
|
|
if (getLexer().is(AsmToken::Integer)) {
|
|
LineNumber = getTok().getIntVal();
|
|
if (LineNumber < 0)
|
|
return TokError("line number less than zero in '.cv_loc' directive");
|
|
Lex();
|
|
}
|
|
|
|
int64_t ColumnPos = 0;
|
|
if (getLexer().is(AsmToken::Integer)) {
|
|
ColumnPos = getTok().getIntVal();
|
|
if (ColumnPos < 0)
|
|
return TokError("column position less than zero in '.cv_loc' directive");
|
|
Lex();
|
|
}
|
|
|
|
bool PrologueEnd = false;
|
|
uint64_t IsStmt = 0;
|
|
|
|
auto parseOp = [&]() -> bool {
|
|
StringRef Name;
|
|
SMLoc Loc = getTok().getLoc();
|
|
if (parseIdentifier(Name))
|
|
return TokError("unexpected token in '.cv_loc' directive");
|
|
if (Name == "prologue_end")
|
|
PrologueEnd = true;
|
|
else if (Name == "is_stmt") {
|
|
Loc = getTok().getLoc();
|
|
const MCExpr *Value;
|
|
if (parseExpression(Value))
|
|
return true;
|
|
// The expression must be the constant 0 or 1.
|
|
IsStmt = ~0ULL;
|
|
if (const auto *MCE = dyn_cast<MCConstantExpr>(Value))
|
|
IsStmt = MCE->getValue();
|
|
|
|
if (IsStmt > 1)
|
|
return Error(Loc, "is_stmt value not 0 or 1");
|
|
} else {
|
|
return Error(Loc, "unknown sub-directive in '.cv_loc' directive");
|
|
}
|
|
return false;
|
|
};
|
|
|
|
if (parseMany(parseOp, false /*hasComma*/))
|
|
return true;
|
|
|
|
getStreamer().emitCVLocDirective(FunctionId, FileNumber, LineNumber,
|
|
ColumnPos, PrologueEnd, IsStmt, StringRef(),
|
|
DirectiveLoc);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCVLinetable
|
|
/// ::= .cv_linetable FunctionId, FnStart, FnEnd
|
|
bool AsmParser::parseDirectiveCVLinetable() {
|
|
int64_t FunctionId;
|
|
StringRef FnStartName, FnEndName;
|
|
SMLoc Loc = getTok().getLoc();
|
|
if (parseCVFunctionId(FunctionId, ".cv_linetable") || parseComma() ||
|
|
parseTokenLoc(Loc) ||
|
|
check(parseIdentifier(FnStartName), Loc,
|
|
"expected identifier in directive") ||
|
|
parseComma() || parseTokenLoc(Loc) ||
|
|
check(parseIdentifier(FnEndName), Loc,
|
|
"expected identifier in directive"))
|
|
return true;
|
|
|
|
MCSymbol *FnStartSym = getContext().getOrCreateSymbol(FnStartName);
|
|
MCSymbol *FnEndSym = getContext().getOrCreateSymbol(FnEndName);
|
|
|
|
getStreamer().emitCVLinetableDirective(FunctionId, FnStartSym, FnEndSym);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCVInlineLinetable
|
|
/// ::= .cv_inline_linetable PrimaryFunctionId FileId LineNum FnStart FnEnd
|
|
bool AsmParser::parseDirectiveCVInlineLinetable() {
|
|
int64_t PrimaryFunctionId, SourceFileId, SourceLineNum;
|
|
StringRef FnStartName, FnEndName;
|
|
SMLoc Loc = getTok().getLoc();
|
|
if (parseCVFunctionId(PrimaryFunctionId, ".cv_inline_linetable") ||
|
|
parseTokenLoc(Loc) ||
|
|
parseIntToken(SourceFileId, "expected SourceField") ||
|
|
check(SourceFileId <= 0, Loc, "File id less than zero") ||
|
|
parseTokenLoc(Loc) ||
|
|
parseIntToken(SourceLineNum, "expected SourceLineNum") ||
|
|
check(SourceLineNum < 0, Loc, "Line number less than zero") ||
|
|
parseTokenLoc(Loc) ||
|
|
check(parseIdentifier(FnStartName), Loc, "expected identifier") ||
|
|
parseTokenLoc(Loc) ||
|
|
check(parseIdentifier(FnEndName), Loc, "expected identifier"))
|
|
return true;
|
|
|
|
if (parseEOL())
|
|
return true;
|
|
|
|
MCSymbol *FnStartSym = getContext().getOrCreateSymbol(FnStartName);
|
|
MCSymbol *FnEndSym = getContext().getOrCreateSymbol(FnEndName);
|
|
getStreamer().emitCVInlineLinetableDirective(PrimaryFunctionId, SourceFileId,
|
|
SourceLineNum, FnStartSym,
|
|
FnEndSym);
|
|
return false;
|
|
}
|
|
|
|
void AsmParser::initializeCVDefRangeTypeMap() {
|
|
CVDefRangeTypeMap["reg"] = CVDR_DEFRANGE_REGISTER;
|
|
CVDefRangeTypeMap["frame_ptr_rel"] = CVDR_DEFRANGE_FRAMEPOINTER_REL;
|
|
CVDefRangeTypeMap["subfield_reg"] = CVDR_DEFRANGE_SUBFIELD_REGISTER;
|
|
CVDefRangeTypeMap["reg_rel"] = CVDR_DEFRANGE_REGISTER_REL;
|
|
}
|
|
|
|
/// parseDirectiveCVDefRange
|
|
/// ::= .cv_def_range RangeStart RangeEnd (GapStart GapEnd)*, bytes*
|
|
bool AsmParser::parseDirectiveCVDefRange() {
|
|
SMLoc Loc;
|
|
std::vector<std::pair<const MCSymbol *, const MCSymbol *>> Ranges;
|
|
while (getLexer().is(AsmToken::Identifier)) {
|
|
Loc = getLexer().getLoc();
|
|
StringRef GapStartName;
|
|
if (parseIdentifier(GapStartName))
|
|
return Error(Loc, "expected identifier in directive");
|
|
MCSymbol *GapStartSym = getContext().getOrCreateSymbol(GapStartName);
|
|
|
|
Loc = getLexer().getLoc();
|
|
StringRef GapEndName;
|
|
if (parseIdentifier(GapEndName))
|
|
return Error(Loc, "expected identifier in directive");
|
|
MCSymbol *GapEndSym = getContext().getOrCreateSymbol(GapEndName);
|
|
|
|
Ranges.push_back({GapStartSym, GapEndSym});
|
|
}
|
|
|
|
StringRef CVDefRangeTypeStr;
|
|
if (parseToken(
|
|
AsmToken::Comma,
|
|
"expected comma before def_range type in .cv_def_range directive") ||
|
|
parseIdentifier(CVDefRangeTypeStr))
|
|
return Error(Loc, "expected def_range type in directive");
|
|
|
|
StringMap<CVDefRangeType>::const_iterator CVTypeIt =
|
|
CVDefRangeTypeMap.find(CVDefRangeTypeStr);
|
|
CVDefRangeType CVDRType = (CVTypeIt == CVDefRangeTypeMap.end())
|
|
? CVDR_DEFRANGE
|
|
: CVTypeIt->getValue();
|
|
switch (CVDRType) {
|
|
case CVDR_DEFRANGE_REGISTER: {
|
|
int64_t DRRegister;
|
|
if (parseToken(AsmToken::Comma, "expected comma before register number in "
|
|
".cv_def_range directive") ||
|
|
parseAbsoluteExpression(DRRegister))
|
|
return Error(Loc, "expected register number");
|
|
|
|
codeview::DefRangeRegisterHeader DRHdr;
|
|
DRHdr.Register = DRRegister;
|
|
DRHdr.MayHaveNoName = 0;
|
|
getStreamer().emitCVDefRangeDirective(Ranges, DRHdr);
|
|
break;
|
|
}
|
|
case CVDR_DEFRANGE_FRAMEPOINTER_REL: {
|
|
int64_t DROffset;
|
|
if (parseToken(AsmToken::Comma,
|
|
"expected comma before offset in .cv_def_range directive") ||
|
|
parseAbsoluteExpression(DROffset))
|
|
return Error(Loc, "expected offset value");
|
|
|
|
codeview::DefRangeFramePointerRelHeader DRHdr;
|
|
DRHdr.Offset = DROffset;
|
|
getStreamer().emitCVDefRangeDirective(Ranges, DRHdr);
|
|
break;
|
|
}
|
|
case CVDR_DEFRANGE_SUBFIELD_REGISTER: {
|
|
int64_t DRRegister;
|
|
int64_t DROffsetInParent;
|
|
if (parseToken(AsmToken::Comma, "expected comma before register number in "
|
|
".cv_def_range directive") ||
|
|
parseAbsoluteExpression(DRRegister))
|
|
return Error(Loc, "expected register number");
|
|
if (parseToken(AsmToken::Comma,
|
|
"expected comma before offset in .cv_def_range directive") ||
|
|
parseAbsoluteExpression(DROffsetInParent))
|
|
return Error(Loc, "expected offset value");
|
|
|
|
codeview::DefRangeSubfieldRegisterHeader DRHdr;
|
|
DRHdr.Register = DRRegister;
|
|
DRHdr.MayHaveNoName = 0;
|
|
DRHdr.OffsetInParent = DROffsetInParent;
|
|
getStreamer().emitCVDefRangeDirective(Ranges, DRHdr);
|
|
break;
|
|
}
|
|
case CVDR_DEFRANGE_REGISTER_REL: {
|
|
int64_t DRRegister;
|
|
int64_t DRFlags;
|
|
int64_t DRBasePointerOffset;
|
|
if (parseToken(AsmToken::Comma, "expected comma before register number in "
|
|
".cv_def_range directive") ||
|
|
parseAbsoluteExpression(DRRegister))
|
|
return Error(Loc, "expected register value");
|
|
if (parseToken(
|
|
AsmToken::Comma,
|
|
"expected comma before flag value in .cv_def_range directive") ||
|
|
parseAbsoluteExpression(DRFlags))
|
|
return Error(Loc, "expected flag value");
|
|
if (parseToken(AsmToken::Comma, "expected comma before base pointer offset "
|
|
"in .cv_def_range directive") ||
|
|
parseAbsoluteExpression(DRBasePointerOffset))
|
|
return Error(Loc, "expected base pointer offset value");
|
|
|
|
codeview::DefRangeRegisterRelHeader DRHdr;
|
|
DRHdr.Register = DRRegister;
|
|
DRHdr.Flags = DRFlags;
|
|
DRHdr.BasePointerOffset = DRBasePointerOffset;
|
|
getStreamer().emitCVDefRangeDirective(Ranges, DRHdr);
|
|
break;
|
|
}
|
|
default:
|
|
return Error(Loc, "unexpected def_range type in .cv_def_range directive");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// parseDirectiveCVString
|
|
/// ::= .cv_stringtable "string"
|
|
bool AsmParser::parseDirectiveCVString() {
|
|
std::string Data;
|
|
if (checkForValidSection() || parseEscapedString(Data))
|
|
return true;
|
|
|
|
// Put the string in the table and emit the offset.
|
|
std::pair<StringRef, unsigned> Insertion =
|
|
getCVContext().addToStringTable(Data);
|
|
getStreamer().emitInt32(Insertion.second);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCVStringTable
|
|
/// ::= .cv_stringtable
|
|
bool AsmParser::parseDirectiveCVStringTable() {
|
|
getStreamer().emitCVStringTableDirective();
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCVFileChecksums
|
|
/// ::= .cv_filechecksums
|
|
bool AsmParser::parseDirectiveCVFileChecksums() {
|
|
getStreamer().emitCVFileChecksumsDirective();
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCVFileChecksumOffset
|
|
/// ::= .cv_filechecksumoffset fileno
|
|
bool AsmParser::parseDirectiveCVFileChecksumOffset() {
|
|
int64_t FileNo;
|
|
if (parseIntToken(FileNo))
|
|
return true;
|
|
if (parseEOL())
|
|
return true;
|
|
getStreamer().emitCVFileChecksumOffsetDirective(FileNo);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCVFPOData
|
|
/// ::= .cv_fpo_data procsym
|
|
bool AsmParser::parseDirectiveCVFPOData() {
|
|
SMLoc DirLoc = getLexer().getLoc();
|
|
StringRef ProcName;
|
|
if (parseIdentifier(ProcName))
|
|
return TokError("expected symbol name");
|
|
if (parseEOL())
|
|
return true;
|
|
MCSymbol *ProcSym = getContext().getOrCreateSymbol(ProcName);
|
|
getStreamer().emitCVFPOData(ProcSym, DirLoc);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFISections
|
|
/// ::= .cfi_sections section [, section][, section]
|
|
bool AsmParser::parseDirectiveCFISections() {
|
|
StringRef Name;
|
|
bool EH = false;
|
|
bool Debug = false;
|
|
bool SFrame = false;
|
|
|
|
if (!parseOptionalToken(AsmToken::EndOfStatement)) {
|
|
for (;;) {
|
|
if (parseIdentifier(Name))
|
|
return TokError("expected .eh_frame, .debug_frame, or .sframe");
|
|
if (Name == ".eh_frame")
|
|
EH = true;
|
|
else if (Name == ".debug_frame")
|
|
Debug = true;
|
|
else if (Name == ".sframe")
|
|
SFrame = true;
|
|
if (parseOptionalToken(AsmToken::EndOfStatement))
|
|
break;
|
|
if (parseComma())
|
|
return true;
|
|
}
|
|
}
|
|
getStreamer().emitCFISections(EH, Debug, SFrame);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIStartProc
|
|
/// ::= .cfi_startproc [simple]
|
|
bool AsmParser::parseDirectiveCFIStartProc() {
|
|
CFIStartProcLoc = StartTokLoc;
|
|
|
|
StringRef Simple;
|
|
if (!parseOptionalToken(AsmToken::EndOfStatement)) {
|
|
if (check(parseIdentifier(Simple) || Simple != "simple",
|
|
"unexpected token") ||
|
|
parseEOL())
|
|
return true;
|
|
}
|
|
|
|
// TODO(kristina): Deal with a corner case of incorrect diagnostic context
|
|
// being produced if this directive is emitted as part of preprocessor macro
|
|
// expansion which can *ONLY* happen if Clang's cc1as is the API consumer.
|
|
// Tools like llvm-mc on the other hand are not affected by it, and report
|
|
// correct context information.
|
|
getStreamer().emitCFIStartProc(!Simple.empty(), Lexer.getLoc());
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIEndProc
|
|
/// ::= .cfi_endproc
|
|
bool AsmParser::parseDirectiveCFIEndProc() {
|
|
CFIStartProcLoc = std::nullopt;
|
|
|
|
if (parseEOL())
|
|
return true;
|
|
|
|
getStreamer().emitCFIEndProc();
|
|
return false;
|
|
}
|
|
|
|
/// parse register name or number.
|
|
bool AsmParser::parseRegisterOrRegisterNumber(int64_t &Register,
|
|
SMLoc DirectiveLoc) {
|
|
MCRegister RegNo;
|
|
|
|
if (getLexer().isNot(AsmToken::Integer)) {
|
|
if (getTargetParser().parseRegister(RegNo, DirectiveLoc, DirectiveLoc))
|
|
return true;
|
|
Register = getContext().getRegisterInfo()->getDwarfRegNum(RegNo, true);
|
|
} else
|
|
return parseAbsoluteExpression(Register);
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIDefCfa
|
|
/// ::= .cfi_def_cfa register, offset
|
|
bool AsmParser::parseDirectiveCFIDefCfa(SMLoc DirectiveLoc) {
|
|
int64_t Register = 0, Offset = 0;
|
|
if (parseRegisterOrRegisterNumber(Register, DirectiveLoc) || parseComma() ||
|
|
parseAbsoluteExpression(Offset) || parseEOL())
|
|
return true;
|
|
|
|
getStreamer().emitCFIDefCfa(Register, Offset, DirectiveLoc);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIDefCfaOffset
|
|
/// ::= .cfi_def_cfa_offset offset
|
|
bool AsmParser::parseDirectiveCFIDefCfaOffset(SMLoc DirectiveLoc) {
|
|
int64_t Offset = 0;
|
|
if (parseAbsoluteExpression(Offset) || parseEOL())
|
|
return true;
|
|
|
|
getStreamer().emitCFIDefCfaOffset(Offset, DirectiveLoc);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIRegister
|
|
/// ::= .cfi_register register, register
|
|
bool AsmParser::parseDirectiveCFIRegister(SMLoc DirectiveLoc) {
|
|
int64_t Register1 = 0, Register2 = 0;
|
|
if (parseRegisterOrRegisterNumber(Register1, DirectiveLoc) || parseComma() ||
|
|
parseRegisterOrRegisterNumber(Register2, DirectiveLoc) || parseEOL())
|
|
return true;
|
|
|
|
getStreamer().emitCFIRegister(Register1, Register2, DirectiveLoc);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIWindowSave
|
|
/// ::= .cfi_window_save
|
|
bool AsmParser::parseDirectiveCFIWindowSave(SMLoc DirectiveLoc) {
|
|
if (parseEOL())
|
|
return true;
|
|
getStreamer().emitCFIWindowSave(DirectiveLoc);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIAdjustCfaOffset
|
|
/// ::= .cfi_adjust_cfa_offset adjustment
|
|
bool AsmParser::parseDirectiveCFIAdjustCfaOffset(SMLoc DirectiveLoc) {
|
|
int64_t Adjustment = 0;
|
|
if (parseAbsoluteExpression(Adjustment) || parseEOL())
|
|
return true;
|
|
|
|
getStreamer().emitCFIAdjustCfaOffset(Adjustment, DirectiveLoc);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIDefCfaRegister
|
|
/// ::= .cfi_def_cfa_register register
|
|
bool AsmParser::parseDirectiveCFIDefCfaRegister(SMLoc DirectiveLoc) {
|
|
int64_t Register = 0;
|
|
if (parseRegisterOrRegisterNumber(Register, DirectiveLoc) || parseEOL())
|
|
return true;
|
|
|
|
getStreamer().emitCFIDefCfaRegister(Register, DirectiveLoc);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFILLVMDefAspaceCfa
|
|
/// ::= .cfi_llvm_def_aspace_cfa register, offset, address_space
|
|
bool AsmParser::parseDirectiveCFILLVMDefAspaceCfa(SMLoc DirectiveLoc) {
|
|
int64_t Register = 0, Offset = 0, AddressSpace = 0;
|
|
if (parseRegisterOrRegisterNumber(Register, DirectiveLoc) || parseComma() ||
|
|
parseAbsoluteExpression(Offset) || parseComma() ||
|
|
parseAbsoluteExpression(AddressSpace) || parseEOL())
|
|
return true;
|
|
|
|
getStreamer().emitCFILLVMDefAspaceCfa(Register, Offset, AddressSpace,
|
|
DirectiveLoc);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIOffset
|
|
/// ::= .cfi_offset register, offset
|
|
bool AsmParser::parseDirectiveCFIOffset(SMLoc DirectiveLoc) {
|
|
int64_t Register = 0;
|
|
int64_t Offset = 0;
|
|
|
|
if (parseRegisterOrRegisterNumber(Register, DirectiveLoc) || parseComma() ||
|
|
parseAbsoluteExpression(Offset) || parseEOL())
|
|
return true;
|
|
|
|
getStreamer().emitCFIOffset(Register, Offset, DirectiveLoc);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIRelOffset
|
|
/// ::= .cfi_rel_offset register, offset
|
|
bool AsmParser::parseDirectiveCFIRelOffset(SMLoc DirectiveLoc) {
|
|
int64_t Register = 0, Offset = 0;
|
|
|
|
if (parseRegisterOrRegisterNumber(Register, DirectiveLoc) || parseComma() ||
|
|
parseAbsoluteExpression(Offset) || parseEOL())
|
|
return true;
|
|
|
|
getStreamer().emitCFIRelOffset(Register, Offset, DirectiveLoc);
|
|
return false;
|
|
}
|
|
|
|
static bool isValidEncoding(int64_t Encoding) {
|
|
if (Encoding & ~0xff)
|
|
return false;
|
|
|
|
if (Encoding == dwarf::DW_EH_PE_omit)
|
|
return true;
|
|
|
|
const unsigned Format = Encoding & 0xf;
|
|
if (Format != dwarf::DW_EH_PE_absptr && Format != dwarf::DW_EH_PE_udata2 &&
|
|
Format != dwarf::DW_EH_PE_udata4 && Format != dwarf::DW_EH_PE_udata8 &&
|
|
Format != dwarf::DW_EH_PE_sdata2 && Format != dwarf::DW_EH_PE_sdata4 &&
|
|
Format != dwarf::DW_EH_PE_sdata8 && Format != dwarf::DW_EH_PE_signed)
|
|
return false;
|
|
|
|
const unsigned Application = Encoding & 0x70;
|
|
if (Application != dwarf::DW_EH_PE_absptr &&
|
|
Application != dwarf::DW_EH_PE_pcrel)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/// parseDirectiveCFIPersonalityOrLsda
|
|
/// IsPersonality true for cfi_personality, false for cfi_lsda
|
|
/// ::= .cfi_personality encoding, [symbol_name]
|
|
/// ::= .cfi_lsda encoding, [symbol_name]
|
|
bool AsmParser::parseDirectiveCFIPersonalityOrLsda(bool IsPersonality) {
|
|
int64_t Encoding = 0;
|
|
if (parseAbsoluteExpression(Encoding))
|
|
return true;
|
|
if (Encoding == dwarf::DW_EH_PE_omit)
|
|
return false;
|
|
|
|
StringRef Name;
|
|
if (check(!isValidEncoding(Encoding), "unsupported encoding.") ||
|
|
parseComma() ||
|
|
check(parseIdentifier(Name), "expected identifier in directive") ||
|
|
parseEOL())
|
|
return true;
|
|
|
|
MCSymbol *Sym = getContext().getOrCreateSymbol(Name);
|
|
|
|
if (IsPersonality)
|
|
getStreamer().emitCFIPersonality(Sym, Encoding);
|
|
else
|
|
getStreamer().emitCFILsda(Sym, Encoding);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIRememberState
|
|
/// ::= .cfi_remember_state
|
|
bool AsmParser::parseDirectiveCFIRememberState(SMLoc DirectiveLoc) {
|
|
if (parseEOL())
|
|
return true;
|
|
getStreamer().emitCFIRememberState(DirectiveLoc);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIRestoreState
|
|
/// ::= .cfi_remember_state
|
|
bool AsmParser::parseDirectiveCFIRestoreState(SMLoc DirectiveLoc) {
|
|
if (parseEOL())
|
|
return true;
|
|
getStreamer().emitCFIRestoreState(DirectiveLoc);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFISameValue
|
|
/// ::= .cfi_same_value register
|
|
bool AsmParser::parseDirectiveCFISameValue(SMLoc DirectiveLoc) {
|
|
int64_t Register = 0;
|
|
|
|
if (parseRegisterOrRegisterNumber(Register, DirectiveLoc) || parseEOL())
|
|
return true;
|
|
|
|
getStreamer().emitCFISameValue(Register, DirectiveLoc);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIRestore
|
|
/// ::= .cfi_restore register
|
|
bool AsmParser::parseDirectiveCFIRestore(SMLoc DirectiveLoc) {
|
|
int64_t Register = 0;
|
|
if (parseRegisterOrRegisterNumber(Register, DirectiveLoc) || parseEOL())
|
|
return true;
|
|
|
|
getStreamer().emitCFIRestore(Register, DirectiveLoc);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIEscape
|
|
/// ::= .cfi_escape expression[,...]
|
|
bool AsmParser::parseDirectiveCFIEscape(SMLoc DirectiveLoc) {
|
|
std::string Values;
|
|
int64_t CurrValue;
|
|
if (parseAbsoluteExpression(CurrValue))
|
|
return true;
|
|
|
|
Values.push_back((uint8_t)CurrValue);
|
|
|
|
while (getLexer().is(AsmToken::Comma)) {
|
|
Lex();
|
|
|
|
if (parseAbsoluteExpression(CurrValue))
|
|
return true;
|
|
|
|
Values.push_back((uint8_t)CurrValue);
|
|
}
|
|
|
|
getStreamer().emitCFIEscape(Values, DirectiveLoc);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIReturnColumn
|
|
/// ::= .cfi_return_column register
|
|
bool AsmParser::parseDirectiveCFIReturnColumn(SMLoc DirectiveLoc) {
|
|
int64_t Register = 0;
|
|
if (parseRegisterOrRegisterNumber(Register, DirectiveLoc) || parseEOL())
|
|
return true;
|
|
getStreamer().emitCFIReturnColumn(Register);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFISignalFrame
|
|
/// ::= .cfi_signal_frame
|
|
bool AsmParser::parseDirectiveCFISignalFrame(SMLoc DirectiveLoc) {
|
|
if (parseEOL())
|
|
return true;
|
|
|
|
getStreamer().emitCFISignalFrame();
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIUndefined
|
|
/// ::= .cfi_undefined register
|
|
bool AsmParser::parseDirectiveCFIUndefined(SMLoc DirectiveLoc) {
|
|
int64_t Register = 0;
|
|
|
|
if (parseRegisterOrRegisterNumber(Register, DirectiveLoc) || parseEOL())
|
|
return true;
|
|
|
|
getStreamer().emitCFIUndefined(Register, DirectiveLoc);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFILabel
|
|
/// ::= .cfi_label label
|
|
bool AsmParser::parseDirectiveCFILabel(SMLoc Loc) {
|
|
StringRef Name;
|
|
Loc = Lexer.getLoc();
|
|
if (parseIdentifier(Name))
|
|
return TokError("expected identifier");
|
|
if (parseEOL())
|
|
return true;
|
|
getStreamer().emitCFILabelDirective(Loc, Name);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIValOffset
|
|
/// ::= .cfi_val_offset register, offset
|
|
bool AsmParser::parseDirectiveCFIValOffset(SMLoc DirectiveLoc) {
|
|
int64_t Register = 0;
|
|
int64_t Offset = 0;
|
|
|
|
if (parseRegisterOrRegisterNumber(Register, DirectiveLoc) || parseComma() ||
|
|
parseAbsoluteExpression(Offset) || parseEOL())
|
|
return true;
|
|
|
|
getStreamer().emitCFIValOffset(Register, Offset, DirectiveLoc);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveAltmacro
|
|
/// ::= .altmacro
|
|
/// ::= .noaltmacro
|
|
bool AsmParser::parseDirectiveAltmacro(StringRef Directive) {
|
|
if (parseEOL())
|
|
return true;
|
|
AltMacroMode = (Directive == ".altmacro");
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveMacrosOnOff
|
|
/// ::= .macros_on
|
|
/// ::= .macros_off
|
|
bool AsmParser::parseDirectiveMacrosOnOff(StringRef Directive) {
|
|
if (parseEOL())
|
|
return true;
|
|
setMacrosEnabled(Directive == ".macros_on");
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveMacro
|
|
/// ::= .macro name[,] [parameters]
|
|
bool AsmParser::parseDirectiveMacro(SMLoc DirectiveLoc) {
|
|
StringRef Name;
|
|
if (parseIdentifier(Name))
|
|
return TokError("expected identifier in '.macro' directive");
|
|
|
|
if (getLexer().is(AsmToken::Comma))
|
|
Lex();
|
|
|
|
MCAsmMacroParameters Parameters;
|
|
while (getLexer().isNot(AsmToken::EndOfStatement)) {
|
|
|
|
if (!Parameters.empty() && Parameters.back().Vararg)
|
|
return Error(Lexer.getLoc(), "vararg parameter '" +
|
|
Parameters.back().Name +
|
|
"' should be the last parameter");
|
|
|
|
MCAsmMacroParameter Parameter;
|
|
if (parseIdentifier(Parameter.Name))
|
|
return TokError("expected identifier in '.macro' directive");
|
|
|
|
// Emit an error if two (or more) named parameters share the same name
|
|
for (const MCAsmMacroParameter& CurrParam : Parameters)
|
|
if (CurrParam.Name == Parameter.Name)
|
|
return TokError("macro '" + Name + "' has multiple parameters"
|
|
" named '" + Parameter.Name + "'");
|
|
|
|
if (Lexer.is(AsmToken::Colon)) {
|
|
Lex(); // consume ':'
|
|
|
|
SMLoc QualLoc;
|
|
StringRef Qualifier;
|
|
|
|
QualLoc = Lexer.getLoc();
|
|
if (parseIdentifier(Qualifier))
|
|
return Error(QualLoc, "missing parameter qualifier for "
|
|
"'" + Parameter.Name + "' in macro '" + Name + "'");
|
|
|
|
if (Qualifier == "req")
|
|
Parameter.Required = true;
|
|
else if (Qualifier == "vararg")
|
|
Parameter.Vararg = true;
|
|
else
|
|
return Error(QualLoc, Qualifier + " is not a valid parameter qualifier "
|
|
"for '" + Parameter.Name + "' in macro '" + Name + "'");
|
|
}
|
|
|
|
if (getLexer().is(AsmToken::Equal)) {
|
|
Lex();
|
|
|
|
SMLoc ParamLoc;
|
|
|
|
ParamLoc = Lexer.getLoc();
|
|
if (parseMacroArgument(Parameter.Value, /*Vararg=*/false ))
|
|
return true;
|
|
|
|
if (Parameter.Required)
|
|
Warning(ParamLoc, "pointless default value for required parameter "
|
|
"'" + Parameter.Name + "' in macro '" + Name + "'");
|
|
}
|
|
|
|
Parameters.push_back(std::move(Parameter));
|
|
|
|
if (getLexer().is(AsmToken::Comma))
|
|
Lex();
|
|
}
|
|
|
|
// Eat just the end of statement.
|
|
Lexer.Lex();
|
|
|
|
// Consuming deferred text, so use Lexer.Lex to ignore Lexing Errors
|
|
AsmToken EndToken, StartToken = getTok();
|
|
unsigned MacroDepth = 0;
|
|
// Lex the macro definition.
|
|
while (true) {
|
|
// Ignore Lexing errors in macros.
|
|
while (Lexer.is(AsmToken::Error)) {
|
|
Lexer.Lex();
|
|
}
|
|
|
|
// Check whether we have reached the end of the file.
|
|
if (getLexer().is(AsmToken::Eof))
|
|
return Error(DirectiveLoc, "no matching '.endmacro' in definition");
|
|
|
|
// Otherwise, check whether we have reach the .endmacro or the start of a
|
|
// preprocessor line marker.
|
|
if (getLexer().is(AsmToken::Identifier)) {
|
|
if (getTok().getIdentifier() == ".endm" ||
|
|
getTok().getIdentifier() == ".endmacro") {
|
|
if (MacroDepth == 0) { // Outermost macro.
|
|
EndToken = getTok();
|
|
Lexer.Lex();
|
|
if (getLexer().isNot(AsmToken::EndOfStatement))
|
|
return TokError("unexpected token in '" + EndToken.getIdentifier() +
|
|
"' directive");
|
|
break;
|
|
} else {
|
|
// Otherwise we just found the end of an inner macro.
|
|
--MacroDepth;
|
|
}
|
|
} else if (getTok().getIdentifier() == ".macro") {
|
|
// We allow nested macros. Those aren't instantiated until the outermost
|
|
// macro is expanded so just ignore them for now.
|
|
++MacroDepth;
|
|
}
|
|
} else if (Lexer.is(AsmToken::HashDirective)) {
|
|
(void)parseCppHashLineFilenameComment(getLexer().getLoc());
|
|
}
|
|
|
|
// Otherwise, scan til the end of the statement.
|
|
eatToEndOfStatement();
|
|
}
|
|
|
|
if (getContext().lookupMacro(Name)) {
|
|
return Error(DirectiveLoc, "macro '" + Name + "' is already defined");
|
|
}
|
|
|
|
const char *BodyStart = StartToken.getLoc().getPointer();
|
|
const char *BodyEnd = EndToken.getLoc().getPointer();
|
|
StringRef Body = StringRef(BodyStart, BodyEnd - BodyStart);
|
|
checkForBadMacro(DirectiveLoc, Name, Body, Parameters);
|
|
MCAsmMacro Macro(Name, Body, std::move(Parameters));
|
|
DEBUG_WITH_TYPE("asm-macros", dbgs() << "Defining new macro:\n";
|
|
Macro.dump());
|
|
getContext().defineMacro(Name, std::move(Macro));
|
|
return false;
|
|
}
|
|
|
|
/// checkForBadMacro
|
|
///
|
|
/// With the support added for named parameters there may be code out there that
|
|
/// is transitioning from positional parameters. In versions of gas that did
|
|
/// not support named parameters they would be ignored on the macro definition.
|
|
/// But to support both styles of parameters this is not possible so if a macro
|
|
/// definition has named parameters but does not use them and has what appears
|
|
/// to be positional parameters, strings like $1, $2, ... and $n, then issue a
|
|
/// warning that the positional parameter found in body which have no effect.
|
|
/// Hoping the developer will either remove the named parameters from the macro
|
|
/// definition so the positional parameters get used if that was what was
|
|
/// intended or change the macro to use the named parameters. It is possible
|
|
/// this warning will trigger when the none of the named parameters are used
|
|
/// and the strings like $1 are infact to simply to be passed trough unchanged.
|
|
void AsmParser::checkForBadMacro(SMLoc DirectiveLoc, StringRef Name,
|
|
StringRef Body,
|
|
ArrayRef<MCAsmMacroParameter> Parameters) {
|
|
// If this macro is not defined with named parameters the warning we are
|
|
// checking for here doesn't apply.
|
|
unsigned NParameters = Parameters.size();
|
|
if (NParameters == 0)
|
|
return;
|
|
|
|
bool NamedParametersFound = false;
|
|
bool PositionalParametersFound = false;
|
|
|
|
// Look at the body of the macro for use of both the named parameters and what
|
|
// are likely to be positional parameters. This is what expandMacro() is
|
|
// doing when it finds the parameters in the body.
|
|
while (!Body.empty()) {
|
|
// Scan for the next possible parameter.
|
|
std::size_t End = Body.size(), Pos = 0;
|
|
for (; Pos != End; ++Pos) {
|
|
// Check for a substitution or escape.
|
|
// This macro is defined with parameters, look for \foo, \bar, etc.
|
|
if (Body[Pos] == '\\' && Pos + 1 != End)
|
|
break;
|
|
|
|
// This macro should have parameters, but look for $0, $1, ..., $n too.
|
|
if (Body[Pos] != '$' || Pos + 1 == End)
|
|
continue;
|
|
char Next = Body[Pos + 1];
|
|
if (Next == '$' || Next == 'n' ||
|
|
isdigit(static_cast<unsigned char>(Next)))
|
|
break;
|
|
}
|
|
|
|
// Check if we reached the end.
|
|
if (Pos == End)
|
|
break;
|
|
|
|
if (Body[Pos] == '$') {
|
|
switch (Body[Pos + 1]) {
|
|
// $$ => $
|
|
case '$':
|
|
break;
|
|
|
|
// $n => number of arguments
|
|
case 'n':
|
|
PositionalParametersFound = true;
|
|
break;
|
|
|
|
// $[0-9] => argument
|
|
default: {
|
|
PositionalParametersFound = true;
|
|
break;
|
|
}
|
|
}
|
|
Pos += 2;
|
|
} else {
|
|
unsigned I = Pos + 1;
|
|
while (isIdentifierChar(Body[I]) && I + 1 != End)
|
|
++I;
|
|
|
|
const char *Begin = Body.data() + Pos + 1;
|
|
StringRef Argument(Begin, I - (Pos + 1));
|
|
unsigned Index = 0;
|
|
for (; Index < NParameters; ++Index)
|
|
if (Parameters[Index].Name == Argument)
|
|
break;
|
|
|
|
if (Index == NParameters) {
|
|
if (Body[Pos + 1] == '(' && Body[Pos + 2] == ')')
|
|
Pos += 3;
|
|
else {
|
|
Pos = I;
|
|
}
|
|
} else {
|
|
NamedParametersFound = true;
|
|
Pos += 1 + Argument.size();
|
|
}
|
|
}
|
|
// Update the scan point.
|
|
Body = Body.substr(Pos);
|
|
}
|
|
|
|
if (!NamedParametersFound && PositionalParametersFound)
|
|
Warning(DirectiveLoc, "macro defined with named parameters which are not "
|
|
"used in macro body, possible positional parameter "
|
|
"found in body which will have no effect");
|
|
}
|
|
|
|
/// parseDirectiveExitMacro
|
|
/// ::= .exitm
|
|
bool AsmParser::parseDirectiveExitMacro(StringRef Directive) {
|
|
if (parseEOL())
|
|
return true;
|
|
|
|
if (!isInsideMacroInstantiation())
|
|
return TokError("unexpected '" + Directive + "' in file, "
|
|
"no current macro definition");
|
|
|
|
// Exit all conditionals that are active in the current macro.
|
|
while (TheCondStack.size() != ActiveMacros.back()->CondStackDepth) {
|
|
TheCondState = TheCondStack.back();
|
|
TheCondStack.pop_back();
|
|
}
|
|
|
|
handleMacroExit();
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveEndMacro
|
|
/// ::= .endm
|
|
/// ::= .endmacro
|
|
bool AsmParser::parseDirectiveEndMacro(StringRef Directive) {
|
|
if (getLexer().isNot(AsmToken::EndOfStatement))
|
|
return TokError("unexpected token in '" + Directive + "' directive");
|
|
|
|
// If we are inside a macro instantiation, terminate the current
|
|
// instantiation.
|
|
if (isInsideMacroInstantiation()) {
|
|
handleMacroExit();
|
|
return false;
|
|
}
|
|
|
|
// Otherwise, this .endmacro is a stray entry in the file; well formed
|
|
// .endmacro directives are handled during the macro definition parsing.
|
|
return TokError("unexpected '" + Directive + "' in file, "
|
|
"no current macro definition");
|
|
}
|
|
|
|
/// parseDirectivePurgeMacro
|
|
/// ::= .purgem name
|
|
bool AsmParser::parseDirectivePurgeMacro(SMLoc DirectiveLoc) {
|
|
StringRef Name;
|
|
SMLoc Loc;
|
|
if (parseTokenLoc(Loc) ||
|
|
check(parseIdentifier(Name), Loc,
|
|
"expected identifier in '.purgem' directive") ||
|
|
parseEOL())
|
|
return true;
|
|
|
|
if (!getContext().lookupMacro(Name))
|
|
return Error(DirectiveLoc, "macro '" + Name + "' is not defined");
|
|
|
|
getContext().undefineMacro(Name);
|
|
DEBUG_WITH_TYPE("asm-macros", dbgs()
|
|
<< "Un-defining macro: " << Name << "\n");
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveSpace
|
|
/// ::= (.skip | .space) expression [ , expression ]
|
|
bool AsmParser::parseDirectiveSpace(StringRef IDVal) {
|
|
SMLoc NumBytesLoc = Lexer.getLoc();
|
|
const MCExpr *NumBytes;
|
|
if (checkForValidSection() || parseExpression(NumBytes))
|
|
return true;
|
|
|
|
int64_t FillExpr = 0;
|
|
if (parseOptionalToken(AsmToken::Comma))
|
|
if (parseAbsoluteExpression(FillExpr))
|
|
return true;
|
|
if (parseEOL())
|
|
return true;
|
|
|
|
// FIXME: Sometimes the fill expr is 'nop' if it isn't supplied, instead of 0.
|
|
getStreamer().emitFill(*NumBytes, FillExpr, NumBytesLoc);
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveDCB
|
|
/// ::= .dcb.{b, l, w} expression, expression
|
|
bool AsmParser::parseDirectiveDCB(StringRef IDVal, unsigned Size) {
|
|
SMLoc NumValuesLoc = Lexer.getLoc();
|
|
int64_t NumValues;
|
|
if (checkForValidSection() || parseAbsoluteExpression(NumValues))
|
|
return true;
|
|
|
|
if (NumValues < 0) {
|
|
Warning(NumValuesLoc, "'" + Twine(IDVal) + "' directive with negative repeat count has no effect");
|
|
return false;
|
|
}
|
|
|
|
if (parseComma())
|
|
return true;
|
|
|
|
const MCExpr *Value;
|
|
SMLoc ExprLoc = getLexer().getLoc();
|
|
if (parseExpression(Value))
|
|
return true;
|
|
|
|
// Special case constant expressions to match code generator.
|
|
if (const MCConstantExpr *MCE = dyn_cast<MCConstantExpr>(Value)) {
|
|
assert(Size <= 8 && "Invalid size");
|
|
uint64_t IntValue = MCE->getValue();
|
|
if (!isUIntN(8 * Size, IntValue) && !isIntN(8 * Size, IntValue))
|
|
return Error(ExprLoc, "literal value out of range for directive");
|
|
for (uint64_t i = 0, e = NumValues; i != e; ++i)
|
|
getStreamer().emitIntValue(IntValue, Size);
|
|
} else {
|
|
for (uint64_t i = 0, e = NumValues; i != e; ++i)
|
|
getStreamer().emitValue(Value, Size, ExprLoc);
|
|
}
|
|
|
|
return parseEOL();
|
|
}
|
|
|
|
/// parseDirectiveRealDCB
|
|
/// ::= .dcb.{d, s} expression, expression
|
|
bool AsmParser::parseDirectiveRealDCB(StringRef IDVal, const fltSemantics &Semantics) {
|
|
SMLoc NumValuesLoc = Lexer.getLoc();
|
|
int64_t NumValues;
|
|
if (checkForValidSection() || parseAbsoluteExpression(NumValues))
|
|
return true;
|
|
|
|
if (NumValues < 0) {
|
|
Warning(NumValuesLoc, "'" + Twine(IDVal) + "' directive with negative repeat count has no effect");
|
|
return false;
|
|
}
|
|
|
|
if (parseComma())
|
|
return true;
|
|
|
|
APInt AsInt;
|
|
if (parseRealValue(Semantics, AsInt) || parseEOL())
|
|
return true;
|
|
|
|
for (uint64_t i = 0, e = NumValues; i != e; ++i)
|
|
getStreamer().emitIntValue(AsInt.getLimitedValue(),
|
|
AsInt.getBitWidth() / 8);
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveDS
|
|
/// ::= .ds.{b, d, l, p, s, w, x} expression
|
|
bool AsmParser::parseDirectiveDS(StringRef IDVal, unsigned Size) {
|
|
SMLoc NumValuesLoc = Lexer.getLoc();
|
|
int64_t NumValues;
|
|
if (checkForValidSection() || parseAbsoluteExpression(NumValues) ||
|
|
parseEOL())
|
|
return true;
|
|
|
|
if (NumValues < 0) {
|
|
Warning(NumValuesLoc, "'" + Twine(IDVal) + "' directive with negative repeat count has no effect");
|
|
return false;
|
|
}
|
|
|
|
for (uint64_t i = 0, e = NumValues; i != e; ++i)
|
|
getStreamer().emitFill(Size, 0);
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveLEB128
|
|
/// ::= (.sleb128 | .uleb128) [ expression (, expression)* ]
|
|
bool AsmParser::parseDirectiveLEB128(bool Signed) {
|
|
if (checkForValidSection())
|
|
return true;
|
|
|
|
auto parseOp = [&]() -> bool {
|
|
const MCExpr *Value;
|
|
if (parseExpression(Value))
|
|
return true;
|
|
if (Signed)
|
|
getStreamer().emitSLEB128Value(Value);
|
|
else
|
|
getStreamer().emitULEB128Value(Value);
|
|
return false;
|
|
};
|
|
|
|
return parseMany(parseOp);
|
|
}
|
|
|
|
/// parseDirectiveSymbolAttribute
|
|
/// ::= { ".globl", ".weak", ... } [ identifier ( , identifier )* ]
|
|
bool AsmParser::parseDirectiveSymbolAttribute(MCSymbolAttr Attr) {
|
|
auto parseOp = [&]() -> bool {
|
|
StringRef Name;
|
|
SMLoc Loc = getTok().getLoc();
|
|
if (parseIdentifier(Name))
|
|
return Error(Loc, "expected identifier");
|
|
|
|
if (discardLTOSymbol(Name))
|
|
return false;
|
|
|
|
MCSymbol *Sym = getContext().getOrCreateSymbol(Name);
|
|
|
|
// Assembler local symbols don't make any sense here, except for directives
|
|
// that the symbol should be tagged.
|
|
if (Sym->isTemporary() && Attr != MCSA_Memtag)
|
|
return Error(Loc, "non-local symbol required");
|
|
|
|
if (!getStreamer().emitSymbolAttribute(Sym, Attr))
|
|
return Error(Loc, "unable to emit symbol attribute");
|
|
return false;
|
|
};
|
|
|
|
return parseMany(parseOp);
|
|
}
|
|
|
|
/// parseDirectiveComm
|
|
/// ::= ( .comm | .lcomm ) identifier , size_expression [ , align_expression ]
|
|
bool AsmParser::parseDirectiveComm(bool IsLocal) {
|
|
if (checkForValidSection())
|
|
return true;
|
|
|
|
SMLoc IDLoc = getLexer().getLoc();
|
|
StringRef Name;
|
|
if (parseIdentifier(Name))
|
|
return TokError("expected identifier in directive");
|
|
|
|
// Handle the identifier as the key symbol.
|
|
MCSymbol *Sym = getContext().getOrCreateSymbol(Name);
|
|
|
|
if (parseComma())
|
|
return true;
|
|
|
|
int64_t Size;
|
|
SMLoc SizeLoc = getLexer().getLoc();
|
|
if (parseAbsoluteExpression(Size))
|
|
return true;
|
|
|
|
int64_t Pow2Alignment = 0;
|
|
SMLoc Pow2AlignmentLoc;
|
|
if (getLexer().is(AsmToken::Comma)) {
|
|
Lex();
|
|
Pow2AlignmentLoc = getLexer().getLoc();
|
|
if (parseAbsoluteExpression(Pow2Alignment))
|
|
return true;
|
|
|
|
LCOMM::LCOMMType LCOMM = Lexer.getMAI().getLCOMMDirectiveAlignmentType();
|
|
if (IsLocal && LCOMM == LCOMM::NoAlignment)
|
|
return Error(Pow2AlignmentLoc, "alignment not supported on this target");
|
|
|
|
// If this target takes alignments in bytes (not log) validate and convert.
|
|
if ((!IsLocal && Lexer.getMAI().getCOMMDirectiveAlignmentIsInBytes()) ||
|
|
(IsLocal && LCOMM == LCOMM::ByteAlignment)) {
|
|
if (!isPowerOf2_64(Pow2Alignment))
|
|
return Error(Pow2AlignmentLoc, "alignment must be a power of 2");
|
|
Pow2Alignment = Log2_64(Pow2Alignment);
|
|
}
|
|
}
|
|
|
|
if (parseEOL())
|
|
return true;
|
|
|
|
// NOTE: a size of zero for a .comm should create a undefined symbol
|
|
// but a size of .lcomm creates a bss symbol of size zero.
|
|
if (Size < 0)
|
|
return Error(SizeLoc, "size must be non-negative");
|
|
|
|
Sym->redefineIfPossible();
|
|
if (!Sym->isUndefined())
|
|
return Error(IDLoc, "invalid symbol redefinition");
|
|
|
|
// Create the Symbol as a common or local common with Size and Pow2Alignment
|
|
if (IsLocal) {
|
|
getStreamer().emitLocalCommonSymbol(Sym, Size,
|
|
Align(1ULL << Pow2Alignment));
|
|
return false;
|
|
}
|
|
|
|
getStreamer().emitCommonSymbol(Sym, Size, Align(1ULL << Pow2Alignment));
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveAbort
|
|
/// ::= .abort [... message ...]
|
|
bool AsmParser::parseDirectiveAbort(SMLoc DirectiveLoc) {
|
|
StringRef Str = parseStringToEndOfStatement();
|
|
if (parseEOL())
|
|
return true;
|
|
|
|
if (Str.empty())
|
|
return Error(DirectiveLoc, ".abort detected. Assembly stopping");
|
|
|
|
// FIXME: Actually abort assembly here.
|
|
return Error(DirectiveLoc,
|
|
".abort '" + Str + "' detected. Assembly stopping");
|
|
}
|
|
|
|
/// parseDirectiveInclude
|
|
/// ::= .include "filename"
|
|
bool AsmParser::parseDirectiveInclude() {
|
|
// Allow the strings to have escaped octal character sequence.
|
|
std::string Filename;
|
|
SMLoc IncludeLoc = getTok().getLoc();
|
|
|
|
if (check(getTok().isNot(AsmToken::String),
|
|
"expected string in '.include' directive") ||
|
|
parseEscapedString(Filename) ||
|
|
check(getTok().isNot(AsmToken::EndOfStatement),
|
|
"unexpected token in '.include' directive") ||
|
|
// Attempt to switch the lexer to the included file before consuming the
|
|
// end of statement to avoid losing it when we switch.
|
|
check(enterIncludeFile(Filename), IncludeLoc,
|
|
"Could not find include file '" + Filename + "'"))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveIncbin
|
|
/// ::= .incbin "filename" [ , skip [ , count ] ]
|
|
bool AsmParser::parseDirectiveIncbin() {
|
|
// Allow the strings to have escaped octal character sequence.
|
|
std::string Filename;
|
|
SMLoc IncbinLoc = getTok().getLoc();
|
|
if (check(getTok().isNot(AsmToken::String),
|
|
"expected string in '.incbin' directive") ||
|
|
parseEscapedString(Filename))
|
|
return true;
|
|
|
|
int64_t Skip = 0;
|
|
const MCExpr *Count = nullptr;
|
|
SMLoc SkipLoc, CountLoc;
|
|
if (parseOptionalToken(AsmToken::Comma)) {
|
|
// The skip expression can be omitted while specifying the count, e.g:
|
|
// .incbin "filename",,4
|
|
if (getTok().isNot(AsmToken::Comma)) {
|
|
if (parseTokenLoc(SkipLoc) || parseAbsoluteExpression(Skip))
|
|
return true;
|
|
}
|
|
if (parseOptionalToken(AsmToken::Comma)) {
|
|
CountLoc = getTok().getLoc();
|
|
if (parseExpression(Count))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (parseEOL())
|
|
return true;
|
|
|
|
if (check(Skip < 0, SkipLoc, "skip is negative"))
|
|
return true;
|
|
|
|
// Attempt to process the included file.
|
|
if (processIncbinFile(Filename, Skip, Count, CountLoc))
|
|
return Error(IncbinLoc, "Could not find incbin file '" + Filename + "'");
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveIf
|
|
/// ::= .if{,eq,ge,gt,le,lt,ne} expression
|
|
bool AsmParser::parseDirectiveIf(SMLoc DirectiveLoc, DirectiveKind DirKind) {
|
|
TheCondStack.push_back(TheCondState);
|
|
TheCondState.TheCond = AsmCond::IfCond;
|
|
if (TheCondState.Ignore) {
|
|
eatToEndOfStatement();
|
|
} else {
|
|
int64_t ExprValue;
|
|
if (parseAbsoluteExpression(ExprValue) || parseEOL())
|
|
return true;
|
|
|
|
switch (DirKind) {
|
|
default:
|
|
llvm_unreachable("unsupported directive");
|
|
case DK_IF:
|
|
case DK_IFNE:
|
|
break;
|
|
case DK_IFEQ:
|
|
ExprValue = ExprValue == 0;
|
|
break;
|
|
case DK_IFGE:
|
|
ExprValue = ExprValue >= 0;
|
|
break;
|
|
case DK_IFGT:
|
|
ExprValue = ExprValue > 0;
|
|
break;
|
|
case DK_IFLE:
|
|
ExprValue = ExprValue <= 0;
|
|
break;
|
|
case DK_IFLT:
|
|
ExprValue = ExprValue < 0;
|
|
break;
|
|
}
|
|
|
|
TheCondState.CondMet = ExprValue;
|
|
TheCondState.Ignore = !TheCondState.CondMet;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveIfb
|
|
/// ::= .ifb string
|
|
bool AsmParser::parseDirectiveIfb(SMLoc DirectiveLoc, bool ExpectBlank) {
|
|
TheCondStack.push_back(TheCondState);
|
|
TheCondState.TheCond = AsmCond::IfCond;
|
|
|
|
if (TheCondState.Ignore) {
|
|
eatToEndOfStatement();
|
|
} else {
|
|
StringRef Str = parseStringToEndOfStatement();
|
|
|
|
if (parseEOL())
|
|
return true;
|
|
|
|
TheCondState.CondMet = ExpectBlank == Str.empty();
|
|
TheCondState.Ignore = !TheCondState.CondMet;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveIfc
|
|
/// ::= .ifc string1, string2
|
|
/// ::= .ifnc string1, string2
|
|
bool AsmParser::parseDirectiveIfc(SMLoc DirectiveLoc, bool ExpectEqual) {
|
|
TheCondStack.push_back(TheCondState);
|
|
TheCondState.TheCond = AsmCond::IfCond;
|
|
|
|
if (TheCondState.Ignore) {
|
|
eatToEndOfStatement();
|
|
} else {
|
|
StringRef Str1 = parseStringToComma();
|
|
|
|
if (parseComma())
|
|
return true;
|
|
|
|
StringRef Str2 = parseStringToEndOfStatement();
|
|
|
|
if (parseEOL())
|
|
return true;
|
|
|
|
TheCondState.CondMet = ExpectEqual == (Str1.trim() == Str2.trim());
|
|
TheCondState.Ignore = !TheCondState.CondMet;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveIfeqs
|
|
/// ::= .ifeqs string1, string2
|
|
bool AsmParser::parseDirectiveIfeqs(SMLoc DirectiveLoc, bool ExpectEqual) {
|
|
TheCondStack.push_back(TheCondState);
|
|
TheCondState.TheCond = AsmCond::IfCond;
|
|
|
|
if (TheCondState.Ignore) {
|
|
eatToEndOfStatement();
|
|
} else {
|
|
if (Lexer.isNot(AsmToken::String)) {
|
|
if (ExpectEqual)
|
|
return TokError("expected string parameter for '.ifeqs' directive");
|
|
return TokError("expected string parameter for '.ifnes' directive");
|
|
}
|
|
|
|
StringRef String1 = getTok().getStringContents();
|
|
Lex();
|
|
|
|
if (Lexer.isNot(AsmToken::Comma)) {
|
|
if (ExpectEqual)
|
|
return TokError(
|
|
"expected comma after first string for '.ifeqs' directive");
|
|
return TokError(
|
|
"expected comma after first string for '.ifnes' directive");
|
|
}
|
|
|
|
Lex();
|
|
|
|
if (Lexer.isNot(AsmToken::String)) {
|
|
if (ExpectEqual)
|
|
return TokError("expected string parameter for '.ifeqs' directive");
|
|
return TokError("expected string parameter for '.ifnes' directive");
|
|
}
|
|
|
|
StringRef String2 = getTok().getStringContents();
|
|
Lex();
|
|
|
|
TheCondState.CondMet = ExpectEqual == (String1 == String2);
|
|
TheCondState.Ignore = !TheCondState.CondMet;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveIfdef
|
|
/// ::= .ifdef symbol
|
|
bool AsmParser::parseDirectiveIfdef(SMLoc DirectiveLoc, bool expect_defined) {
|
|
StringRef Name;
|
|
TheCondStack.push_back(TheCondState);
|
|
TheCondState.TheCond = AsmCond::IfCond;
|
|
|
|
if (TheCondState.Ignore) {
|
|
eatToEndOfStatement();
|
|
} else {
|
|
if (check(parseIdentifier(Name), "expected identifier after '.ifdef'") ||
|
|
parseEOL())
|
|
return true;
|
|
|
|
MCSymbol *Sym = getContext().lookupSymbol(Name);
|
|
|
|
if (expect_defined)
|
|
TheCondState.CondMet = (Sym && !Sym->isUndefined());
|
|
else
|
|
TheCondState.CondMet = (!Sym || Sym->isUndefined());
|
|
TheCondState.Ignore = !TheCondState.CondMet;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveElseIf
|
|
/// ::= .elseif expression
|
|
bool AsmParser::parseDirectiveElseIf(SMLoc DirectiveLoc) {
|
|
if (TheCondState.TheCond != AsmCond::IfCond &&
|
|
TheCondState.TheCond != AsmCond::ElseIfCond)
|
|
return Error(DirectiveLoc, "Encountered a .elseif that doesn't follow an"
|
|
" .if or an .elseif");
|
|
TheCondState.TheCond = AsmCond::ElseIfCond;
|
|
|
|
bool LastIgnoreState = false;
|
|
if (!TheCondStack.empty())
|
|
LastIgnoreState = TheCondStack.back().Ignore;
|
|
if (LastIgnoreState || TheCondState.CondMet) {
|
|
TheCondState.Ignore = true;
|
|
eatToEndOfStatement();
|
|
} else {
|
|
int64_t ExprValue;
|
|
if (parseAbsoluteExpression(ExprValue))
|
|
return true;
|
|
|
|
if (parseEOL())
|
|
return true;
|
|
|
|
TheCondState.CondMet = ExprValue;
|
|
TheCondState.Ignore = !TheCondState.CondMet;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveElse
|
|
/// ::= .else
|
|
bool AsmParser::parseDirectiveElse(SMLoc DirectiveLoc) {
|
|
if (parseEOL())
|
|
return true;
|
|
|
|
if (TheCondState.TheCond != AsmCond::IfCond &&
|
|
TheCondState.TheCond != AsmCond::ElseIfCond)
|
|
return Error(DirectiveLoc, "Encountered a .else that doesn't follow "
|
|
" an .if or an .elseif");
|
|
TheCondState.TheCond = AsmCond::ElseCond;
|
|
bool LastIgnoreState = false;
|
|
if (!TheCondStack.empty())
|
|
LastIgnoreState = TheCondStack.back().Ignore;
|
|
if (LastIgnoreState || TheCondState.CondMet)
|
|
TheCondState.Ignore = true;
|
|
else
|
|
TheCondState.Ignore = false;
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveEnd
|
|
/// ::= .end
|
|
bool AsmParser::parseDirectiveEnd(SMLoc DirectiveLoc) {
|
|
if (parseEOL())
|
|
return true;
|
|
|
|
while (Lexer.isNot(AsmToken::Eof))
|
|
Lexer.Lex();
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveError
|
|
/// ::= .err
|
|
/// ::= .error [string]
|
|
bool AsmParser::parseDirectiveError(SMLoc L, bool WithMessage) {
|
|
if (!TheCondStack.empty()) {
|
|
if (TheCondStack.back().Ignore) {
|
|
eatToEndOfStatement();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!WithMessage)
|
|
return Error(L, ".err encountered");
|
|
|
|
StringRef Message = ".error directive invoked in source file";
|
|
if (Lexer.isNot(AsmToken::EndOfStatement)) {
|
|
if (Lexer.isNot(AsmToken::String))
|
|
return TokError(".error argument must be a string");
|
|
|
|
Message = getTok().getStringContents();
|
|
Lex();
|
|
}
|
|
|
|
return Error(L, Message);
|
|
}
|
|
|
|
/// parseDirectiveWarning
|
|
/// ::= .warning [string]
|
|
bool AsmParser::parseDirectiveWarning(SMLoc L) {
|
|
if (!TheCondStack.empty()) {
|
|
if (TheCondStack.back().Ignore) {
|
|
eatToEndOfStatement();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
StringRef Message = ".warning directive invoked in source file";
|
|
|
|
if (!parseOptionalToken(AsmToken::EndOfStatement)) {
|
|
if (Lexer.isNot(AsmToken::String))
|
|
return TokError(".warning argument must be a string");
|
|
|
|
Message = getTok().getStringContents();
|
|
Lex();
|
|
if (parseEOL())
|
|
return true;
|
|
}
|
|
|
|
return Warning(L, Message);
|
|
}
|
|
|
|
/// parseDirectiveEndIf
|
|
/// ::= .endif
|
|
bool AsmParser::parseDirectiveEndIf(SMLoc DirectiveLoc) {
|
|
if (parseEOL())
|
|
return true;
|
|
|
|
if ((TheCondState.TheCond == AsmCond::NoCond) || TheCondStack.empty())
|
|
return Error(DirectiveLoc, "Encountered a .endif that doesn't follow "
|
|
"an .if or .else");
|
|
if (!TheCondStack.empty()) {
|
|
TheCondState = TheCondStack.back();
|
|
TheCondStack.pop_back();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void AsmParser::initializeDirectiveKindMap() {
|
|
/* Lookup will be done with the directive
|
|
* converted to lower case, so all these
|
|
* keys should be lower case.
|
|
* (target specific directives are handled
|
|
* elsewhere)
|
|
*/
|
|
DirectiveKindMap[".set"] = DK_SET;
|
|
DirectiveKindMap[".equ"] = DK_EQU;
|
|
DirectiveKindMap[".equiv"] = DK_EQUIV;
|
|
DirectiveKindMap[".ascii"] = DK_ASCII;
|
|
DirectiveKindMap[".asciz"] = DK_ASCIZ;
|
|
DirectiveKindMap[".string"] = DK_STRING;
|
|
DirectiveKindMap[".byte"] = DK_BYTE;
|
|
DirectiveKindMap[".short"] = DK_SHORT;
|
|
DirectiveKindMap[".value"] = DK_VALUE;
|
|
DirectiveKindMap[".2byte"] = DK_2BYTE;
|
|
DirectiveKindMap[".long"] = DK_LONG;
|
|
DirectiveKindMap[".int"] = DK_INT;
|
|
DirectiveKindMap[".4byte"] = DK_4BYTE;
|
|
DirectiveKindMap[".quad"] = DK_QUAD;
|
|
DirectiveKindMap[".8byte"] = DK_8BYTE;
|
|
DirectiveKindMap[".octa"] = DK_OCTA;
|
|
DirectiveKindMap[".single"] = DK_SINGLE;
|
|
DirectiveKindMap[".float"] = DK_FLOAT;
|
|
DirectiveKindMap[".double"] = DK_DOUBLE;
|
|
DirectiveKindMap[".align"] = DK_ALIGN;
|
|
DirectiveKindMap[".align32"] = DK_ALIGN32;
|
|
DirectiveKindMap[".balign"] = DK_BALIGN;
|
|
DirectiveKindMap[".balignw"] = DK_BALIGNW;
|
|
DirectiveKindMap[".balignl"] = DK_BALIGNL;
|
|
DirectiveKindMap[".p2align"] = DK_P2ALIGN;
|
|
DirectiveKindMap[".p2alignw"] = DK_P2ALIGNW;
|
|
DirectiveKindMap[".p2alignl"] = DK_P2ALIGNL;
|
|
DirectiveKindMap[".org"] = DK_ORG;
|
|
DirectiveKindMap[".fill"] = DK_FILL;
|
|
DirectiveKindMap[".zero"] = DK_ZERO;
|
|
DirectiveKindMap[".extern"] = DK_EXTERN;
|
|
DirectiveKindMap[".globl"] = DK_GLOBL;
|
|
DirectiveKindMap[".global"] = DK_GLOBAL;
|
|
DirectiveKindMap[".lazy_reference"] = DK_LAZY_REFERENCE;
|
|
DirectiveKindMap[".no_dead_strip"] = DK_NO_DEAD_STRIP;
|
|
DirectiveKindMap[".symbol_resolver"] = DK_SYMBOL_RESOLVER;
|
|
DirectiveKindMap[".private_extern"] = DK_PRIVATE_EXTERN;
|
|
DirectiveKindMap[".reference"] = DK_REFERENCE;
|
|
DirectiveKindMap[".weak_definition"] = DK_WEAK_DEFINITION;
|
|
DirectiveKindMap[".weak_reference"] = DK_WEAK_REFERENCE;
|
|
DirectiveKindMap[".weak_def_can_be_hidden"] = DK_WEAK_DEF_CAN_BE_HIDDEN;
|
|
DirectiveKindMap[".cold"] = DK_COLD;
|
|
DirectiveKindMap[".comm"] = DK_COMM;
|
|
DirectiveKindMap[".common"] = DK_COMMON;
|
|
DirectiveKindMap[".lcomm"] = DK_LCOMM;
|
|
DirectiveKindMap[".abort"] = DK_ABORT;
|
|
DirectiveKindMap[".include"] = DK_INCLUDE;
|
|
DirectiveKindMap[".incbin"] = DK_INCBIN;
|
|
DirectiveKindMap[".code16"] = DK_CODE16;
|
|
DirectiveKindMap[".code16gcc"] = DK_CODE16GCC;
|
|
DirectiveKindMap[".rept"] = DK_REPT;
|
|
DirectiveKindMap[".rep"] = DK_REPT;
|
|
DirectiveKindMap[".irp"] = DK_IRP;
|
|
DirectiveKindMap[".irpc"] = DK_IRPC;
|
|
DirectiveKindMap[".endr"] = DK_ENDR;
|
|
DirectiveKindMap[".if"] = DK_IF;
|
|
DirectiveKindMap[".ifeq"] = DK_IFEQ;
|
|
DirectiveKindMap[".ifge"] = DK_IFGE;
|
|
DirectiveKindMap[".ifgt"] = DK_IFGT;
|
|
DirectiveKindMap[".ifle"] = DK_IFLE;
|
|
DirectiveKindMap[".iflt"] = DK_IFLT;
|
|
DirectiveKindMap[".ifne"] = DK_IFNE;
|
|
DirectiveKindMap[".ifb"] = DK_IFB;
|
|
DirectiveKindMap[".ifnb"] = DK_IFNB;
|
|
DirectiveKindMap[".ifc"] = DK_IFC;
|
|
DirectiveKindMap[".ifeqs"] = DK_IFEQS;
|
|
DirectiveKindMap[".ifnc"] = DK_IFNC;
|
|
DirectiveKindMap[".ifnes"] = DK_IFNES;
|
|
DirectiveKindMap[".ifdef"] = DK_IFDEF;
|
|
DirectiveKindMap[".ifndef"] = DK_IFNDEF;
|
|
DirectiveKindMap[".ifnotdef"] = DK_IFNOTDEF;
|
|
DirectiveKindMap[".elseif"] = DK_ELSEIF;
|
|
DirectiveKindMap[".else"] = DK_ELSE;
|
|
DirectiveKindMap[".end"] = DK_END;
|
|
DirectiveKindMap[".endif"] = DK_ENDIF;
|
|
DirectiveKindMap[".skip"] = DK_SKIP;
|
|
DirectiveKindMap[".space"] = DK_SPACE;
|
|
DirectiveKindMap[".file"] = DK_FILE;
|
|
DirectiveKindMap[".line"] = DK_LINE;
|
|
DirectiveKindMap[".loc"] = DK_LOC;
|
|
DirectiveKindMap[".loc_label"] = DK_LOC_LABEL;
|
|
DirectiveKindMap[".stabs"] = DK_STABS;
|
|
DirectiveKindMap[".cv_file"] = DK_CV_FILE;
|
|
DirectiveKindMap[".cv_func_id"] = DK_CV_FUNC_ID;
|
|
DirectiveKindMap[".cv_loc"] = DK_CV_LOC;
|
|
DirectiveKindMap[".cv_linetable"] = DK_CV_LINETABLE;
|
|
DirectiveKindMap[".cv_inline_linetable"] = DK_CV_INLINE_LINETABLE;
|
|
DirectiveKindMap[".cv_inline_site_id"] = DK_CV_INLINE_SITE_ID;
|
|
DirectiveKindMap[".cv_def_range"] = DK_CV_DEF_RANGE;
|
|
DirectiveKindMap[".cv_string"] = DK_CV_STRING;
|
|
DirectiveKindMap[".cv_stringtable"] = DK_CV_STRINGTABLE;
|
|
DirectiveKindMap[".cv_filechecksums"] = DK_CV_FILECHECKSUMS;
|
|
DirectiveKindMap[".cv_filechecksumoffset"] = DK_CV_FILECHECKSUM_OFFSET;
|
|
DirectiveKindMap[".cv_fpo_data"] = DK_CV_FPO_DATA;
|
|
DirectiveKindMap[".sleb128"] = DK_SLEB128;
|
|
DirectiveKindMap[".uleb128"] = DK_ULEB128;
|
|
DirectiveKindMap[".cfi_sections"] = DK_CFI_SECTIONS;
|
|
DirectiveKindMap[".cfi_startproc"] = DK_CFI_STARTPROC;
|
|
DirectiveKindMap[".cfi_endproc"] = DK_CFI_ENDPROC;
|
|
DirectiveKindMap[".cfi_def_cfa"] = DK_CFI_DEF_CFA;
|
|
DirectiveKindMap[".cfi_def_cfa_offset"] = DK_CFI_DEF_CFA_OFFSET;
|
|
DirectiveKindMap[".cfi_adjust_cfa_offset"] = DK_CFI_ADJUST_CFA_OFFSET;
|
|
DirectiveKindMap[".cfi_def_cfa_register"] = DK_CFI_DEF_CFA_REGISTER;
|
|
DirectiveKindMap[".cfi_llvm_def_aspace_cfa"] = DK_CFI_LLVM_DEF_ASPACE_CFA;
|
|
DirectiveKindMap[".cfi_offset"] = DK_CFI_OFFSET;
|
|
DirectiveKindMap[".cfi_rel_offset"] = DK_CFI_REL_OFFSET;
|
|
DirectiveKindMap[".cfi_personality"] = DK_CFI_PERSONALITY;
|
|
DirectiveKindMap[".cfi_lsda"] = DK_CFI_LSDA;
|
|
DirectiveKindMap[".cfi_remember_state"] = DK_CFI_REMEMBER_STATE;
|
|
DirectiveKindMap[".cfi_restore_state"] = DK_CFI_RESTORE_STATE;
|
|
DirectiveKindMap[".cfi_same_value"] = DK_CFI_SAME_VALUE;
|
|
DirectiveKindMap[".cfi_restore"] = DK_CFI_RESTORE;
|
|
DirectiveKindMap[".cfi_escape"] = DK_CFI_ESCAPE;
|
|
DirectiveKindMap[".cfi_return_column"] = DK_CFI_RETURN_COLUMN;
|
|
DirectiveKindMap[".cfi_signal_frame"] = DK_CFI_SIGNAL_FRAME;
|
|
DirectiveKindMap[".cfi_undefined"] = DK_CFI_UNDEFINED;
|
|
DirectiveKindMap[".cfi_register"] = DK_CFI_REGISTER;
|
|
DirectiveKindMap[".cfi_window_save"] = DK_CFI_WINDOW_SAVE;
|
|
DirectiveKindMap[".cfi_label"] = DK_CFI_LABEL;
|
|
DirectiveKindMap[".cfi_b_key_frame"] = DK_CFI_B_KEY_FRAME;
|
|
DirectiveKindMap[".cfi_mte_tagged_frame"] = DK_CFI_MTE_TAGGED_FRAME;
|
|
DirectiveKindMap[".cfi_val_offset"] = DK_CFI_VAL_OFFSET;
|
|
DirectiveKindMap[".macros_on"] = DK_MACROS_ON;
|
|
DirectiveKindMap[".macros_off"] = DK_MACROS_OFF;
|
|
DirectiveKindMap[".macro"] = DK_MACRO;
|
|
DirectiveKindMap[".exitm"] = DK_EXITM;
|
|
DirectiveKindMap[".endm"] = DK_ENDM;
|
|
DirectiveKindMap[".endmacro"] = DK_ENDMACRO;
|
|
DirectiveKindMap[".purgem"] = DK_PURGEM;
|
|
DirectiveKindMap[".err"] = DK_ERR;
|
|
DirectiveKindMap[".error"] = DK_ERROR;
|
|
DirectiveKindMap[".warning"] = DK_WARNING;
|
|
DirectiveKindMap[".altmacro"] = DK_ALTMACRO;
|
|
DirectiveKindMap[".noaltmacro"] = DK_NOALTMACRO;
|
|
DirectiveKindMap[".reloc"] = DK_RELOC;
|
|
DirectiveKindMap[".dc"] = DK_DC;
|
|
DirectiveKindMap[".dc.a"] = DK_DC_A;
|
|
DirectiveKindMap[".dc.b"] = DK_DC_B;
|
|
DirectiveKindMap[".dc.d"] = DK_DC_D;
|
|
DirectiveKindMap[".dc.l"] = DK_DC_L;
|
|
DirectiveKindMap[".dc.s"] = DK_DC_S;
|
|
DirectiveKindMap[".dc.w"] = DK_DC_W;
|
|
DirectiveKindMap[".dc.x"] = DK_DC_X;
|
|
DirectiveKindMap[".dcb"] = DK_DCB;
|
|
DirectiveKindMap[".dcb.b"] = DK_DCB_B;
|
|
DirectiveKindMap[".dcb.d"] = DK_DCB_D;
|
|
DirectiveKindMap[".dcb.l"] = DK_DCB_L;
|
|
DirectiveKindMap[".dcb.s"] = DK_DCB_S;
|
|
DirectiveKindMap[".dcb.w"] = DK_DCB_W;
|
|
DirectiveKindMap[".dcb.x"] = DK_DCB_X;
|
|
DirectiveKindMap[".ds"] = DK_DS;
|
|
DirectiveKindMap[".ds.b"] = DK_DS_B;
|
|
DirectiveKindMap[".ds.d"] = DK_DS_D;
|
|
DirectiveKindMap[".ds.l"] = DK_DS_L;
|
|
DirectiveKindMap[".ds.p"] = DK_DS_P;
|
|
DirectiveKindMap[".ds.s"] = DK_DS_S;
|
|
DirectiveKindMap[".ds.w"] = DK_DS_W;
|
|
DirectiveKindMap[".ds.x"] = DK_DS_X;
|
|
DirectiveKindMap[".print"] = DK_PRINT;
|
|
DirectiveKindMap[".addrsig"] = DK_ADDRSIG;
|
|
DirectiveKindMap[".addrsig_sym"] = DK_ADDRSIG_SYM;
|
|
DirectiveKindMap[".pseudoprobe"] = DK_PSEUDO_PROBE;
|
|
DirectiveKindMap[".lto_discard"] = DK_LTO_DISCARD;
|
|
DirectiveKindMap[".lto_set_conditional"] = DK_LTO_SET_CONDITIONAL;
|
|
DirectiveKindMap[".memtag"] = DK_MEMTAG;
|
|
}
|
|
|
|
MCAsmMacro *AsmParser::parseMacroLikeBody(SMLoc DirectiveLoc) {
|
|
AsmToken EndToken, StartToken = getTok();
|
|
|
|
unsigned NestLevel = 0;
|
|
while (true) {
|
|
// Check whether we have reached the end of the file.
|
|
if (getLexer().is(AsmToken::Eof)) {
|
|
printError(DirectiveLoc, "no matching '.endr' in definition");
|
|
return nullptr;
|
|
}
|
|
|
|
if (Lexer.is(AsmToken::Identifier)) {
|
|
StringRef Ident = getTok().getIdentifier();
|
|
if (Ident == ".rep" || Ident == ".rept" || Ident == ".irp" ||
|
|
Ident == ".irpc") {
|
|
++NestLevel;
|
|
} else if (Ident == ".endr") {
|
|
if (NestLevel == 0) {
|
|
EndToken = getTok();
|
|
Lex();
|
|
if (Lexer.is(AsmToken::EndOfStatement))
|
|
break;
|
|
printError(getTok().getLoc(), "expected newline");
|
|
return nullptr;
|
|
}
|
|
--NestLevel;
|
|
}
|
|
}
|
|
|
|
// Otherwise, scan till the end of the statement.
|
|
eatToEndOfStatement();
|
|
}
|
|
|
|
const char *BodyStart = StartToken.getLoc().getPointer();
|
|
const char *BodyEnd = EndToken.getLoc().getPointer();
|
|
StringRef Body = StringRef(BodyStart, BodyEnd - BodyStart);
|
|
|
|
// We Are Anonymous.
|
|
MacroLikeBodies.emplace_back(StringRef(), Body, MCAsmMacroParameters());
|
|
return &MacroLikeBodies.back();
|
|
}
|
|
|
|
void AsmParser::instantiateMacroLikeBody(MCAsmMacro *M, SMLoc DirectiveLoc,
|
|
raw_svector_ostream &OS) {
|
|
OS << ".endr\n";
|
|
|
|
std::unique_ptr<MemoryBuffer> Instantiation =
|
|
MemoryBuffer::getMemBufferCopy(OS.str(), "<instantiation>");
|
|
|
|
// Create the macro instantiation object and add to the current macro
|
|
// instantiation stack.
|
|
MacroInstantiation *MI = new MacroInstantiation{
|
|
DirectiveLoc, CurBuffer, getTok().getLoc(), TheCondStack.size()};
|
|
ActiveMacros.push_back(MI);
|
|
|
|
// Jump to the macro instantiation and prime the lexer.
|
|
CurBuffer = SrcMgr.AddNewSourceBuffer(std::move(Instantiation), SMLoc());
|
|
Lexer.setBuffer(SrcMgr.getMemoryBuffer(CurBuffer)->getBuffer());
|
|
Lex();
|
|
}
|
|
|
|
/// parseDirectiveRept
|
|
/// ::= .rep | .rept count
|
|
bool AsmParser::parseDirectiveRept(SMLoc DirectiveLoc, StringRef Dir) {
|
|
const MCExpr *CountExpr;
|
|
SMLoc CountLoc = getTok().getLoc();
|
|
if (parseExpression(CountExpr))
|
|
return true;
|
|
|
|
int64_t Count;
|
|
if (!CountExpr->evaluateAsAbsolute(Count, getStreamer().getAssemblerPtr())) {
|
|
return Error(CountLoc, "unexpected token in '" + Dir + "' directive");
|
|
}
|
|
|
|
if (check(Count < 0, CountLoc, "Count is negative") || parseEOL())
|
|
return true;
|
|
|
|
// Lex the rept definition.
|
|
MCAsmMacro *M = parseMacroLikeBody(DirectiveLoc);
|
|
if (!M)
|
|
return true;
|
|
|
|
// Macro instantiation is lexical, unfortunately. We construct a new buffer
|
|
// to hold the macro body with substitutions.
|
|
SmallString<256> Buf;
|
|
raw_svector_ostream OS(Buf);
|
|
while (Count--) {
|
|
// Note that the AtPseudoVariable is disabled for instantiations of .rep(t).
|
|
if (expandMacro(OS, *M, {}, {}, false))
|
|
return true;
|
|
}
|
|
instantiateMacroLikeBody(M, DirectiveLoc, OS);
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveIrp
|
|
/// ::= .irp symbol,values
|
|
bool AsmParser::parseDirectiveIrp(SMLoc DirectiveLoc) {
|
|
MCAsmMacroParameter Parameter;
|
|
MCAsmMacroArguments A;
|
|
if (check(parseIdentifier(Parameter.Name),
|
|
"expected identifier in '.irp' directive") ||
|
|
parseComma() || parseMacroArguments(nullptr, A) || parseEOL())
|
|
return true;
|
|
|
|
// Lex the irp definition.
|
|
MCAsmMacro *M = parseMacroLikeBody(DirectiveLoc);
|
|
if (!M)
|
|
return true;
|
|
|
|
// Macro instantiation is lexical, unfortunately. We construct a new buffer
|
|
// to hold the macro body with substitutions.
|
|
SmallString<256> Buf;
|
|
raw_svector_ostream OS(Buf);
|
|
|
|
for (const MCAsmMacroArgument &Arg : A) {
|
|
// Note that the AtPseudoVariable is enabled for instantiations of .irp.
|
|
// This is undocumented, but GAS seems to support it.
|
|
if (expandMacro(OS, *M, Parameter, Arg, true))
|
|
return true;
|
|
}
|
|
|
|
instantiateMacroLikeBody(M, DirectiveLoc, OS);
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveIrpc
|
|
/// ::= .irpc symbol,values
|
|
bool AsmParser::parseDirectiveIrpc(SMLoc DirectiveLoc) {
|
|
MCAsmMacroParameter Parameter;
|
|
MCAsmMacroArguments A;
|
|
|
|
if (check(parseIdentifier(Parameter.Name),
|
|
"expected identifier in '.irpc' directive") ||
|
|
parseComma() || parseMacroArguments(nullptr, A))
|
|
return true;
|
|
|
|
if (A.size() != 1 || A.front().size() != 1)
|
|
return TokError("unexpected token in '.irpc' directive");
|
|
if (parseEOL())
|
|
return true;
|
|
|
|
// Lex the irpc definition.
|
|
MCAsmMacro *M = parseMacroLikeBody(DirectiveLoc);
|
|
if (!M)
|
|
return true;
|
|
|
|
// Macro instantiation is lexical, unfortunately. We construct a new buffer
|
|
// to hold the macro body with substitutions.
|
|
SmallString<256> Buf;
|
|
raw_svector_ostream OS(Buf);
|
|
|
|
StringRef Values = A[0][0].is(AsmToken::String) ? A[0][0].getStringContents()
|
|
: A[0][0].getString();
|
|
for (std::size_t I = 0, End = Values.size(); I != End; ++I) {
|
|
MCAsmMacroArgument Arg;
|
|
Arg.emplace_back(AsmToken::Identifier, Values.substr(I, 1));
|
|
|
|
// Note that the AtPseudoVariable is enabled for instantiations of .irpc.
|
|
// This is undocumented, but GAS seems to support it.
|
|
if (expandMacro(OS, *M, Parameter, Arg, true))
|
|
return true;
|
|
}
|
|
|
|
instantiateMacroLikeBody(M, DirectiveLoc, OS);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AsmParser::parseDirectiveEndr(SMLoc DirectiveLoc) {
|
|
if (ActiveMacros.empty())
|
|
return TokError("unmatched '.endr' directive");
|
|
|
|
// The only .repl that should get here are the ones created by
|
|
// instantiateMacroLikeBody.
|
|
assert(getLexer().is(AsmToken::EndOfStatement));
|
|
|
|
handleMacroExit();
|
|
return false;
|
|
}
|
|
|
|
bool AsmParser::parseDirectiveMSEmit(SMLoc IDLoc, ParseStatementInfo &Info,
|
|
size_t Len) {
|
|
const MCExpr *Value;
|
|
SMLoc ExprLoc = getLexer().getLoc();
|
|
if (parseExpression(Value))
|
|
return true;
|
|
const MCConstantExpr *MCE = dyn_cast<MCConstantExpr>(Value);
|
|
if (!MCE)
|
|
return Error(ExprLoc, "unexpected expression in _emit");
|
|
uint64_t IntValue = MCE->getValue();
|
|
if (!isUInt<8>(IntValue) && !isInt<8>(IntValue))
|
|
return Error(ExprLoc, "literal value out of range for directive");
|
|
|
|
Info.AsmRewrites->emplace_back(AOK_Emit, IDLoc, Len);
|
|
return false;
|
|
}
|
|
|
|
bool AsmParser::parseDirectiveMSAlign(SMLoc IDLoc, ParseStatementInfo &Info) {
|
|
const MCExpr *Value;
|
|
SMLoc ExprLoc = getLexer().getLoc();
|
|
if (parseExpression(Value))
|
|
return true;
|
|
const MCConstantExpr *MCE = dyn_cast<MCConstantExpr>(Value);
|
|
if (!MCE)
|
|
return Error(ExprLoc, "unexpected expression in align");
|
|
uint64_t IntValue = MCE->getValue();
|
|
if (!isPowerOf2_64(IntValue))
|
|
return Error(ExprLoc, "literal value not a power of two greater then zero");
|
|
|
|
Info.AsmRewrites->emplace_back(AOK_Align, IDLoc, 5, Log2_64(IntValue));
|
|
return false;
|
|
}
|
|
|
|
bool AsmParser::parseDirectivePrint(SMLoc DirectiveLoc) {
|
|
const AsmToken StrTok = getTok();
|
|
Lex();
|
|
if (StrTok.isNot(AsmToken::String) || StrTok.getString().front() != '"')
|
|
return Error(DirectiveLoc, "expected double quoted string after .print");
|
|
if (parseEOL())
|
|
return true;
|
|
llvm::outs() << StrTok.getStringContents() << '\n';
|
|
return false;
|
|
}
|
|
|
|
bool AsmParser::parseDirectiveAddrsig() {
|
|
if (parseEOL())
|
|
return true;
|
|
getStreamer().emitAddrsig();
|
|
return false;
|
|
}
|
|
|
|
bool AsmParser::parseDirectiveAddrsigSym() {
|
|
StringRef Name;
|
|
if (check(parseIdentifier(Name), "expected identifier") || parseEOL())
|
|
return true;
|
|
MCSymbol *Sym = getContext().getOrCreateSymbol(Name);
|
|
getStreamer().emitAddrsigSym(Sym);
|
|
return false;
|
|
}
|
|
|
|
bool AsmParser::parseDirectivePseudoProbe() {
|
|
int64_t Guid;
|
|
int64_t Index;
|
|
int64_t Type;
|
|
int64_t Attr;
|
|
int64_t Discriminator = 0;
|
|
if (parseIntToken(Guid))
|
|
return true;
|
|
if (parseIntToken(Index))
|
|
return true;
|
|
if (parseIntToken(Type))
|
|
return true;
|
|
if (parseIntToken(Attr))
|
|
return true;
|
|
if (hasDiscriminator(Attr) && parseIntToken(Discriminator))
|
|
return true;
|
|
|
|
// Parse inline stack like @ GUID:11:12 @ GUID:1:11 @ GUID:3:21
|
|
MCPseudoProbeInlineStack InlineStack;
|
|
|
|
while (getLexer().is(AsmToken::At)) {
|
|
// eat @
|
|
Lex();
|
|
|
|
int64_t CallerGuid = 0;
|
|
if (getLexer().is(AsmToken::Integer)) {
|
|
CallerGuid = getTok().getIntVal();
|
|
Lex();
|
|
}
|
|
|
|
// eat colon
|
|
if (getLexer().is(AsmToken::Colon))
|
|
Lex();
|
|
|
|
int64_t CallerProbeId = 0;
|
|
if (getLexer().is(AsmToken::Integer)) {
|
|
CallerProbeId = getTok().getIntVal();
|
|
Lex();
|
|
}
|
|
|
|
InlineSite Site(CallerGuid, CallerProbeId);
|
|
InlineStack.push_back(Site);
|
|
}
|
|
|
|
// Parse function entry name
|
|
StringRef FnName;
|
|
if (parseIdentifier(FnName))
|
|
return Error(getLexer().getLoc(), "expected identifier");
|
|
MCSymbol *FnSym = getContext().lookupSymbol(FnName);
|
|
|
|
if (parseEOL())
|
|
return true;
|
|
|
|
getStreamer().emitPseudoProbe(Guid, Index, Type, Attr, Discriminator,
|
|
InlineStack, FnSym);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveLTODiscard
|
|
/// ::= ".lto_discard" [ identifier ( , identifier )* ]
|
|
/// The LTO library emits this directive to discard non-prevailing symbols.
|
|
/// We ignore symbol assignments and attribute changes for the specified
|
|
/// symbols.
|
|
bool AsmParser::parseDirectiveLTODiscard() {
|
|
auto ParseOp = [&]() -> bool {
|
|
StringRef Name;
|
|
SMLoc Loc = getTok().getLoc();
|
|
if (parseIdentifier(Name))
|
|
return Error(Loc, "expected identifier");
|
|
LTODiscardSymbols.insert(Name);
|
|
return false;
|
|
};
|
|
|
|
LTODiscardSymbols.clear();
|
|
return parseMany(ParseOp);
|
|
}
|
|
|
|
// We are comparing pointers, but the pointers are relative to a single string.
|
|
// Thus, this should always be deterministic.
|
|
static int rewritesSort(const AsmRewrite *AsmRewriteA,
|
|
const AsmRewrite *AsmRewriteB) {
|
|
if (AsmRewriteA->Loc.getPointer() < AsmRewriteB->Loc.getPointer())
|
|
return -1;
|
|
if (AsmRewriteB->Loc.getPointer() < AsmRewriteA->Loc.getPointer())
|
|
return 1;
|
|
|
|
// It's possible to have a SizeDirective, Imm/ImmPrefix and an Input/Output
|
|
// rewrite to the same location. Make sure the SizeDirective rewrite is
|
|
// performed first, then the Imm/ImmPrefix and finally the Input/Output. This
|
|
// ensures the sort algorithm is stable.
|
|
if (AsmRewritePrecedence[AsmRewriteA->Kind] >
|
|
AsmRewritePrecedence[AsmRewriteB->Kind])
|
|
return -1;
|
|
|
|
if (AsmRewritePrecedence[AsmRewriteA->Kind] <
|
|
AsmRewritePrecedence[AsmRewriteB->Kind])
|
|
return 1;
|
|
llvm_unreachable("Unstable rewrite sort.");
|
|
}
|
|
|
|
bool AsmParser::parseMSInlineAsm(
|
|
std::string &AsmString, unsigned &NumOutputs, unsigned &NumInputs,
|
|
SmallVectorImpl<std::pair<void *, bool>> &OpDecls,
|
|
SmallVectorImpl<std::string> &Constraints,
|
|
SmallVectorImpl<std::string> &Clobbers, const MCInstrInfo *MII,
|
|
MCInstPrinter *IP, MCAsmParserSemaCallback &SI) {
|
|
SmallVector<void *, 4> InputDecls;
|
|
SmallVector<void *, 4> OutputDecls;
|
|
SmallVector<bool, 4> InputDeclsAddressOf;
|
|
SmallVector<bool, 4> OutputDeclsAddressOf;
|
|
SmallVector<std::string, 4> InputConstraints;
|
|
SmallVector<std::string, 4> OutputConstraints;
|
|
SmallVector<MCRegister, 4> ClobberRegs;
|
|
|
|
SmallVector<AsmRewrite, 4> AsmStrRewrites;
|
|
|
|
// Prime the lexer.
|
|
Lex();
|
|
|
|
// While we have input, parse each statement.
|
|
unsigned InputIdx = 0;
|
|
unsigned OutputIdx = 0;
|
|
while (getLexer().isNot(AsmToken::Eof)) {
|
|
// Parse curly braces marking block start/end
|
|
if (parseCurlyBlockScope(AsmStrRewrites))
|
|
continue;
|
|
|
|
ParseStatementInfo Info(&AsmStrRewrites);
|
|
bool StatementErr = parseStatement(Info, &SI);
|
|
|
|
if (StatementErr || Info.ParseError) {
|
|
// Emit pending errors if any exist.
|
|
printPendingErrors();
|
|
return true;
|
|
}
|
|
|
|
// No pending error should exist here.
|
|
assert(!hasPendingError() && "unexpected error from parseStatement");
|
|
|
|
if (Info.Opcode == ~0U)
|
|
continue;
|
|
|
|
const MCInstrDesc &Desc = MII->get(Info.Opcode);
|
|
|
|
// Build the list of clobbers, outputs and inputs.
|
|
for (unsigned i = 1, e = Info.ParsedOperands.size(); i != e; ++i) {
|
|
MCParsedAsmOperand &Operand = *Info.ParsedOperands[i];
|
|
|
|
// Register operand.
|
|
if (Operand.isReg() && !Operand.needAddressOf() &&
|
|
!getTargetParser().omitRegisterFromClobberLists(Operand.getReg())) {
|
|
unsigned NumDefs = Desc.getNumDefs();
|
|
// Clobber.
|
|
if (NumDefs && Operand.getMCOperandNum() < NumDefs)
|
|
ClobberRegs.push_back(Operand.getReg());
|
|
continue;
|
|
}
|
|
|
|
// Expr/Input or Output.
|
|
StringRef SymName = Operand.getSymName();
|
|
if (SymName.empty())
|
|
continue;
|
|
|
|
void *OpDecl = Operand.getOpDecl();
|
|
if (!OpDecl)
|
|
continue;
|
|
|
|
StringRef Constraint = Operand.getConstraint();
|
|
if (Operand.isImm()) {
|
|
// Offset as immediate
|
|
if (Operand.isOffsetOfLocal())
|
|
Constraint = "r";
|
|
else
|
|
Constraint = "i";
|
|
}
|
|
|
|
bool isOutput = (i == 1) && Desc.mayStore();
|
|
bool Restricted = Operand.isMemUseUpRegs();
|
|
SMLoc Start = SMLoc::getFromPointer(SymName.data());
|
|
if (isOutput) {
|
|
++InputIdx;
|
|
OutputDecls.push_back(OpDecl);
|
|
OutputDeclsAddressOf.push_back(Operand.needAddressOf());
|
|
OutputConstraints.push_back(("=" + Constraint).str());
|
|
AsmStrRewrites.emplace_back(AOK_Output, Start, SymName.size(), 0,
|
|
Restricted);
|
|
} else {
|
|
InputDecls.push_back(OpDecl);
|
|
InputDeclsAddressOf.push_back(Operand.needAddressOf());
|
|
InputConstraints.push_back(Constraint.str());
|
|
if (Desc.operands()[i - 1].isBranchTarget())
|
|
AsmStrRewrites.emplace_back(AOK_CallInput, Start, SymName.size(), 0,
|
|
Restricted);
|
|
else
|
|
AsmStrRewrites.emplace_back(AOK_Input, Start, SymName.size(), 0,
|
|
Restricted);
|
|
}
|
|
}
|
|
|
|
// Consider implicit defs to be clobbers. Think of cpuid and push.
|
|
llvm::append_range(ClobberRegs, Desc.implicit_defs());
|
|
}
|
|
|
|
// Set the number of Outputs and Inputs.
|
|
NumOutputs = OutputDecls.size();
|
|
NumInputs = InputDecls.size();
|
|
|
|
// Set the unique clobbers.
|
|
array_pod_sort(ClobberRegs.begin(), ClobberRegs.end());
|
|
ClobberRegs.erase(llvm::unique(ClobberRegs), ClobberRegs.end());
|
|
Clobbers.assign(ClobberRegs.size(), std::string());
|
|
for (unsigned I = 0, E = ClobberRegs.size(); I != E; ++I) {
|
|
raw_string_ostream OS(Clobbers[I]);
|
|
IP->printRegName(OS, ClobberRegs[I]);
|
|
}
|
|
|
|
// Merge the various outputs and inputs. Output are expected first.
|
|
if (NumOutputs || NumInputs) {
|
|
unsigned NumExprs = NumOutputs + NumInputs;
|
|
OpDecls.resize(NumExprs);
|
|
Constraints.resize(NumExprs);
|
|
for (unsigned i = 0; i < NumOutputs; ++i) {
|
|
OpDecls[i] = std::make_pair(OutputDecls[i], OutputDeclsAddressOf[i]);
|
|
Constraints[i] = OutputConstraints[i];
|
|
}
|
|
for (unsigned i = 0, j = NumOutputs; i < NumInputs; ++i, ++j) {
|
|
OpDecls[j] = std::make_pair(InputDecls[i], InputDeclsAddressOf[i]);
|
|
Constraints[j] = InputConstraints[i];
|
|
}
|
|
}
|
|
|
|
// Build the IR assembly string.
|
|
std::string AsmStringIR;
|
|
raw_string_ostream OS(AsmStringIR);
|
|
StringRef ASMString =
|
|
SrcMgr.getMemoryBuffer(SrcMgr.getMainFileID())->getBuffer();
|
|
const char *AsmStart = ASMString.begin();
|
|
const char *AsmEnd = ASMString.end();
|
|
array_pod_sort(AsmStrRewrites.begin(), AsmStrRewrites.end(), rewritesSort);
|
|
for (auto I = AsmStrRewrites.begin(), E = AsmStrRewrites.end(); I != E; ++I) {
|
|
const AsmRewrite &AR = *I;
|
|
// Check if this has already been covered by another rewrite...
|
|
if (AR.Done)
|
|
continue;
|
|
AsmRewriteKind Kind = AR.Kind;
|
|
|
|
const char *Loc = AR.Loc.getPointer();
|
|
assert(Loc >= AsmStart && "Expected Loc to be at or after Start!");
|
|
|
|
// Emit everything up to the immediate/expression.
|
|
if (unsigned Len = Loc - AsmStart)
|
|
OS << StringRef(AsmStart, Len);
|
|
|
|
// Skip the original expression.
|
|
if (Kind == AOK_Skip) {
|
|
AsmStart = Loc + AR.Len;
|
|
continue;
|
|
}
|
|
|
|
unsigned AdditionalSkip = 0;
|
|
// Rewrite expressions in $N notation.
|
|
switch (Kind) {
|
|
default:
|
|
break;
|
|
case AOK_IntelExpr:
|
|
assert(AR.IntelExp.isValid() && "cannot write invalid intel expression");
|
|
if (AR.IntelExp.NeedBracs)
|
|
OS << "[";
|
|
if (AR.IntelExp.hasBaseReg())
|
|
OS << AR.IntelExp.BaseReg;
|
|
if (AR.IntelExp.hasIndexReg())
|
|
OS << (AR.IntelExp.hasBaseReg() ? " + " : "")
|
|
<< AR.IntelExp.IndexReg;
|
|
if (AR.IntelExp.Scale > 1)
|
|
OS << " * $$" << AR.IntelExp.Scale;
|
|
if (AR.IntelExp.hasOffset()) {
|
|
if (AR.IntelExp.hasRegs())
|
|
OS << " + ";
|
|
// Fuse this rewrite with a rewrite of the offset name, if present.
|
|
StringRef OffsetName = AR.IntelExp.OffsetName;
|
|
SMLoc OffsetLoc = SMLoc::getFromPointer(AR.IntelExp.OffsetName.data());
|
|
size_t OffsetLen = OffsetName.size();
|
|
auto rewrite_it = std::find_if(
|
|
I, AsmStrRewrites.end(), [&](const AsmRewrite &FusingAR) {
|
|
return FusingAR.Loc == OffsetLoc && FusingAR.Len == OffsetLen &&
|
|
(FusingAR.Kind == AOK_Input ||
|
|
FusingAR.Kind == AOK_CallInput);
|
|
});
|
|
if (rewrite_it == AsmStrRewrites.end()) {
|
|
OS << "offset " << OffsetName;
|
|
} else if (rewrite_it->Kind == AOK_CallInput) {
|
|
OS << "${" << InputIdx++ << ":P}";
|
|
rewrite_it->Done = true;
|
|
} else {
|
|
OS << '$' << InputIdx++;
|
|
rewrite_it->Done = true;
|
|
}
|
|
}
|
|
if (AR.IntelExp.Imm || AR.IntelExp.emitImm())
|
|
OS << (AR.IntelExp.emitImm() ? "$$" : " + $$") << AR.IntelExp.Imm;
|
|
if (AR.IntelExp.NeedBracs)
|
|
OS << "]";
|
|
break;
|
|
case AOK_Label:
|
|
OS << Ctx.getAsmInfo()->getPrivateLabelPrefix() << AR.Label;
|
|
break;
|
|
case AOK_Input:
|
|
if (AR.IntelExpRestricted)
|
|
OS << "${" << InputIdx++ << ":P}";
|
|
else
|
|
OS << '$' << InputIdx++;
|
|
break;
|
|
case AOK_CallInput:
|
|
OS << "${" << InputIdx++ << ":P}";
|
|
break;
|
|
case AOK_Output:
|
|
if (AR.IntelExpRestricted)
|
|
OS << "${" << OutputIdx++ << ":P}";
|
|
else
|
|
OS << '$' << OutputIdx++;
|
|
break;
|
|
case AOK_SizeDirective:
|
|
switch (AR.Val) {
|
|
default: break;
|
|
case 8: OS << "byte ptr "; break;
|
|
case 16: OS << "word ptr "; break;
|
|
case 32: OS << "dword ptr "; break;
|
|
case 64: OS << "qword ptr "; break;
|
|
case 80: OS << "xword ptr "; break;
|
|
case 128: OS << "xmmword ptr "; break;
|
|
case 256: OS << "ymmword ptr "; break;
|
|
}
|
|
break;
|
|
case AOK_Emit:
|
|
OS << ".byte";
|
|
break;
|
|
case AOK_Align: {
|
|
// MS alignment directives are measured in bytes. If the native assembler
|
|
// measures alignment in bytes, we can pass it straight through.
|
|
OS << ".align";
|
|
if (getContext().getAsmInfo()->getAlignmentIsInBytes())
|
|
break;
|
|
|
|
// Alignment is in log2 form, so print that instead and skip the original
|
|
// immediate.
|
|
unsigned Val = AR.Val;
|
|
OS << ' ' << Val;
|
|
assert(Val < 10 && "Expected alignment less then 2^10.");
|
|
AdditionalSkip = (Val < 4) ? 2 : Val < 7 ? 3 : 4;
|
|
break;
|
|
}
|
|
case AOK_EVEN:
|
|
OS << ".even";
|
|
break;
|
|
case AOK_EndOfStatement:
|
|
OS << "\n\t";
|
|
break;
|
|
}
|
|
|
|
// Skip the original expression.
|
|
AsmStart = Loc + AR.Len + AdditionalSkip;
|
|
}
|
|
|
|
// Emit the remainder of the asm string.
|
|
if (AsmStart != AsmEnd)
|
|
OS << StringRef(AsmStart, AsmEnd - AsmStart);
|
|
|
|
AsmString = std::move(AsmStringIR);
|
|
return false;
|
|
}
|
|
|
|
bool HLASMAsmParser::parseAsHLASMLabel(ParseStatementInfo &Info,
|
|
MCAsmParserSemaCallback *SI) {
|
|
AsmToken LabelTok = getTok();
|
|
SMLoc LabelLoc = LabelTok.getLoc();
|
|
StringRef LabelVal;
|
|
|
|
if (parseIdentifier(LabelVal))
|
|
return Error(LabelLoc, "The HLASM Label has to be an Identifier");
|
|
|
|
// We have validated whether the token is an Identifier.
|
|
// Now we have to validate whether the token is a
|
|
// valid HLASM Label.
|
|
if (!getTargetParser().isLabel(LabelTok) || checkForValidSection())
|
|
return true;
|
|
|
|
// Lex leading spaces to get to the next operand.
|
|
lexLeadingSpaces();
|
|
|
|
// We shouldn't emit the label if there is nothing else after the label.
|
|
// i.e asm("<token>\n")
|
|
if (getTok().is(AsmToken::EndOfStatement))
|
|
return Error(LabelLoc,
|
|
"Cannot have just a label for an HLASM inline asm statement");
|
|
|
|
MCSymbol *Sym = getContext().getOrCreateSymbol(
|
|
getContext().getAsmInfo()->isHLASM() ? LabelVal.upper() : LabelVal);
|
|
|
|
// Emit the label.
|
|
Out.emitLabel(Sym, LabelLoc);
|
|
|
|
// If we are generating dwarf for assembly source files then gather the
|
|
// info to make a dwarf label entry for this label if needed.
|
|
if (enabledGenDwarfForAssembly())
|
|
MCGenDwarfLabelEntry::Make(Sym, &getStreamer(), getSourceManager(),
|
|
LabelLoc);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool HLASMAsmParser::parseAsMachineInstruction(ParseStatementInfo &Info,
|
|
MCAsmParserSemaCallback *SI) {
|
|
AsmToken OperationEntryTok = Lexer.getTok();
|
|
SMLoc OperationEntryLoc = OperationEntryTok.getLoc();
|
|
StringRef OperationEntryVal;
|
|
|
|
// Attempt to parse the first token as an Identifier
|
|
if (parseIdentifier(OperationEntryVal))
|
|
return Error(OperationEntryLoc, "unexpected token at start of statement");
|
|
|
|
// Once we've parsed the operation entry successfully, lex
|
|
// any spaces to get to the OperandEntries.
|
|
lexLeadingSpaces();
|
|
|
|
return parseAndMatchAndEmitTargetInstruction(
|
|
Info, OperationEntryVal, OperationEntryTok, OperationEntryLoc);
|
|
}
|
|
|
|
bool HLASMAsmParser::parseStatement(ParseStatementInfo &Info,
|
|
MCAsmParserSemaCallback *SI) {
|
|
assert(!hasPendingError() && "parseStatement started with pending error");
|
|
|
|
// Should the first token be interpreted as a HLASM Label.
|
|
bool ShouldParseAsHLASMLabel = false;
|
|
|
|
// If a Name Entry exists, it should occur at the very
|
|
// start of the string. In this case, we should parse the
|
|
// first non-space token as a Label.
|
|
// If the Name entry is missing (i.e. there's some other
|
|
// token), then we attempt to parse the first non-space
|
|
// token as a Machine Instruction.
|
|
if (getTok().isNot(AsmToken::Space))
|
|
ShouldParseAsHLASMLabel = true;
|
|
|
|
// If we have an EndOfStatement (which includes the target's comment
|
|
// string) we can appropriately lex it early on)
|
|
if (Lexer.is(AsmToken::EndOfStatement)) {
|
|
// if this is a line comment we can drop it safely
|
|
if (getTok().getString().empty() || getTok().getString().front() == '\r' ||
|
|
getTok().getString().front() == '\n')
|
|
Out.addBlankLine();
|
|
Lex();
|
|
return false;
|
|
}
|
|
|
|
// We have established how to parse the inline asm statement.
|
|
// Now we can safely lex any leading spaces to get to the
|
|
// first token.
|
|
lexLeadingSpaces();
|
|
|
|
// If we see a new line or carriage return as the first operand,
|
|
// after lexing leading spaces, emit the new line and lex the
|
|
// EndOfStatement token.
|
|
if (Lexer.is(AsmToken::EndOfStatement)) {
|
|
if (getTok().getString().front() == '\n' ||
|
|
getTok().getString().front() == '\r') {
|
|
Out.addBlankLine();
|
|
Lex();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Handle the label first if we have to before processing the rest
|
|
// of the tokens as a machine instruction.
|
|
if (ShouldParseAsHLASMLabel) {
|
|
// If there were any errors while handling and emitting the label,
|
|
// early return.
|
|
if (parseAsHLASMLabel(Info, SI)) {
|
|
// If we know we've failed in parsing, simply eat until end of the
|
|
// statement. This ensures that we don't process any other statements.
|
|
eatToEndOfStatement();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return parseAsMachineInstruction(Info, SI);
|
|
}
|
|
|
|
namespace llvm {
|
|
namespace MCParserUtils {
|
|
|
|
bool parseAssignmentExpression(StringRef Name, bool allow_redef,
|
|
MCAsmParser &Parser, MCSymbol *&Sym,
|
|
const MCExpr *&Value) {
|
|
|
|
// FIXME: Use better location, we should use proper tokens.
|
|
SMLoc EqualLoc = Parser.getTok().getLoc();
|
|
if (Parser.parseExpression(Value))
|
|
return Parser.TokError("missing expression");
|
|
if (Parser.parseEOL())
|
|
return true;
|
|
// Relocation specifiers are not permitted. For now, handle just
|
|
// MCSymbolRefExpr.
|
|
if (auto *S = dyn_cast<MCSymbolRefExpr>(Value); S && S->getSpecifier())
|
|
return Parser.Error(
|
|
EqualLoc, "relocation specifier not permitted in symbol equating");
|
|
|
|
// Validate that the LHS is allowed to be a variable (either it has not been
|
|
// used as a symbol, or it is an absolute symbol).
|
|
Sym = Parser.getContext().lookupSymbol(Name);
|
|
if (Sym) {
|
|
if ((Sym->isVariable() || Sym->isDefined()) &&
|
|
(!allow_redef || !Sym->isRedefinable()))
|
|
return Parser.Error(EqualLoc, "redefinition of '" + Name + "'");
|
|
// If the symbol is redefinable, clone it and update the symbol table
|
|
// to the new symbol. Existing references to the original symbol remain
|
|
// unchanged.
|
|
if (Sym->isRedefinable())
|
|
Sym = Parser.getContext().cloneSymbol(*Sym);
|
|
} else if (Name == ".") {
|
|
Parser.getStreamer().emitValueToOffset(Value, 0, EqualLoc);
|
|
return false;
|
|
} else
|
|
Sym = Parser.getContext().getOrCreateSymbol(Name);
|
|
|
|
Sym->setRedefinable(allow_redef);
|
|
|
|
return false;
|
|
}
|
|
|
|
} // end namespace MCParserUtils
|
|
} // end namespace llvm
|
|
|
|
/// Create an MCAsmParser instance.
|
|
MCAsmParser *llvm::createMCAsmParser(SourceMgr &SM, MCContext &C,
|
|
MCStreamer &Out, const MCAsmInfo &MAI,
|
|
unsigned CB) {
|
|
if (C.getTargetTriple().isSystemZ() && C.getTargetTriple().isOSzOS())
|
|
return new HLASMAsmParser(SM, C, Out, MAI, CB);
|
|
|
|
return new AsmParser(SM, C, Out, MAI, CB);
|
|
}
|