
Match ML.EXE's behavior for ALIGN, EVEN, and ORG directives both at file level and in STRUCTs. We currently reject negative offsets passed to ORG inside STRUCTs (in ML.EXE and ML64.EXE, they wrap around as for an unsigned 32-bit integer). Also, if a STRUCT is declared using an ORG directive, no value of that type can be defined. Reviewed By: thakis Differential Revision: https://reviews.llvm.org/D92507
7512 lines
250 KiB
C++
7512 lines
250 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 the parser for assembly files.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/ADT/APFloat.h"
|
|
#include "llvm/ADT/APInt.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/None.h"
|
|
#include "llvm/ADT/Optional.h"
|
|
#include "llvm/ADT/STLExtras.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/StringSwitch.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/MCObjectFileInfo.h"
|
|
#include "llvm/MC/MCParser/AsmCond.h"
|
|
#include "llvm/MC/MCParser/AsmLexer.h"
|
|
#include "llvm/MC/MCParser/MCAsmLexer.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/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/Format.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 <sstream>
|
|
#include <string>
|
|
#include <tuple>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
using namespace llvm;
|
|
|
|
extern cl::opt<unsigned> AsmMacroMaxNestingDepth;
|
|
|
|
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;
|
|
|
|
/// The value associated with a macro exit.
|
|
Optional<std::string> ExitValue;
|
|
|
|
SmallVectorImpl<AsmRewrite> *AsmRewrites = nullptr;
|
|
|
|
ParseStatementInfo() = delete;
|
|
ParseStatementInfo(SmallVectorImpl<AsmRewrite> *rewrites)
|
|
: AsmRewrites(rewrites) {}
|
|
};
|
|
|
|
enum FieldType {
|
|
FT_INTEGRAL, // Initializer: integer expression, stored as an MCExpr.
|
|
FT_REAL, // Initializer: real number, stored as an APInt.
|
|
FT_STRUCT // Initializer: struct initializer, stored recursively.
|
|
};
|
|
|
|
struct FieldInfo;
|
|
struct StructInfo {
|
|
StringRef Name;
|
|
bool IsUnion = false;
|
|
bool Initializable = true;
|
|
unsigned Alignment = 0;
|
|
unsigned AlignmentSize = 0;
|
|
unsigned NextOffset = 0;
|
|
unsigned Size = 0;
|
|
std::vector<FieldInfo> Fields;
|
|
StringMap<size_t> FieldsByName;
|
|
|
|
FieldInfo &addField(StringRef FieldName, FieldType FT,
|
|
unsigned FieldAlignmentSize);
|
|
|
|
StructInfo() = default;
|
|
|
|
StructInfo(StringRef StructName, bool Union, unsigned AlignmentValue)
|
|
: Name(StructName), IsUnion(Union), Alignment(AlignmentValue) {}
|
|
};
|
|
|
|
// FIXME: This should probably use a class hierarchy, raw pointers between the
|
|
// objects, and dynamic type resolution instead of a union. On the other hand,
|
|
// ownership then becomes much more complicated; the obvious thing would be to
|
|
// use BumpPtrAllocator, but the lack of a destructor makes that messy.
|
|
|
|
struct StructInitializer;
|
|
struct IntFieldInfo {
|
|
SmallVector<const MCExpr *, 1> Values;
|
|
|
|
IntFieldInfo() = default;
|
|
IntFieldInfo(const SmallVector<const MCExpr *, 1> &V) { Values = V; }
|
|
IntFieldInfo(SmallVector<const MCExpr *, 1> &&V) { Values = V; }
|
|
};
|
|
struct RealFieldInfo {
|
|
SmallVector<APInt, 1> AsIntValues;
|
|
|
|
RealFieldInfo() = default;
|
|
RealFieldInfo(const SmallVector<APInt, 1> &V) { AsIntValues = V; }
|
|
RealFieldInfo(SmallVector<APInt, 1> &&V) { AsIntValues = V; }
|
|
};
|
|
struct StructFieldInfo {
|
|
std::vector<StructInitializer> Initializers;
|
|
StructInfo Structure;
|
|
|
|
StructFieldInfo() = default;
|
|
StructFieldInfo(const std::vector<StructInitializer> &V, StructInfo S) {
|
|
Initializers = V;
|
|
Structure = S;
|
|
}
|
|
StructFieldInfo(std::vector<StructInitializer> &&V, StructInfo S) {
|
|
Initializers = V;
|
|
Structure = S;
|
|
}
|
|
};
|
|
|
|
class FieldInitializer {
|
|
public:
|
|
FieldType FT;
|
|
union {
|
|
IntFieldInfo IntInfo;
|
|
RealFieldInfo RealInfo;
|
|
StructFieldInfo StructInfo;
|
|
};
|
|
|
|
~FieldInitializer() {
|
|
switch (FT) {
|
|
case FT_INTEGRAL:
|
|
IntInfo.~IntFieldInfo();
|
|
break;
|
|
case FT_REAL:
|
|
RealInfo.~RealFieldInfo();
|
|
break;
|
|
case FT_STRUCT:
|
|
StructInfo.~StructFieldInfo();
|
|
break;
|
|
}
|
|
}
|
|
|
|
FieldInitializer(FieldType FT) : FT(FT) {
|
|
switch (FT) {
|
|
case FT_INTEGRAL:
|
|
new (&IntInfo) IntFieldInfo();
|
|
break;
|
|
case FT_REAL:
|
|
new (&RealInfo) RealFieldInfo();
|
|
break;
|
|
case FT_STRUCT:
|
|
new (&StructInfo) StructFieldInfo();
|
|
break;
|
|
}
|
|
}
|
|
|
|
FieldInitializer(SmallVector<const MCExpr *, 1> &&Values) : FT(FT_INTEGRAL) {
|
|
new (&IntInfo) IntFieldInfo(Values);
|
|
}
|
|
|
|
FieldInitializer(SmallVector<APInt, 1> &&AsIntValues) : FT(FT_REAL) {
|
|
new (&RealInfo) RealFieldInfo(AsIntValues);
|
|
}
|
|
|
|
FieldInitializer(std::vector<StructInitializer> &&Initializers,
|
|
struct StructInfo Structure)
|
|
: FT(FT_STRUCT) {
|
|
new (&StructInfo) StructFieldInfo(Initializers, Structure);
|
|
}
|
|
|
|
FieldInitializer(const FieldInitializer &Initializer) : FT(Initializer.FT) {
|
|
switch (FT) {
|
|
case FT_INTEGRAL:
|
|
new (&IntInfo) IntFieldInfo(Initializer.IntInfo);
|
|
break;
|
|
case FT_REAL:
|
|
new (&RealInfo) RealFieldInfo(Initializer.RealInfo);
|
|
break;
|
|
case FT_STRUCT:
|
|
new (&StructInfo) StructFieldInfo(Initializer.StructInfo);
|
|
break;
|
|
}
|
|
}
|
|
|
|
FieldInitializer(FieldInitializer &&Initializer) : FT(Initializer.FT) {
|
|
switch (FT) {
|
|
case FT_INTEGRAL:
|
|
new (&IntInfo) IntFieldInfo(Initializer.IntInfo);
|
|
break;
|
|
case FT_REAL:
|
|
new (&RealInfo) RealFieldInfo(Initializer.RealInfo);
|
|
break;
|
|
case FT_STRUCT:
|
|
new (&StructInfo) StructFieldInfo(Initializer.StructInfo);
|
|
break;
|
|
}
|
|
}
|
|
|
|
FieldInitializer &operator=(const FieldInitializer &Initializer) {
|
|
if (FT != Initializer.FT) {
|
|
switch (FT) {
|
|
case FT_INTEGRAL:
|
|
IntInfo.~IntFieldInfo();
|
|
break;
|
|
case FT_REAL:
|
|
RealInfo.~RealFieldInfo();
|
|
break;
|
|
case FT_STRUCT:
|
|
StructInfo.~StructFieldInfo();
|
|
break;
|
|
}
|
|
}
|
|
FT = Initializer.FT;
|
|
switch (FT) {
|
|
case FT_INTEGRAL:
|
|
IntInfo = Initializer.IntInfo;
|
|
break;
|
|
case FT_REAL:
|
|
RealInfo = Initializer.RealInfo;
|
|
break;
|
|
case FT_STRUCT:
|
|
StructInfo = Initializer.StructInfo;
|
|
break;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
FieldInitializer &operator=(FieldInitializer &&Initializer) {
|
|
if (FT != Initializer.FT) {
|
|
switch (FT) {
|
|
case FT_INTEGRAL:
|
|
IntInfo.~IntFieldInfo();
|
|
break;
|
|
case FT_REAL:
|
|
RealInfo.~RealFieldInfo();
|
|
break;
|
|
case FT_STRUCT:
|
|
StructInfo.~StructFieldInfo();
|
|
break;
|
|
}
|
|
}
|
|
FT = Initializer.FT;
|
|
switch (FT) {
|
|
case FT_INTEGRAL:
|
|
IntInfo = Initializer.IntInfo;
|
|
break;
|
|
case FT_REAL:
|
|
RealInfo = Initializer.RealInfo;
|
|
break;
|
|
case FT_STRUCT:
|
|
StructInfo = Initializer.StructInfo;
|
|
break;
|
|
}
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
struct StructInitializer {
|
|
std::vector<FieldInitializer> FieldInitializers;
|
|
};
|
|
|
|
struct FieldInfo {
|
|
// Offset of the field within the containing STRUCT.
|
|
unsigned Offset = 0;
|
|
|
|
// Total size of the field (= LengthOf * Type).
|
|
unsigned SizeOf = 0;
|
|
|
|
// Number of elements in the field (1 if scalar, >1 if an array).
|
|
unsigned LengthOf = 0;
|
|
|
|
// Size of a single entry in this field, in bytes ("type" in MASM standards).
|
|
unsigned Type = 0;
|
|
|
|
FieldInitializer Contents;
|
|
|
|
FieldInfo(FieldType FT) : Contents(FT) {}
|
|
};
|
|
|
|
FieldInfo &StructInfo::addField(StringRef FieldName, FieldType FT,
|
|
unsigned FieldAlignmentSize) {
|
|
if (!FieldName.empty())
|
|
FieldsByName[FieldName.lower()] = Fields.size();
|
|
Fields.emplace_back(FT);
|
|
FieldInfo &Field = Fields.back();
|
|
Field.Offset =
|
|
llvm::alignTo(NextOffset, std::min(Alignment, FieldAlignmentSize));
|
|
if (!IsUnion) {
|
|
NextOffset = std::max(NextOffset, Field.Offset);
|
|
}
|
|
AlignmentSize = std::max(AlignmentSize, FieldAlignmentSize);
|
|
return Field;
|
|
}
|
|
|
|
/// The concrete assembly parser instance.
|
|
// Note that this is a full MCAsmParser, not an MCAsmParserExtension!
|
|
// It's a peer of AsmParser, not of COFFAsmParser, WasmAsmParser, etc.
|
|
class MasmParser : public MCAsmParser {
|
|
private:
|
|
AsmLexer Lexer;
|
|
MCContext &Ctx;
|
|
MCStreamer &Out;
|
|
const MCAsmInfo &MAI;
|
|
SourceMgr &SrcMgr;
|
|
SourceMgr::DiagHandlerTy SavedDiagHandler;
|
|
void *SavedDiagContext;
|
|
std::unique_ptr<MCAsmParserExtension> PlatformParser;
|
|
|
|
/// This is the current buffer index we're lexing from as managed by the
|
|
/// SourceMgr object.
|
|
unsigned CurBuffer;
|
|
std::vector<bool> EndStatementAtEOFStack;
|
|
|
|
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;
|
|
|
|
/// maps assembly-time variable names to variables.
|
|
struct Variable {
|
|
enum RedefinableKind { NOT_REDEFINABLE, WARN_ON_REDEFINITION, REDEFINABLE };
|
|
|
|
StringRef Name;
|
|
RedefinableKind Redefinable = REDEFINABLE;
|
|
bool IsText = false;
|
|
int64_t NumericValue = 0;
|
|
std::string TextValue;
|
|
};
|
|
StringMap<Variable> Variables;
|
|
|
|
/// Stack of active struct definitions.
|
|
SmallVector<StructInfo, 1> StructInProgress;
|
|
|
|
/// Maps struct tags to struct definitions.
|
|
StringMap<StructInfo> Structs;
|
|
|
|
/// Maps data location names to types.
|
|
StringMap<AsmTypeInfo> KnownType;
|
|
|
|
/// Stack of active macro instantiations.
|
|
std::vector<MacroInstantiation*> ActiveMacros;
|
|
|
|
/// List of bodies of anonymous macros.
|
|
std::deque<MCAsmMacro> MacroLikeBodies;
|
|
|
|
/// Keeps track of how many .macro's have been instantiated.
|
|
unsigned NumOfMacroInstantiations;
|
|
|
|
/// 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() : Filename(), LineNumber(0), Loc(), Buf(0) {}
|
|
};
|
|
CppHashInfoTy CppHashInfo;
|
|
|
|
/// The filename from the first cpp hash file line comment, if any.
|
|
StringRef FirstCppHashFilename;
|
|
|
|
/// List of forward directional labels for diagnosis at the end.
|
|
SmallVector<std::tuple<SMLoc, CppHashInfoTy, MCSymbol *>, 4> DirLabels;
|
|
|
|
/// AssemblerDialect. ~OU means unset value and use value provided by MAI.
|
|
/// Defaults to 1U, meaning Intel.
|
|
unsigned AssemblerDialect = 1U;
|
|
|
|
/// 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;
|
|
|
|
// Current <...> expression depth.
|
|
unsigned AngleBracketDepth = 0U;
|
|
|
|
// Number of locals defined.
|
|
uint16_t LocalCounter = 0;
|
|
|
|
public:
|
|
MasmParser(SourceMgr &SM, MCContext &Ctx, MCStreamer &Out,
|
|
const MCAsmInfo &MAI, unsigned CB);
|
|
MasmParser(const MasmParser &) = delete;
|
|
MasmParser &operator=(const MasmParser &) = delete;
|
|
~MasmParser() override;
|
|
|
|
bool Run(bool NoInitialTextSection, bool NoFinalize = false) override;
|
|
|
|
void addDirectiveHandler(StringRef Directive,
|
|
ExtensionDirectiveHandler Handler) override {
|
|
ExtensionDirectiveMap[Directive] = Handler;
|
|
if (DirectiveKindMap.find(Directive) == DirectiveKindMap.end()) {
|
|
DirectiveKindMap[Directive] = DK_HANDLER_DIRECTIVE;
|
|
}
|
|
}
|
|
|
|
void addAliasForDirective(StringRef Directive, StringRef Alias) override {
|
|
DirectiveKindMap[Directive] = DirectiveKindMap[Alias];
|
|
}
|
|
|
|
/// @name MCAsmParser Interface
|
|
/// {
|
|
|
|
SourceMgr &getSourceManager() override { return SrcMgr; }
|
|
MCAsmLexer &getLexer() override { return Lexer; }
|
|
MCContext &getContext() override { return Ctx; }
|
|
MCStreamer &getStreamer() override { return Out; }
|
|
|
|
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 = None) override;
|
|
bool Warning(SMLoc L, const Twine &Msg, SMRange Range = None) override;
|
|
bool printError(SMLoc L, const Twine &Msg, SMRange Range = None) 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 isParsingMasm() const override { return true; }
|
|
|
|
bool defineMacro(StringRef Name, StringRef Value) override;
|
|
|
|
bool lookUpField(StringRef Name, AsmFieldInfo &Info) const override;
|
|
bool lookUpField(StringRef Base, StringRef Member,
|
|
AsmFieldInfo &Info) const override;
|
|
|
|
bool lookUpType(StringRef Name, AsmTypeInfo &Info) const override;
|
|
|
|
bool parseMSInlineAsm(void *AsmLoc, std::string &AsmString,
|
|
unsigned &NumOutputs, unsigned &NumInputs,
|
|
SmallVectorImpl<std::pair<void *,bool>> &OpDecls,
|
|
SmallVectorImpl<std::string> &Constraints,
|
|
SmallVectorImpl<std::string> &Clobbers,
|
|
const MCInstrInfo *MII, const 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 parseParenExprOfDepth(unsigned ParenDepth, 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:
|
|
const AsmToken peekTok(bool ShouldSkipSpace = true);
|
|
|
|
bool parseStatement(ParseStatementInfo &Info,
|
|
MCAsmParserSemaCallback *SI);
|
|
bool parseCurlyBlockScope(SmallVectorImpl<AsmRewrite>& AsmStrRewrites);
|
|
bool parseCppHashLineFilenameComment(SMLoc L);
|
|
|
|
bool expandMacro(raw_svector_ostream &OS, StringRef Body,
|
|
ArrayRef<MCAsmMacroParameter> Parameters,
|
|
ArrayRef<MCAsmMacroArgument> A,
|
|
const std::vector<std::string> &Locals, SMLoc L);
|
|
|
|
/// 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(
|
|
const MCAsmMacro *M, SMLoc NameLoc,
|
|
AsmToken::TokenKind ArgumentEndTok = AsmToken::EndOfStatement);
|
|
|
|
/// Handle invocation of macro function.
|
|
///
|
|
/// \param M The macro.
|
|
/// \param NameLoc Invocation location.
|
|
bool handleMacroInvocation(const MCAsmMacro *M, SMLoc NameLoc);
|
|
|
|
/// Handle exit from macro instantiation.
|
|
void handleMacroExit();
|
|
|
|
/// Extract AsmTokens for a macro argument.
|
|
bool
|
|
parseMacroArgument(const MCAsmMacroParameter *MP, MCAsmMacroArgument &MA,
|
|
AsmToken::TokenKind EndTok = AsmToken::EndOfStatement);
|
|
|
|
/// Parse all macro arguments for a given macro.
|
|
bool
|
|
parseMacroArguments(const MCAsmMacro *M, MCAsmMacroArguments &A,
|
|
AsmToken::TokenKind EndTok = AsmToken::EndOfStatement);
|
|
|
|
void printMacroInstantiations();
|
|
|
|
bool expandStatement(SMLoc Loc);
|
|
|
|
void printMessage(SMLoc Loc, SourceMgr::DiagKind Kind, const Twine &Msg,
|
|
SMRange Range = None) const {
|
|
ArrayRef<SMRange> Ranges(Range);
|
|
SrcMgr.PrintMessage(Loc, Kind, Msg, Ranges);
|
|
}
|
|
static void DiagHandler(const SMDiagnostic &Diag, void *Context);
|
|
|
|
bool lookUpField(const StructInfo &Structure, StringRef Member,
|
|
AsmFieldInfo &Info) const;
|
|
|
|
/// 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();
|
|
|
|
/// Enter the specified file. This returns true on failure.
|
|
bool enterIncludeFile(const std::string &Filename);
|
|
|
|
/// 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,
|
|
bool EndStatementAtEOF = true);
|
|
|
|
/// Parse up to a token of kind \p EndTok and return the contents from the
|
|
/// current token up to (but not including) this token; the current token on
|
|
/// exit will be either this kind or EOF. Reads through instantiated macro
|
|
/// functions and text macros.
|
|
SmallVector<StringRef, 1> parseStringRefsTo(AsmToken::TokenKind EndTok);
|
|
std::string parseStringTo(AsmToken::TokenKind EndTok);
|
|
|
|
/// Parse up to the end of statement and 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;
|
|
|
|
bool parseTextItem(std::string &Data);
|
|
|
|
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_HANDLER_DIRECTIVE,
|
|
DK_ASSIGN,
|
|
DK_EQU,
|
|
DK_TEXTEQU,
|
|
DK_ASCII,
|
|
DK_ASCIZ,
|
|
DK_STRING,
|
|
DK_BYTE,
|
|
DK_SBYTE,
|
|
DK_WORD,
|
|
DK_SWORD,
|
|
DK_DWORD,
|
|
DK_SDWORD,
|
|
DK_FWORD,
|
|
DK_QWORD,
|
|
DK_SQWORD,
|
|
DK_DB,
|
|
DK_DD,
|
|
DK_DF,
|
|
DK_DQ,
|
|
DK_DW,
|
|
DK_REAL4,
|
|
DK_REAL8,
|
|
DK_REAL10,
|
|
DK_ALIGN,
|
|
DK_EVEN,
|
|
DK_ORG,
|
|
DK_ENDR,
|
|
DK_EXTERN,
|
|
DK_PUBLIC,
|
|
DK_COMM,
|
|
DK_COMMENT,
|
|
DK_INCLUDE,
|
|
DK_REPEAT,
|
|
DK_WHILE,
|
|
DK_FOR,
|
|
DK_FORC,
|
|
DK_IF,
|
|
DK_IFE,
|
|
DK_IFB,
|
|
DK_IFNB,
|
|
DK_IFDEF,
|
|
DK_IFNDEF,
|
|
DK_IFDIF,
|
|
DK_IFDIFI,
|
|
DK_IFIDN,
|
|
DK_IFIDNI,
|
|
DK_ELSEIF,
|
|
DK_ELSEIFE,
|
|
DK_ELSEIFB,
|
|
DK_ELSEIFNB,
|
|
DK_ELSEIFDEF,
|
|
DK_ELSEIFNDEF,
|
|
DK_ELSEIFDIF,
|
|
DK_ELSEIFDIFI,
|
|
DK_ELSEIFIDN,
|
|
DK_ELSEIFIDNI,
|
|
DK_ELSE,
|
|
DK_ENDIF,
|
|
DK_FILE,
|
|
DK_LINE,
|
|
DK_LOC,
|
|
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_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_B_KEY_FRAME,
|
|
DK_MACRO,
|
|
DK_EXITM,
|
|
DK_ENDM,
|
|
DK_PURGE,
|
|
DK_ERR,
|
|
DK_ERRB,
|
|
DK_ERRNB,
|
|
DK_ERRDEF,
|
|
DK_ERRNDEF,
|
|
DK_ERRDIF,
|
|
DK_ERRDIFI,
|
|
DK_ERRIDN,
|
|
DK_ERRIDNI,
|
|
DK_ERRE,
|
|
DK_ERRNZ,
|
|
DK_ECHO,
|
|
DK_STRUCT,
|
|
DK_UNION,
|
|
DK_ENDS,
|
|
DK_END,
|
|
DK_PUSHFRAME,
|
|
DK_PUSHREG,
|
|
DK_SAVEREG,
|
|
DK_SAVEXMM128,
|
|
DK_SETFRAME,
|
|
DK_RADIX,
|
|
};
|
|
|
|
/// Maps directive name --> DirectiveKind enum, for directives parsed by this
|
|
/// class.
|
|
StringMap<DirectiveKind> DirectiveKindMap;
|
|
|
|
bool isMacroLikeDirective();
|
|
|
|
// 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);
|
|
|
|
// "byte", "word", ...
|
|
bool emitIntValue(const MCExpr *Value, unsigned Size);
|
|
bool parseScalarInitializer(unsigned Size,
|
|
SmallVectorImpl<const MCExpr *> &Values,
|
|
unsigned StringPadLength = 0);
|
|
bool parseScalarInstList(
|
|
unsigned Size, SmallVectorImpl<const MCExpr *> &Values,
|
|
const AsmToken::TokenKind EndToken = AsmToken::EndOfStatement);
|
|
bool emitIntegralValues(unsigned Size, unsigned *Count = nullptr);
|
|
bool addIntegralField(StringRef Name, unsigned Size);
|
|
bool parseDirectiveValue(StringRef IDVal, unsigned Size);
|
|
bool parseDirectiveNamedValue(StringRef TypeName, unsigned Size,
|
|
StringRef Name, SMLoc NameLoc);
|
|
|
|
// "real4", "real8", "real10"
|
|
bool emitRealValues(const fltSemantics &Semantics, unsigned *Count = nullptr);
|
|
bool addRealField(StringRef Name, const fltSemantics &Semantics, size_t Size);
|
|
bool parseDirectiveRealValue(StringRef IDVal, const fltSemantics &Semantics,
|
|
size_t Size);
|
|
bool parseRealInstList(
|
|
const fltSemantics &Semantics, SmallVectorImpl<APInt> &Values,
|
|
const AsmToken::TokenKind EndToken = AsmToken::EndOfStatement);
|
|
bool parseDirectiveNamedRealValue(StringRef TypeName,
|
|
const fltSemantics &Semantics,
|
|
unsigned Size, StringRef Name,
|
|
SMLoc NameLoc);
|
|
|
|
bool parseOptionalAngleBracketOpen();
|
|
bool parseAngleBracketClose(const Twine &Msg = "expected '>'");
|
|
|
|
bool parseFieldInitializer(const FieldInfo &Field,
|
|
FieldInitializer &Initializer);
|
|
bool parseFieldInitializer(const FieldInfo &Field,
|
|
const IntFieldInfo &Contents,
|
|
FieldInitializer &Initializer);
|
|
bool parseFieldInitializer(const FieldInfo &Field,
|
|
const RealFieldInfo &Contents,
|
|
FieldInitializer &Initializer);
|
|
bool parseFieldInitializer(const FieldInfo &Field,
|
|
const StructFieldInfo &Contents,
|
|
FieldInitializer &Initializer);
|
|
|
|
bool parseStructInitializer(const StructInfo &Structure,
|
|
StructInitializer &Initializer);
|
|
bool parseStructInstList(
|
|
const StructInfo &Structure, std::vector<StructInitializer> &Initializers,
|
|
const AsmToken::TokenKind EndToken = AsmToken::EndOfStatement);
|
|
|
|
bool emitFieldValue(const FieldInfo &Field);
|
|
bool emitFieldValue(const FieldInfo &Field, const IntFieldInfo &Contents);
|
|
bool emitFieldValue(const FieldInfo &Field, const RealFieldInfo &Contents);
|
|
bool emitFieldValue(const FieldInfo &Field, const StructFieldInfo &Contents);
|
|
|
|
bool emitFieldInitializer(const FieldInfo &Field,
|
|
const FieldInitializer &Initializer);
|
|
bool emitFieldInitializer(const FieldInfo &Field,
|
|
const IntFieldInfo &Contents,
|
|
const IntFieldInfo &Initializer);
|
|
bool emitFieldInitializer(const FieldInfo &Field,
|
|
const RealFieldInfo &Contents,
|
|
const RealFieldInfo &Initializer);
|
|
bool emitFieldInitializer(const FieldInfo &Field,
|
|
const StructFieldInfo &Contents,
|
|
const StructFieldInfo &Initializer);
|
|
|
|
bool emitStructInitializer(const StructInfo &Structure,
|
|
const StructInitializer &Initializer);
|
|
|
|
// User-defined types (structs, unions):
|
|
bool emitStructValues(const StructInfo &Structure, unsigned *Count = nullptr);
|
|
bool addStructField(StringRef Name, const StructInfo &Structure);
|
|
bool parseDirectiveStructValue(const StructInfo &Structure,
|
|
StringRef Directive, SMLoc DirLoc);
|
|
bool parseDirectiveNamedStructValue(const StructInfo &Structure,
|
|
StringRef Directive, SMLoc DirLoc,
|
|
StringRef Name);
|
|
|
|
// "=", "equ", "textequ"
|
|
bool parseDirectiveEquate(StringRef IDVal, StringRef Name,
|
|
DirectiveKind DirKind, SMLoc NameLoc);
|
|
|
|
bool parseDirectiveOrg(); // "org"
|
|
|
|
bool emitAlignTo(int64_t Alignment);
|
|
bool parseDirectiveAlign(); // "align"
|
|
bool parseDirectiveEven(); // "even"
|
|
|
|
// ".file", ".line", ".loc", ".stabs"
|
|
bool parseDirectiveFile(SMLoc DirectiveLoc);
|
|
bool parseDirectiveLine();
|
|
bool parseDirectiveLoc();
|
|
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();
|
|
bool parseDirectiveCFISections();
|
|
bool parseDirectiveCFIStartProc();
|
|
bool parseDirectiveCFIEndProc();
|
|
bool parseDirectiveCFIDefCfaOffset();
|
|
bool parseDirectiveCFIDefCfa(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFIAdjustCfaOffset();
|
|
bool parseDirectiveCFIDefCfaRegister(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFIOffset(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFIRelOffset(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFIPersonalityOrLsda(bool IsPersonality);
|
|
bool parseDirectiveCFIRememberState();
|
|
bool parseDirectiveCFIRestoreState();
|
|
bool parseDirectiveCFISameValue(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFIRestore(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFIEscape();
|
|
bool parseDirectiveCFIReturnColumn(SMLoc DirectiveLoc);
|
|
bool parseDirectiveCFISignalFrame();
|
|
bool parseDirectiveCFIUndefined(SMLoc DirectiveLoc);
|
|
|
|
// macro directives
|
|
bool parseDirectivePurgeMacro(SMLoc DirectiveLoc);
|
|
bool parseDirectiveExitMacro(SMLoc DirectiveLoc, StringRef Directive,
|
|
std::string &Value);
|
|
bool parseDirectiveEndMacro(StringRef Directive);
|
|
bool parseDirectiveMacro(StringRef Name, SMLoc NameLoc);
|
|
|
|
bool parseDirectiveStruct(StringRef Directive, DirectiveKind DirKind,
|
|
StringRef Name, SMLoc NameLoc);
|
|
bool parseDirectiveNestedStruct(StringRef Directive, DirectiveKind DirKind);
|
|
bool parseDirectiveEnds(StringRef Name, SMLoc NameLoc);
|
|
bool parseDirectiveNestedEnds();
|
|
|
|
/// 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 parseDirectiveComment(SMLoc DirectiveLoc); // "comment"
|
|
|
|
bool parseDirectiveInclude(); // "include"
|
|
|
|
// "if" or "ife"
|
|
bool parseDirectiveIf(SMLoc DirectiveLoc, DirectiveKind DirKind);
|
|
// "ifb" or "ifnb", depending on ExpectBlank.
|
|
bool parseDirectiveIfb(SMLoc DirectiveLoc, bool ExpectBlank);
|
|
// "ifidn", "ifdif", "ifidni", or "ifdifi", depending on ExpectEqual and
|
|
// CaseInsensitive.
|
|
bool parseDirectiveIfidn(SMLoc DirectiveLoc, bool ExpectEqual,
|
|
bool CaseInsensitive);
|
|
// "ifdef" or "ifndef", depending on expect_defined
|
|
bool parseDirectiveIfdef(SMLoc DirectiveLoc, bool expect_defined);
|
|
// "elseif" or "elseife"
|
|
bool parseDirectiveElseIf(SMLoc DirectiveLoc, DirectiveKind DirKind);
|
|
// "elseifb" or "elseifnb", depending on ExpectBlank.
|
|
bool parseDirectiveElseIfb(SMLoc DirectiveLoc, bool ExpectBlank);
|
|
// ".elseifdef" or ".elseifndef", depending on expect_defined
|
|
bool parseDirectiveElseIfdef(SMLoc DirectiveLoc, bool expect_defined);
|
|
// "elseifidn", "elseifdif", "elseifidni", or "elseifdifi", depending on
|
|
// ExpectEqual and CaseInsensitive.
|
|
bool parseDirectiveElseIfidn(SMLoc DirectiveLoc, bool ExpectEqual,
|
|
bool CaseInsensitive);
|
|
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);
|
|
void instantiateMacroLikeBody(MCAsmMacro *M, SMLoc DirectiveLoc,
|
|
SMLoc ExitLoc, raw_svector_ostream &OS);
|
|
bool parseDirectiveRepeat(SMLoc DirectiveLoc, StringRef Directive);
|
|
bool parseDirectiveFor(SMLoc DirectiveLoc, StringRef Directive);
|
|
bool parseDirectiveForc(SMLoc DirectiveLoc, StringRef Directive);
|
|
bool parseDirectiveWhile(SMLoc DirectiveLoc);
|
|
|
|
// "_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"
|
|
bool parseDirectiveError(SMLoc DirectiveLoc);
|
|
// ".errb" or ".errnb", depending on ExpectBlank.
|
|
bool parseDirectiveErrorIfb(SMLoc DirectiveLoc, bool ExpectBlank);
|
|
// ".errdef" or ".errndef", depending on ExpectBlank.
|
|
bool parseDirectiveErrorIfdef(SMLoc DirectiveLoc, bool ExpectDefined);
|
|
// ".erridn", ".errdif", ".erridni", or ".errdifi", depending on ExpectEqual
|
|
// and CaseInsensitive.
|
|
bool parseDirectiveErrorIfidn(SMLoc DirectiveLoc, bool ExpectEqual,
|
|
bool CaseInsensitive);
|
|
// ".erre" or ".errnz", depending on ExpectZero.
|
|
bool parseDirectiveErrorIfe(SMLoc DirectiveLoc, bool ExpectZero);
|
|
|
|
// ".radix"
|
|
bool parseDirectiveRadix(SMLoc DirectiveLoc);
|
|
|
|
// "echo"
|
|
bool parseDirectiveEcho();
|
|
|
|
void initializeDirectiveKindMap();
|
|
void initializeCVDefRangeTypeMap();
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
namespace llvm {
|
|
|
|
extern MCAsmParserExtension *createCOFFMasmParser();
|
|
|
|
} // end namespace llvm
|
|
|
|
enum { DEFAULT_ADDRSPACE = 0 };
|
|
|
|
MasmParser::MasmParser(SourceMgr &SM, MCContext &Ctx, MCStreamer &Out,
|
|
const MCAsmInfo &MAI, unsigned CB = 0)
|
|
: Lexer(MAI), Ctx(Ctx), Out(Out), MAI(MAI), SrcMgr(SM),
|
|
CurBuffer(CB ? CB : SM.getMainFileID()) {
|
|
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());
|
|
EndStatementAtEOFStack.push_back(true);
|
|
|
|
// Initialize the platform / file format parser.
|
|
switch (Ctx.getObjectFileType()) {
|
|
case MCContext::IsCOFF:
|
|
PlatformParser.reset(createCOFFMasmParser());
|
|
break;
|
|
default:
|
|
report_fatal_error("llvm-ml currently supports only COFF output.");
|
|
break;
|
|
}
|
|
|
|
initializeDirectiveKindMap();
|
|
PlatformParser->Initialize(*this);
|
|
initializeCVDefRangeTypeMap();
|
|
|
|
NumOfMacroInstantiations = 0;
|
|
}
|
|
|
|
MasmParser::~MasmParser() {
|
|
assert((HadError || ActiveMacros.empty()) &&
|
|
"Unexpected active macro instantiation!");
|
|
|
|
// Restore the saved diagnostics handler and context for use during
|
|
// finalization.
|
|
SrcMgr.setDiagHandler(SavedDiagHandler, SavedDiagContext);
|
|
}
|
|
|
|
void MasmParser::printMacroInstantiations() {
|
|
// Print the active macro instantiation stack.
|
|
for (std::vector<MacroInstantiation *>::const_reverse_iterator
|
|
it = ActiveMacros.rbegin(),
|
|
ie = ActiveMacros.rend();
|
|
it != ie; ++it)
|
|
printMessage((*it)->InstantiationLoc, SourceMgr::DK_Note,
|
|
"while in macro instantiation");
|
|
}
|
|
|
|
void MasmParser::Note(SMLoc L, const Twine &Msg, SMRange Range) {
|
|
printPendingErrors();
|
|
printMessage(L, SourceMgr::DK_Note, Msg, Range);
|
|
printMacroInstantiations();
|
|
}
|
|
|
|
bool MasmParser::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 MasmParser::printError(SMLoc L, const Twine &Msg, SMRange Range) {
|
|
HadError = true;
|
|
printMessage(L, SourceMgr::DK_Error, Msg, Range);
|
|
printMacroInstantiations();
|
|
return true;
|
|
}
|
|
|
|
bool MasmParser::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());
|
|
EndStatementAtEOFStack.push_back(true);
|
|
return false;
|
|
}
|
|
|
|
void MasmParser::jumpToLoc(SMLoc Loc, unsigned InBuffer,
|
|
bool EndStatementAtEOF) {
|
|
CurBuffer = InBuffer ? InBuffer : SrcMgr.FindBufferContainingLoc(Loc);
|
|
Lexer.setBuffer(SrcMgr.getMemoryBuffer(CurBuffer)->getBuffer(),
|
|
Loc.getPointer(), EndStatementAtEOF);
|
|
}
|
|
|
|
const AsmToken &MasmParser::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();
|
|
bool StartOfStatement = Lexer.isAtStartOfStatement();
|
|
|
|
while (tok->is(AsmToken::Identifier)) {
|
|
if (StartOfStatement) {
|
|
AsmToken NextTok;
|
|
|
|
MutableArrayRef<AsmToken> Buf(NextTok);
|
|
size_t ReadCount = Lexer.peekTokens(Buf);
|
|
if (ReadCount && NextTok.is(AsmToken::Identifier) &&
|
|
(NextTok.getString().equals_insensitive("equ") ||
|
|
NextTok.getString().equals_insensitive("textequ"))) {
|
|
// This looks like an EQU or TEXTEQU directive; don't expand the
|
|
// identifier, allowing for redefinitions.
|
|
break;
|
|
}
|
|
}
|
|
auto it = Variables.find(tok->getIdentifier().lower());
|
|
const llvm::MCAsmMacro *M =
|
|
getContext().lookupMacro(tok->getIdentifier().lower());
|
|
if (it != Variables.end() && it->second.IsText) {
|
|
// This is a textmacro; expand it in place.
|
|
std::unique_ptr<MemoryBuffer> Instantiation =
|
|
MemoryBuffer::getMemBufferCopy(it->second.TextValue,
|
|
"<instantiation>");
|
|
|
|
// Jump to the macro instantiation and prime the lexer.
|
|
CurBuffer = SrcMgr.AddNewSourceBuffer(std::move(Instantiation),
|
|
getTok().getEndLoc());
|
|
Lexer.setBuffer(SrcMgr.getMemoryBuffer(CurBuffer)->getBuffer(), nullptr,
|
|
/*EndStatementAtEOF=*/false);
|
|
EndStatementAtEOFStack.push_back(false);
|
|
tok = &Lexer.Lex();
|
|
} else if (M && M->IsFunction && peekTok().is(AsmToken::LParen)) {
|
|
// This is a macro function invocation; expand it in place.
|
|
const AsmToken MacroTok = *tok;
|
|
tok = &Lexer.Lex();
|
|
if (handleMacroInvocation(M, MacroTok.getLoc())) {
|
|
Lexer.UnLex(AsmToken(AsmToken::Error, MacroTok.getIdentifier()));
|
|
tok = &Lexer.Lex();
|
|
}
|
|
continue;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 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();
|
|
}
|
|
|
|
// Recognize and bypass line continuations.
|
|
while (tok->is(AsmToken::BackSlash) &&
|
|
peekTok().is(AsmToken::EndOfStatement)) {
|
|
// Eat both the backslash and the end of statement.
|
|
Lexer.Lex();
|
|
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()) {
|
|
EndStatementAtEOFStack.pop_back();
|
|
jumpToLoc(ParentIncludeLoc, 0, EndStatementAtEOFStack.back());
|
|
return Lex();
|
|
}
|
|
EndStatementAtEOFStack.pop_back();
|
|
assert(EndStatementAtEOFStack.empty());
|
|
}
|
|
|
|
return *tok;
|
|
}
|
|
|
|
const AsmToken MasmParser::peekTok(bool ShouldSkipSpace) {
|
|
AsmToken Tok;
|
|
|
|
MutableArrayRef<AsmToken> Buf(Tok);
|
|
size_t ReadCount = Lexer.peekTokens(Buf, ShouldSkipSpace);
|
|
|
|
if (ReadCount == 0) {
|
|
// 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()) {
|
|
EndStatementAtEOFStack.pop_back();
|
|
jumpToLoc(ParentIncludeLoc, 0, EndStatementAtEOFStack.back());
|
|
return peekTok(ShouldSkipSpace);
|
|
}
|
|
EndStatementAtEOFStack.pop_back();
|
|
assert(EndStatementAtEOFStack.empty());
|
|
}
|
|
|
|
assert(ReadCount == 1);
|
|
return Tok;
|
|
}
|
|
|
|
bool MasmParser::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) {
|
|
// Use the first #line directive for this, if any. It's preprocessed, so
|
|
// there is no checksum, and of course no source directive.
|
|
if (!FirstCppHashFilename.empty())
|
|
getContext().setMCLineTableRootFile(/*CUID=*/0,
|
|
getContext().getCompilationDir(),
|
|
FirstCppHashFilename,
|
|
/*Cksum=*/None, /*Source=*/None);
|
|
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 MasmParser::Run(bool NoInitialTextSection, bool NoFinalize) {
|
|
// Create the initial section, if requested.
|
|
if (!NoInitialTextSection)
|
|
Out.InitSections(false);
|
|
|
|
// 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) ||
|
|
SrcMgr.getParentIncludeLoc(CurBuffer) != SMLoc()) {
|
|
// Skip through the EOF at the end of an inclusion.
|
|
if (Lexer.is(AsmToken::Eof))
|
|
Lex();
|
|
|
|
ParseStatementInfo Info(&AsmStrRewrites);
|
|
bool Parsed = 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 (Parsed && !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 (Parsed && !getLexer().isAtStartOfStatement())
|
|
eatToEndOfStatement();
|
|
}
|
|
|
|
getTargetParser().onEndOfFile();
|
|
printPendingErrors();
|
|
|
|
// All errors should have been emitted.
|
|
assert(!hasPendingError() && "unexpected error from parseStatement");
|
|
|
|
getTargetParser().flushPendingInstructions(getStreamer());
|
|
|
|
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 necessarilly have seen everything yet.
|
|
if (!NoFinalize) {
|
|
if (MAI.hasSubsectionsViaSymbols()) {
|
|
for (const auto &TableEntry : getContext().getSymbols()) {
|
|
MCSymbol *Sym = TableEntry.getValue();
|
|
// 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->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)
|
|
Out.Finish(Lexer.getLoc());
|
|
|
|
return HadError || getContext().hadError();
|
|
}
|
|
|
|
bool MasmParser::checkForValidSection() {
|
|
if (!ParsingMSInlineAsm && !getStreamer().getCurrentSectionOnly()) {
|
|
Out.InitSections(false);
|
|
return Error(getTok().getLoc(),
|
|
"expected section directive before assembly directive");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Throw away the rest of the line for testing purposes.
|
|
void MasmParser::eatToEndOfStatement() {
|
|
while (Lexer.isNot(AsmToken::EndOfStatement)) {
|
|
if (Lexer.is(AsmToken::Eof)) {
|
|
SMLoc ParentIncludeLoc = SrcMgr.getParentIncludeLoc(CurBuffer);
|
|
if (ParentIncludeLoc == SMLoc()) {
|
|
break;
|
|
}
|
|
|
|
EndStatementAtEOFStack.pop_back();
|
|
jumpToLoc(ParentIncludeLoc, 0, EndStatementAtEOFStack.back());
|
|
}
|
|
|
|
Lexer.Lex();
|
|
}
|
|
|
|
// Eat EOL.
|
|
if (Lexer.is(AsmToken::EndOfStatement))
|
|
Lexer.Lex();
|
|
}
|
|
|
|
SmallVector<StringRef, 1>
|
|
MasmParser::parseStringRefsTo(AsmToken::TokenKind EndTok) {
|
|
SmallVector<StringRef, 1> Refs;
|
|
const char *Start = getTok().getLoc().getPointer();
|
|
while (Lexer.isNot(EndTok)) {
|
|
if (Lexer.is(AsmToken::Eof)) {
|
|
SMLoc ParentIncludeLoc = SrcMgr.getParentIncludeLoc(CurBuffer);
|
|
if (ParentIncludeLoc == SMLoc()) {
|
|
break;
|
|
}
|
|
Refs.emplace_back(Start, getTok().getLoc().getPointer() - Start);
|
|
|
|
EndStatementAtEOFStack.pop_back();
|
|
jumpToLoc(ParentIncludeLoc, 0, EndStatementAtEOFStack.back());
|
|
Lexer.Lex();
|
|
Start = getTok().getLoc().getPointer();
|
|
} else {
|
|
Lexer.Lex();
|
|
}
|
|
}
|
|
Refs.emplace_back(Start, getTok().getLoc().getPointer() - Start);
|
|
return Refs;
|
|
}
|
|
|
|
std::string MasmParser::parseStringTo(AsmToken::TokenKind EndTok) {
|
|
SmallVector<StringRef, 1> Refs = parseStringRefsTo(EndTok);
|
|
std::string Str;
|
|
for (StringRef S : Refs) {
|
|
Str.append(S.str());
|
|
}
|
|
return Str;
|
|
}
|
|
|
|
StringRef MasmParser::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);
|
|
}
|
|
|
|
/// Parse a paren expression and return it.
|
|
/// NOTE: This assumes the leading '(' has already been consumed.
|
|
///
|
|
/// parenexpr ::= expr)
|
|
///
|
|
bool MasmParser::parseParenExpr(const MCExpr *&Res, SMLoc &EndLoc) {
|
|
if (parseExpression(Res))
|
|
return true;
|
|
if (Lexer.isNot(AsmToken::RParen))
|
|
return TokError("expected ')' in parentheses expression");
|
|
EndLoc = Lexer.getTok().getEndLoc();
|
|
Lex();
|
|
return false;
|
|
}
|
|
|
|
/// Parse a bracket expression and return it.
|
|
/// NOTE: This assumes the leading '[' has already been consumed.
|
|
///
|
|
/// bracketexpr ::= expr]
|
|
///
|
|
bool MasmParser::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 ::= ~,+,-,'not' primaryexpr
|
|
/// primaryexpr ::= string
|
|
/// (a string is interpreted as a 64-bit number in big-endian base-256)
|
|
bool MasmParser::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, nullptr))
|
|
return true;
|
|
Res = MCUnaryExpr::createLNot(Res, getContext(), FirstTokenLoc);
|
|
return false;
|
|
case AsmToken::Dollar:
|
|
case AsmToken::At:
|
|
case AsmToken::Identifier: {
|
|
StringRef Identifier;
|
|
if (parseIdentifier(Identifier)) {
|
|
// We may have failed but $ may be a valid token.
|
|
if (getTok().is(AsmToken::Dollar)) {
|
|
if (Lexer.getMAI().getDollarIsPC()) {
|
|
Lex();
|
|
// 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, MCSymbolRefExpr::VK_None,
|
|
getContext());
|
|
EndLoc = FirstTokenLoc;
|
|
return false;
|
|
}
|
|
return Error(FirstTokenLoc, "invalid token in expression");
|
|
}
|
|
}
|
|
// Parse named bitwise negation.
|
|
if (Identifier.equals_insensitive("not")) {
|
|
if (parsePrimaryExpr(Res, EndLoc, nullptr))
|
|
return true;
|
|
Res = MCUnaryExpr::createNot(Res, getContext(), FirstTokenLoc);
|
|
return false;
|
|
}
|
|
// Parse symbol variant.
|
|
std::pair<StringRef, StringRef> Split;
|
|
if (!MAI.useParensForSymbolVariant()) {
|
|
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 {
|
|
Split = Identifier.split('@');
|
|
}
|
|
} else if (Lexer.is(AsmToken::LParen)) {
|
|
Lex(); // eat '('.
|
|
StringRef VName;
|
|
parseIdentifier(VName);
|
|
// eat ')'.
|
|
if (parseToken(AsmToken::RParen,
|
|
"unexpected token in variant, expected ')'"))
|
|
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");
|
|
|
|
MCSymbolRefExpr::VariantKind Variant = MCSymbolRefExpr::VK_None;
|
|
|
|
// Look up the symbol variant if used.
|
|
if (!Split.second.empty()) {
|
|
Variant = MCSymbolRefExpr::getVariantKindForName(Split.second);
|
|
if (Variant != MCSymbolRefExpr::VK_Invalid) {
|
|
SymbolName = Split.first;
|
|
} else if (MAI.doesAllowAtInName() && !MAI.useParensForSymbolVariant()) {
|
|
Variant = MCSymbolRefExpr::VK_None;
|
|
} else {
|
|
return Error(SMLoc::getFromPointer(Split.second.begin()),
|
|
"invalid variant '" + Split.second + "'");
|
|
}
|
|
}
|
|
|
|
// Find the field offset if used.
|
|
AsmFieldInfo Info;
|
|
Split = SymbolName.split('.');
|
|
if (Split.second.empty()) {
|
|
} else {
|
|
SymbolName = Split.first;
|
|
if (lookUpField(SymbolName, Split.second, Info)) {
|
|
std::pair<StringRef, StringRef> BaseMember = Split.second.split('.');
|
|
StringRef Base = BaseMember.first, Member = BaseMember.second;
|
|
lookUpField(Base, Member, Info);
|
|
} else if (Structs.count(SymbolName.lower())) {
|
|
// This is actually a reference to a field offset.
|
|
Res = MCConstantExpr::create(Info.Offset, getContext());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
MCSymbol *Sym = getContext().getInlineAsmLabel(SymbolName);
|
|
if (!Sym) {
|
|
// Variables use case-insensitive symbol names; if this is a variable, we
|
|
// find the symbol using its canonical name.
|
|
auto VarIt = Variables.find(SymbolName.lower());
|
|
if (VarIt != Variables.end())
|
|
SymbolName = VarIt->second.Name;
|
|
Sym = getContext().getOrCreateSymbol(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(/*SetUsed*/ false);
|
|
bool DoInline = isa<MCConstantExpr>(V) && !Variant;
|
|
if (auto TV = dyn_cast<MCTargetExpr>(V))
|
|
DoInline = TV->inlineAssignedExpr();
|
|
if (DoInline) {
|
|
if (Variant)
|
|
return Error(EndLoc, "unexpected modifier on variable reference");
|
|
Res = Sym->getVariableValue(/*SetUsed*/ false);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Otherwise create a symbol ref.
|
|
const MCExpr *SymRef =
|
|
MCSymbolRefExpr::create(Sym, Variant, getContext(), FirstTokenLoc);
|
|
if (Info.Offset) {
|
|
Res = MCBinaryExpr::create(
|
|
MCBinaryExpr::Add, SymRef,
|
|
MCConstantExpr::create(Info.Offset, getContext()), getContext());
|
|
} else {
|
|
Res = SymRef;
|
|
}
|
|
if (TypeInfo) {
|
|
if (Info.Type.Name.empty()) {
|
|
auto TypeIt = KnownType.find(Identifier.lower());
|
|
if (TypeIt != KnownType.end()) {
|
|
Info.Type = TypeIt->second;
|
|
}
|
|
}
|
|
|
|
*TypeInfo = Info.Type;
|
|
}
|
|
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();
|
|
// Look up the symbol variant if used.
|
|
std::pair<StringRef, StringRef> Split = IDVal.split('@');
|
|
MCSymbolRefExpr::VariantKind Variant = MCSymbolRefExpr::VK_None;
|
|
if (Split.first.size() != IDVal.size()) {
|
|
Variant = MCSymbolRefExpr::getVariantKindForName(Split.second);
|
|
if (Variant == MCSymbolRefExpr::VK_Invalid)
|
|
return TokError("invalid variant '" + Split.second + "'");
|
|
IDVal = Split.first;
|
|
}
|
|
if (IDVal == "f" || IDVal == "b") {
|
|
MCSymbol *Sym =
|
|
Ctx.getDirectionalLocalSymbol(IntVal, IDVal == "b");
|
|
Res = MCSymbolRefExpr::create(Sym, Variant, getContext());
|
|
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::String: {
|
|
// MASM strings (used as constants) are interpreted as big-endian base-256.
|
|
SMLoc ValueLoc = getTok().getLoc();
|
|
std::string Value;
|
|
if (parseEscapedString(Value))
|
|
return true;
|
|
if (Value.size() > 8)
|
|
return Error(ValueLoc, "literal value out of range");
|
|
uint64_t IntValue = 0;
|
|
for (const unsigned char CharVal : Value)
|
|
IntValue = (IntValue << 8) | CharVal;
|
|
Res = MCConstantExpr::create(IntValue, getContext());
|
|
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: {
|
|
// 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, MCSymbolRefExpr::VK_None, 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, nullptr))
|
|
return true;
|
|
Res = MCUnaryExpr::createMinus(Res, getContext(), FirstTokenLoc);
|
|
return false;
|
|
case AsmToken::Plus:
|
|
Lex(); // Eat the operator.
|
|
if (parsePrimaryExpr(Res, EndLoc, nullptr))
|
|
return true;
|
|
Res = MCUnaryExpr::createPlus(Res, getContext(), FirstTokenLoc);
|
|
return false;
|
|
case AsmToken::Tilde:
|
|
Lex(); // Eat the operator.
|
|
if (parsePrimaryExpr(Res, EndLoc, nullptr))
|
|
return true;
|
|
Res = MCUnaryExpr::createNot(Res, getContext(), FirstTokenLoc);
|
|
return false;
|
|
// MIPS unary expression operators. The lexer won't generate these tokens if
|
|
// MCAsmInfo::HasMipsExpressions is false for the target.
|
|
case AsmToken::PercentCall16:
|
|
case AsmToken::PercentCall_Hi:
|
|
case AsmToken::PercentCall_Lo:
|
|
case AsmToken::PercentDtprel_Hi:
|
|
case AsmToken::PercentDtprel_Lo:
|
|
case AsmToken::PercentGot:
|
|
case AsmToken::PercentGot_Disp:
|
|
case AsmToken::PercentGot_Hi:
|
|
case AsmToken::PercentGot_Lo:
|
|
case AsmToken::PercentGot_Ofst:
|
|
case AsmToken::PercentGot_Page:
|
|
case AsmToken::PercentGottprel:
|
|
case AsmToken::PercentGp_Rel:
|
|
case AsmToken::PercentHi:
|
|
case AsmToken::PercentHigher:
|
|
case AsmToken::PercentHighest:
|
|
case AsmToken::PercentLo:
|
|
case AsmToken::PercentNeg:
|
|
case AsmToken::PercentPcrel_Hi:
|
|
case AsmToken::PercentPcrel_Lo:
|
|
case AsmToken::PercentTlsgd:
|
|
case AsmToken::PercentTlsldm:
|
|
case AsmToken::PercentTprel_Hi:
|
|
case AsmToken::PercentTprel_Lo:
|
|
Lex(); // Eat the operator.
|
|
if (Lexer.isNot(AsmToken::LParen))
|
|
return TokError("expected '(' after operator");
|
|
Lex(); // Eat the operator.
|
|
if (parseExpression(Res, EndLoc))
|
|
return true;
|
|
if (Lexer.isNot(AsmToken::RParen))
|
|
return TokError("expected ')'");
|
|
Lex(); // Eat the operator.
|
|
Res = getTargetParser().createTargetUnaryExpr(Res, FirstTokenKind, Ctx);
|
|
return !Res;
|
|
}
|
|
}
|
|
|
|
bool MasmParser::parseExpression(const MCExpr *&Res) {
|
|
SMLoc EndLoc;
|
|
return parseExpression(Res, EndLoc);
|
|
}
|
|
|
|
/// 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.
|
|
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 BracketContents) {
|
|
std::string Res;
|
|
for (size_t Pos = 0; Pos < BracketContents.size(); Pos++) {
|
|
if (BracketContents[Pos] == '!')
|
|
Pos++;
|
|
Res += BracketContents[Pos];
|
|
}
|
|
return Res;
|
|
}
|
|
|
|
/// 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 MasmParser::parseExpression(const MCExpr *&Res, SMLoc &EndLoc) {
|
|
// Parse the expression.
|
|
Res = nullptr;
|
|
if (getTargetParser().parsePrimaryExpr(Res, EndLoc) ||
|
|
parseBinOpRHS(1, Res, EndLoc))
|
|
return true;
|
|
|
|
// 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 MasmParser::parseParenExpression(const MCExpr *&Res, SMLoc &EndLoc) {
|
|
Res = nullptr;
|
|
return parseParenExpr(Res, EndLoc) || parseBinOpRHS(1, Res, EndLoc);
|
|
}
|
|
|
|
bool MasmParser::parseParenExprOfDepth(unsigned ParenDepth, const MCExpr *&Res,
|
|
SMLoc &EndLoc) {
|
|
if (parseParenExpr(Res, EndLoc))
|
|
return true;
|
|
|
|
for (; ParenDepth > 0; --ParenDepth) {
|
|
if (parseBinOpRHS(1, Res, EndLoc))
|
|
return true;
|
|
|
|
// We don't Lex() the last RParen.
|
|
// This is the same behavior as parseParenExpression().
|
|
if (ParenDepth - 1 > 0) {
|
|
EndLoc = getTok().getEndLoc();
|
|
if (parseToken(AsmToken::RParen,
|
|
"expected ')' in parentheses expression"))
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::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 getGNUBinOpPrecedence(AsmToken::TokenKind K,
|
|
MCBinaryExpr::Opcode &Kind,
|
|
bool ShouldUseLogicalShr,
|
|
bool EndExpressionAtGreater) {
|
|
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:
|
|
if (EndExpressionAtGreater)
|
|
return 0;
|
|
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::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:
|
|
if (EndExpressionAtGreater)
|
|
return 0;
|
|
Kind = ShouldUseLogicalShr ? MCBinaryExpr::LShr : MCBinaryExpr::AShr;
|
|
return 6;
|
|
}
|
|
}
|
|
|
|
unsigned MasmParser::getBinOpPrecedence(AsmToken::TokenKind K,
|
|
MCBinaryExpr::Opcode &Kind) {
|
|
bool ShouldUseLogicalShr = MAI.shouldUseLogicalShr();
|
|
return getGNUBinOpPrecedence(K, Kind, ShouldUseLogicalShr,
|
|
AngleBracketDepth > 0);
|
|
}
|
|
|
|
/// Parse all binary operators with precedence >= 'Precedence'.
|
|
/// Res contains the LHS of the expression on input.
|
|
bool MasmParser::parseBinOpRHS(unsigned Precedence, const MCExpr *&Res,
|
|
SMLoc &EndLoc) {
|
|
SMLoc StartLoc = Lexer.getLoc();
|
|
while (true) {
|
|
AsmToken::TokenKind TokKind = Lexer.getKind();
|
|
if (Lexer.getKind() == AsmToken::Identifier) {
|
|
TokKind = StringSwitch<AsmToken::TokenKind>(Lexer.getTok().getString())
|
|
.CaseLower("and", AsmToken::Amp)
|
|
.CaseLower("not", AsmToken::Exclaim)
|
|
.CaseLower("or", AsmToken::Pipe)
|
|
.CaseLower("eq", AsmToken::EqualEqual)
|
|
.CaseLower("ne", AsmToken::ExclaimEqual)
|
|
.CaseLower("lt", AsmToken::Less)
|
|
.CaseLower("le", AsmToken::LessEqual)
|
|
.CaseLower("gt", AsmToken::Greater)
|
|
.CaseLower("ge", AsmToken::GreaterEqual)
|
|
.Default(TokKind);
|
|
}
|
|
MCBinaryExpr::Opcode Kind = MCBinaryExpr::Add;
|
|
unsigned TokPrec = getBinOpPrecedence(TokKind, 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:
|
|
/// ::= % statement
|
|
/// ::= EndOfStatement
|
|
/// ::= Label* Directive ...Operands... EndOfStatement
|
|
/// ::= Label* Identifier OperandList* EndOfStatement
|
|
bool MasmParser::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;
|
|
}
|
|
|
|
// If preceded by an expansion operator, first expand all text macros and
|
|
// macro functions.
|
|
if (getTok().is(AsmToken::Percent)) {
|
|
SMLoc ExpansionLoc = getTok().getLoc();
|
|
if (parseToken(AsmToken::Percent) || expandStatement(ExpansionLoc))
|
|
return true;
|
|
}
|
|
|
|
// Statements always start with an identifier, unless we're dealing with a
|
|
// processor directive (.386, .686, etc.) that lexes as a real.
|
|
AsmToken ID = getTok();
|
|
SMLoc IDLoc = ID.getLoc();
|
|
StringRef IDVal;
|
|
int64_t LocalLabelVal = -1;
|
|
if (Lexer.is(AsmToken::HashDirective))
|
|
return parseCppHashLineFilenameComment(IDLoc);
|
|
// 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 (Lexer.is(AsmToken::LCurly)) {
|
|
// Treat '{' as a valid identifier in this context.
|
|
Lex();
|
|
IDVal = "{";
|
|
|
|
} else if (Lexer.is(AsmToken::RCurly)) {
|
|
// Treat '}' as a valid identifier in this context.
|
|
Lex();
|
|
IDVal = "}";
|
|
} else if (Lexer.is(AsmToken::Star) &&
|
|
getTargetParser().starIsStartOfStatement()) {
|
|
// Accept '*' as a valid start of statement.
|
|
Lex();
|
|
IDVal = "*";
|
|
} else if (Lexer.is(AsmToken::Real)) {
|
|
// Treat ".<number>" as a valid identifier in this context.
|
|
IDVal = getTok().getString();
|
|
Lex(); // always eat a token
|
|
if (!IDVal.startswith("."))
|
|
return Error(IDLoc, "unexpected token at start of statement");
|
|
} else if (Lexer.is(AsmToken::Identifier) &&
|
|
getTok().getString().equals_insensitive("echo")) {
|
|
// Intercept echo early to avoid lexical substitution in its message, and
|
|
// delegate all handling to the appropriate function.
|
|
return parseDirectiveEcho();
|
|
} 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_IFE:
|
|
return parseDirectiveIf(IDLoc, DirKind);
|
|
case DK_IFB:
|
|
return parseDirectiveIfb(IDLoc, true);
|
|
case DK_IFNB:
|
|
return parseDirectiveIfb(IDLoc, false);
|
|
case DK_IFDEF:
|
|
return parseDirectiveIfdef(IDLoc, true);
|
|
case DK_IFNDEF:
|
|
return parseDirectiveIfdef(IDLoc, false);
|
|
case DK_IFDIF:
|
|
return parseDirectiveIfidn(IDLoc, /*ExpectEqual=*/false,
|
|
/*CaseInsensitive=*/false);
|
|
case DK_IFDIFI:
|
|
return parseDirectiveIfidn(IDLoc, /*ExpectEqual=*/false,
|
|
/*CaseInsensitive=*/true);
|
|
case DK_IFIDN:
|
|
return parseDirectiveIfidn(IDLoc, /*ExpectEqual=*/true,
|
|
/*CaseInsensitive=*/false);
|
|
case DK_IFIDNI:
|
|
return parseDirectiveIfidn(IDLoc, /*ExpectEqual=*/true,
|
|
/*CaseInsensitive=*/true);
|
|
case DK_ELSEIF:
|
|
case DK_ELSEIFE:
|
|
return parseDirectiveElseIf(IDLoc, DirKind);
|
|
case DK_ELSEIFB:
|
|
return parseDirectiveElseIfb(IDLoc, true);
|
|
case DK_ELSEIFNB:
|
|
return parseDirectiveElseIfb(IDLoc, false);
|
|
case DK_ELSEIFDEF:
|
|
return parseDirectiveElseIfdef(IDLoc, true);
|
|
case DK_ELSEIFNDEF:
|
|
return parseDirectiveElseIfdef(IDLoc, false);
|
|
case DK_ELSEIFDIF:
|
|
return parseDirectiveElseIfidn(IDLoc, /*ExpectEqual=*/false,
|
|
/*CaseInsensitive=*/false);
|
|
case DK_ELSEIFDIFI:
|
|
return parseDirectiveElseIfidn(IDLoc, /*ExpectEqual=*/false,
|
|
/*CaseInsensitive=*/true);
|
|
case DK_ELSEIFIDN:
|
|
return parseDirectiveElseIfidn(IDLoc, /*ExpectEqual=*/true,
|
|
/*CaseInsensitive=*/false);
|
|
case DK_ELSEIFIDNI:
|
|
return parseDirectiveElseIfidn(IDLoc, /*ExpectEqual=*/true,
|
|
/*CaseInsensitive=*/true);
|
|
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?
|
|
|
|
// See what kind of statement we have.
|
|
switch (Lexer.getKind()) {
|
|
case AsmToken::Colon: {
|
|
if (!getTargetParser().isLabel(ID))
|
|
break;
|
|
if (checkForValidSection())
|
|
return true;
|
|
|
|
// identifier ':' -> Label.
|
|
Lex();
|
|
|
|
// 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)) {
|
|
std::string CommentStr = parseStringTo(AsmToken::EndOfStatement);
|
|
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();
|
|
}
|
|
|
|
getTargetParser().doBeforeLabelEmit(Sym);
|
|
|
|
// 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;
|
|
}
|
|
|
|
default: // Normal instruction or directive.
|
|
break;
|
|
}
|
|
|
|
// If macros are enabled, check to see if this is a macro instantiation.
|
|
if (const MCAsmMacro *M = getContext().lookupMacro(IDVal.lower())) {
|
|
return handleMacroEntry(M, IDLoc);
|
|
}
|
|
|
|
// Otherwise, we have a normal instruction or directive.
|
|
|
|
if (DirKind != DK_NO_DIRECTIVE) {
|
|
// There are several entities interested in parsing directives:
|
|
//
|
|
// 1. Asm parser extensions. For example, platform-specific parsers
|
|
// (like the ELF parser) register themselves as extensions.
|
|
// 2. The target-specific assembly parser. Some directives are target
|
|
// specific or may potentially behave differently on certain targets.
|
|
// 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());
|
|
|
|
// Special-case handling of structure-end directives at higher priority,
|
|
// since ENDS is overloaded as a segment-end directive.
|
|
if (IDVal.equals_insensitive("ends") && StructInProgress.size() > 1 &&
|
|
getTok().is(AsmToken::EndOfStatement)) {
|
|
return parseDirectiveNestedEnds();
|
|
}
|
|
|
|
// First, 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.lower());
|
|
if (Handler.first)
|
|
return (*Handler.second)(Handler.first, IDVal, IDLoc);
|
|
|
|
// Next, let the target-specific assembly parser try.
|
|
SMLoc StartTokLoc = getTok().getLoc();
|
|
bool TPDirectiveReturn =
|
|
ID.is(AsmToken::Identifier) && getTargetParser().ParseDirective(ID);
|
|
|
|
if (hasPendingError())
|
|
return true;
|
|
// Currently the return value should be true if we are
|
|
// uninterested but as this is at odds with the standard parsing
|
|
// convention (return true = error) we have instances of a parsed
|
|
// directive that fails returning true as an error. Catch these
|
|
// cases as best as possible errors here.
|
|
if (TPDirectiveReturn && StartTokLoc != getTok().getLoc())
|
|
return true;
|
|
// Return if we did some parsing or believe we succeeded.
|
|
if (!TPDirectiveReturn || StartTokLoc != getTok().getLoc())
|
|
return false;
|
|
|
|
// 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_ASCII:
|
|
return parseDirectiveAscii(IDVal, false);
|
|
case DK_ASCIZ:
|
|
case DK_STRING:
|
|
return parseDirectiveAscii(IDVal, true);
|
|
case DK_BYTE:
|
|
case DK_SBYTE:
|
|
case DK_DB:
|
|
return parseDirectiveValue(IDVal, 1);
|
|
case DK_WORD:
|
|
case DK_SWORD:
|
|
case DK_DW:
|
|
return parseDirectiveValue(IDVal, 2);
|
|
case DK_DWORD:
|
|
case DK_SDWORD:
|
|
case DK_DD:
|
|
return parseDirectiveValue(IDVal, 4);
|
|
case DK_FWORD:
|
|
case DK_DF:
|
|
return parseDirectiveValue(IDVal, 6);
|
|
case DK_QWORD:
|
|
case DK_SQWORD:
|
|
case DK_DQ:
|
|
return parseDirectiveValue(IDVal, 8);
|
|
case DK_REAL4:
|
|
return parseDirectiveRealValue(IDVal, APFloat::IEEEsingle(), 4);
|
|
case DK_REAL8:
|
|
return parseDirectiveRealValue(IDVal, APFloat::IEEEdouble(), 8);
|
|
case DK_REAL10:
|
|
return parseDirectiveRealValue(IDVal, APFloat::x87DoubleExtended(), 10);
|
|
case DK_STRUCT:
|
|
case DK_UNION:
|
|
return parseDirectiveNestedStruct(IDVal, DirKind);
|
|
case DK_ENDS:
|
|
return parseDirectiveNestedEnds();
|
|
case DK_ALIGN:
|
|
return parseDirectiveAlign();
|
|
case DK_EVEN:
|
|
return parseDirectiveEven();
|
|
case DK_ORG:
|
|
return parseDirectiveOrg();
|
|
case DK_EXTERN:
|
|
eatToEndOfStatement(); // .extern is the default, ignore it.
|
|
return false;
|
|
case DK_PUBLIC:
|
|
return parseDirectiveSymbolAttribute(MCSA_Global);
|
|
case DK_COMM:
|
|
return parseDirectiveComm(/*IsLocal=*/false);
|
|
case DK_COMMENT:
|
|
return parseDirectiveComment(IDLoc);
|
|
case DK_INCLUDE:
|
|
return parseDirectiveInclude();
|
|
case DK_REPEAT:
|
|
return parseDirectiveRepeat(IDLoc, IDVal);
|
|
case DK_WHILE:
|
|
return parseDirectiveWhile(IDLoc);
|
|
case DK_FOR:
|
|
return parseDirectiveFor(IDLoc, IDVal);
|
|
case DK_FORC:
|
|
return parseDirectiveForc(IDLoc, IDVal);
|
|
case DK_FILE:
|
|
return parseDirectiveFile(IDLoc);
|
|
case DK_LINE:
|
|
return parseDirectiveLine();
|
|
case DK_LOC:
|
|
return parseDirectiveLoc();
|
|
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();
|
|
case DK_CFI_ADJUST_CFA_OFFSET:
|
|
return parseDirectiveCFIAdjustCfaOffset();
|
|
case DK_CFI_DEF_CFA_REGISTER:
|
|
return parseDirectiveCFIDefCfaRegister(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();
|
|
case DK_CFI_RESTORE_STATE:
|
|
return parseDirectiveCFIRestoreState();
|
|
case DK_CFI_SAME_VALUE:
|
|
return parseDirectiveCFISameValue(IDLoc);
|
|
case DK_CFI_RESTORE:
|
|
return parseDirectiveCFIRestore(IDLoc);
|
|
case DK_CFI_ESCAPE:
|
|
return parseDirectiveCFIEscape();
|
|
case DK_CFI_RETURN_COLUMN:
|
|
return parseDirectiveCFIReturnColumn(IDLoc);
|
|
case DK_CFI_SIGNAL_FRAME:
|
|
return parseDirectiveCFISignalFrame();
|
|
case DK_CFI_UNDEFINED:
|
|
return parseDirectiveCFIUndefined(IDLoc);
|
|
case DK_CFI_REGISTER:
|
|
return parseDirectiveCFIRegister(IDLoc);
|
|
case DK_CFI_WINDOW_SAVE:
|
|
return parseDirectiveCFIWindowSave();
|
|
case DK_EXITM:
|
|
Info.ExitValue = "";
|
|
return parseDirectiveExitMacro(IDLoc, IDVal, *Info.ExitValue);
|
|
case DK_ENDM:
|
|
Info.ExitValue = "";
|
|
return parseDirectiveEndMacro(IDVal);
|
|
case DK_PURGE:
|
|
return parseDirectivePurgeMacro(IDLoc);
|
|
case DK_END:
|
|
return parseDirectiveEnd(IDLoc);
|
|
case DK_ERR:
|
|
return parseDirectiveError(IDLoc);
|
|
case DK_ERRB:
|
|
return parseDirectiveErrorIfb(IDLoc, true);
|
|
case DK_ERRNB:
|
|
return parseDirectiveErrorIfb(IDLoc, false);
|
|
case DK_ERRDEF:
|
|
return parseDirectiveErrorIfdef(IDLoc, true);
|
|
case DK_ERRNDEF:
|
|
return parseDirectiveErrorIfdef(IDLoc, false);
|
|
case DK_ERRDIF:
|
|
return parseDirectiveErrorIfidn(IDLoc, /*ExpectEqual=*/false,
|
|
/*CaseInsensitive=*/false);
|
|
case DK_ERRDIFI:
|
|
return parseDirectiveErrorIfidn(IDLoc, /*ExpectEqual=*/false,
|
|
/*CaseInsensitive=*/true);
|
|
case DK_ERRIDN:
|
|
return parseDirectiveErrorIfidn(IDLoc, /*ExpectEqual=*/true,
|
|
/*CaseInsensitive=*/false);
|
|
case DK_ERRIDNI:
|
|
return parseDirectiveErrorIfidn(IDLoc, /*ExpectEqual=*/true,
|
|
/*CaseInsensitive=*/true);
|
|
case DK_ERRE:
|
|
return parseDirectiveErrorIfe(IDLoc, true);
|
|
case DK_ERRNZ:
|
|
return parseDirectiveErrorIfe(IDLoc, false);
|
|
case DK_RADIX:
|
|
return parseDirectiveRadix(IDLoc);
|
|
}
|
|
|
|
return Error(IDLoc, "unknown directive");
|
|
}
|
|
|
|
// We also check if this is allocating memory with user-defined type.
|
|
auto IDIt = Structs.find(IDVal.lower());
|
|
if (IDIt != Structs.end())
|
|
return parseDirectiveStructValue(/*Structure=*/IDIt->getValue(), IDVal,
|
|
IDLoc);
|
|
|
|
// Non-conditional Microsoft directives sometimes follow their first argument.
|
|
const AsmToken nextTok = getTok();
|
|
const StringRef nextVal = nextTok.getString();
|
|
const SMLoc nextLoc = nextTok.getLoc();
|
|
|
|
const AsmToken afterNextTok = peekTok();
|
|
|
|
// There are several entities interested in parsing infix directives:
|
|
//
|
|
// 1. Asm parser extensions. For example, platform-specific parsers
|
|
// (like the ELF parser) register themselves as extensions.
|
|
// 2. 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());
|
|
|
|
// Special-case handling of structure-end directives at higher priority, since
|
|
// ENDS is overloaded as a segment-end directive.
|
|
if (nextVal.equals_insensitive("ends") && StructInProgress.size() == 1) {
|
|
Lex();
|
|
return parseDirectiveEnds(IDVal, IDLoc);
|
|
}
|
|
|
|
// First, check the extension directive map to see if any extension has
|
|
// registered itself to parse this directive.
|
|
std::pair<MCAsmParserExtension *, DirectiveHandler> Handler =
|
|
ExtensionDirectiveMap.lookup(nextVal.lower());
|
|
if (Handler.first) {
|
|
Lex();
|
|
Lexer.UnLex(ID);
|
|
return (*Handler.second)(Handler.first, nextVal, nextLoc);
|
|
}
|
|
|
|
// If no one else is interested in this directive, it must be
|
|
// generic and familiar to this class.
|
|
DirKindIt = DirectiveKindMap.find(nextVal.lower());
|
|
DirKind = (DirKindIt == DirectiveKindMap.end())
|
|
? DK_NO_DIRECTIVE
|
|
: DirKindIt->getValue();
|
|
switch (DirKind) {
|
|
default:
|
|
break;
|
|
case DK_ASSIGN:
|
|
case DK_EQU:
|
|
case DK_TEXTEQU:
|
|
Lex();
|
|
return parseDirectiveEquate(nextVal, IDVal, DirKind, IDLoc);
|
|
case DK_BYTE:
|
|
if (afterNextTok.is(AsmToken::Identifier) &&
|
|
afterNextTok.getString().equals_insensitive("ptr")) {
|
|
// Size directive; part of an instruction.
|
|
break;
|
|
}
|
|
LLVM_FALLTHROUGH;
|
|
case DK_SBYTE:
|
|
case DK_DB:
|
|
Lex();
|
|
return parseDirectiveNamedValue(nextVal, 1, IDVal, IDLoc);
|
|
case DK_WORD:
|
|
if (afterNextTok.is(AsmToken::Identifier) &&
|
|
afterNextTok.getString().equals_insensitive("ptr")) {
|
|
// Size directive; part of an instruction.
|
|
break;
|
|
}
|
|
LLVM_FALLTHROUGH;
|
|
case DK_SWORD:
|
|
case DK_DW:
|
|
Lex();
|
|
return parseDirectiveNamedValue(nextVal, 2, IDVal, IDLoc);
|
|
case DK_DWORD:
|
|
if (afterNextTok.is(AsmToken::Identifier) &&
|
|
afterNextTok.getString().equals_insensitive("ptr")) {
|
|
// Size directive; part of an instruction.
|
|
break;
|
|
}
|
|
LLVM_FALLTHROUGH;
|
|
case DK_SDWORD:
|
|
case DK_DD:
|
|
Lex();
|
|
return parseDirectiveNamedValue(nextVal, 4, IDVal, IDLoc);
|
|
case DK_FWORD:
|
|
if (afterNextTok.is(AsmToken::Identifier) &&
|
|
afterNextTok.getString().equals_insensitive("ptr")) {
|
|
// Size directive; part of an instruction.
|
|
break;
|
|
}
|
|
LLVM_FALLTHROUGH;
|
|
case DK_DF:
|
|
Lex();
|
|
return parseDirectiveNamedValue(nextVal, 6, IDVal, IDLoc);
|
|
case DK_QWORD:
|
|
if (afterNextTok.is(AsmToken::Identifier) &&
|
|
afterNextTok.getString().equals_insensitive("ptr")) {
|
|
// Size directive; part of an instruction.
|
|
break;
|
|
}
|
|
LLVM_FALLTHROUGH;
|
|
case DK_SQWORD:
|
|
case DK_DQ:
|
|
Lex();
|
|
return parseDirectiveNamedValue(nextVal, 8, IDVal, IDLoc);
|
|
case DK_REAL4:
|
|
Lex();
|
|
return parseDirectiveNamedRealValue(nextVal, APFloat::IEEEsingle(), 4,
|
|
IDVal, IDLoc);
|
|
case DK_REAL8:
|
|
Lex();
|
|
return parseDirectiveNamedRealValue(nextVal, APFloat::IEEEdouble(), 8,
|
|
IDVal, IDLoc);
|
|
case DK_REAL10:
|
|
Lex();
|
|
return parseDirectiveNamedRealValue(nextVal, APFloat::x87DoubleExtended(),
|
|
10, IDVal, IDLoc);
|
|
case DK_STRUCT:
|
|
case DK_UNION:
|
|
Lex();
|
|
return parseDirectiveStruct(nextVal, DirKind, IDVal, IDLoc);
|
|
case DK_ENDS:
|
|
Lex();
|
|
return parseDirectiveEnds(IDVal, IDLoc);
|
|
case DK_MACRO:
|
|
Lex();
|
|
return parseDirectiveMacro(IDVal, IDLoc);
|
|
}
|
|
|
|
// Finally, we check if this is allocating a variable with user-defined type.
|
|
auto NextIt = Structs.find(nextVal.lower());
|
|
if (NextIt != Structs.end()) {
|
|
Lex();
|
|
return parseDirectiveNamedStructValue(/*Structure=*/NextIt->getValue(),
|
|
nextVal, nextLoc, IDVal);
|
|
}
|
|
|
|
// __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;
|
|
|
|
// 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);
|
|
}
|
|
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 MasmParser::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 MasmParser::parseCppHashLineFilenameComment(SMLoc L) {
|
|
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();
|
|
|
|
// 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 (FirstCppHashFilename.empty())
|
|
FirstCppHashFilename = Filename;
|
|
return false;
|
|
}
|
|
|
|
/// will use the last parsed cpp hash line filename comment
|
|
/// for the Filename and LineNo if any in the diagnostic.
|
|
void MasmParser::DiagHandler(const SMDiagnostic &Diag, void *Context) {
|
|
const MasmParser *Parser = static_cast<const MasmParser *>(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 || &DiagSrcMgr != &Parser->SrcMgr ||
|
|
DiagBuf != CppHashBuf) {
|
|
if (Parser->SavedDiagHandler)
|
|
Parser->SavedDiagHandler(Diag, Parser->SavedDiagContext);
|
|
else
|
|
Diag.print(nullptr, OS);
|
|
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(NewDiag, Parser->SavedDiagContext);
|
|
else
|
|
NewDiag.print(nullptr, OS);
|
|
}
|
|
|
|
// This is similar to the IsIdentifierChar function in AsmLexer.cpp, but does
|
|
// not accept '.'.
|
|
static bool isMacroParameterChar(char C) {
|
|
return isAlnum(C) || C == '_' || C == '$' || C == '@' || C == '?';
|
|
}
|
|
|
|
bool MasmParser::expandMacro(raw_svector_ostream &OS, StringRef Body,
|
|
ArrayRef<MCAsmMacroParameter> Parameters,
|
|
ArrayRef<MCAsmMacroArgument> A,
|
|
const std::vector<std::string> &Locals, SMLoc L) {
|
|
unsigned NParameters = Parameters.size();
|
|
if (NParameters != A.size())
|
|
return Error(L, "Wrong number of arguments");
|
|
StringMap<std::string> LocalSymbols;
|
|
std::string Name;
|
|
Name.reserve(6);
|
|
for (StringRef Local : Locals) {
|
|
raw_string_ostream LocalName(Name);
|
|
LocalName << "??"
|
|
<< format_hex_no_prefix(LocalCounter++, 4, /*Upper=*/true);
|
|
LocalSymbols.insert({Local, LocalName.str()});
|
|
Name.clear();
|
|
}
|
|
|
|
Optional<char> CurrentQuote;
|
|
while (!Body.empty()) {
|
|
// Scan for the next substitution.
|
|
std::size_t End = Body.size(), Pos = 0;
|
|
std::size_t IdentifierPos = End;
|
|
for (; Pos != End; ++Pos) {
|
|
// Find the next possible macro parameter, including preceding a '&'
|
|
// inside quotes.
|
|
if (Body[Pos] == '&')
|
|
break;
|
|
if (isMacroParameterChar(Body[Pos])) {
|
|
if (!CurrentQuote.hasValue())
|
|
break;
|
|
if (IdentifierPos == End)
|
|
IdentifierPos = Pos;
|
|
} else {
|
|
IdentifierPos = End;
|
|
}
|
|
|
|
// Track quotation status
|
|
if (!CurrentQuote.hasValue()) {
|
|
if (Body[Pos] == '\'' || Body[Pos] == '"')
|
|
CurrentQuote = Body[Pos];
|
|
} else if (Body[Pos] == CurrentQuote) {
|
|
if (Pos + 1 != End && Body[Pos + 1] == CurrentQuote) {
|
|
// Escaped quote, and quotes aren't identifier chars; skip
|
|
++Pos;
|
|
continue;
|
|
} else {
|
|
CurrentQuote.reset();
|
|
}
|
|
}
|
|
}
|
|
if (IdentifierPos != End) {
|
|
// We've recognized an identifier before an apostrophe inside quotes;
|
|
// check once to see if we can expand it.
|
|
Pos = IdentifierPos;
|
|
IdentifierPos = End;
|
|
}
|
|
|
|
// Add the prefix.
|
|
OS << Body.slice(0, Pos);
|
|
|
|
// Check if we reached the end.
|
|
if (Pos == End)
|
|
break;
|
|
|
|
unsigned I = Pos;
|
|
bool InitialAmpersand = (Body[I] == '&');
|
|
if (InitialAmpersand) {
|
|
++I;
|
|
++Pos;
|
|
}
|
|
while (I < End && isMacroParameterChar(Body[I]))
|
|
++I;
|
|
|
|
const char *Begin = Body.data() + Pos;
|
|
StringRef Argument(Begin, I - Pos);
|
|
unsigned Index = 0;
|
|
|
|
for (; Index < NParameters; ++Index)
|
|
if (Parameters[Index].Name == Argument)
|
|
break;
|
|
|
|
if (Index == NParameters) {
|
|
if (InitialAmpersand)
|
|
OS << '&';
|
|
auto it = LocalSymbols.find(Argument.lower());
|
|
if (it != LocalSymbols.end())
|
|
OS << it->second;
|
|
else
|
|
OS << Argument;
|
|
Pos = I;
|
|
} else {
|
|
for (const AsmToken &Token : A[Index]) {
|
|
// In MASM, 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 (Token.getString().front() == '%' && Token.is(AsmToken::Integer))
|
|
// Emit an integer value to the buffer.
|
|
OS << Token.getIntVal();
|
|
else
|
|
OS << Token.getString();
|
|
}
|
|
|
|
Pos += Argument.size();
|
|
if (Pos < End && Body[Pos] == '&') {
|
|
++Pos;
|
|
}
|
|
}
|
|
// Update the scan point.
|
|
Body = Body.substr(Pos);
|
|
}
|
|
|
|
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 MasmParser::parseMacroArgument(const MCAsmMacroParameter *MP,
|
|
MCAsmMacroArgument &MA,
|
|
AsmToken::TokenKind EndTok) {
|
|
if (MP && MP->Vararg) {
|
|
if (Lexer.isNot(EndTok)) {
|
|
SmallVector<StringRef, 1> Str = parseStringRefsTo(EndTok);
|
|
for (StringRef S : Str) {
|
|
MA.emplace_back(AsmToken::String, S);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
SMLoc StrLoc = Lexer.getLoc(), EndLoc;
|
|
if (Lexer.is(AsmToken::Less) && isAngleBracketString(StrLoc, EndLoc)) {
|
|
const char *StrChar = StrLoc.getPointer() + 1;
|
|
const char *EndChar = EndLoc.getPointer() - 1;
|
|
jumpToLoc(EndLoc, CurBuffer, EndStatementAtEOFStack.back());
|
|
/// Eat from '<' to '>'.
|
|
Lex();
|
|
MA.emplace_back(AsmToken::String, StringRef(StrChar, EndChar - StrChar));
|
|
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");
|
|
|
|
if (ParenLevel == 0) {
|
|
if (Lexer.is(AsmToken::Comma))
|
|
break;
|
|
|
|
if (Lexer.is(AsmToken::Space)) {
|
|
SpaceEaten = true;
|
|
Lex(); // Eat spaces.
|
|
}
|
|
|
|
// 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()) && Lexer.isNot(EndTok)) {
|
|
MA.push_back(getTok());
|
|
Lex();
|
|
|
|
// Whitespace after an operator can be ignored.
|
|
if (Lexer.is(AsmToken::Space))
|
|
Lex();
|
|
|
|
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(EndTok) && (EndTok != AsmToken::RParen || ParenLevel == 0))
|
|
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());
|
|
Lex();
|
|
}
|
|
|
|
if (ParenLevel != 0)
|
|
return TokError("unbalanced parentheses in argument");
|
|
|
|
if (MA.empty() && MP) {
|
|
if (MP->Required) {
|
|
return TokError("missing value for required parameter '" + MP->Name +
|
|
"'");
|
|
} else {
|
|
MA = MP->Value;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Parse the macro instantiation arguments.
|
|
bool MasmParser::parseMacroArguments(const MCAsmMacro *M,
|
|
MCAsmMacroArguments &A,
|
|
AsmToken::TokenKind EndTok) {
|
|
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
|
|
for (unsigned Parameter = 0; !NParameters || Parameter < NParameters;
|
|
++Parameter) {
|
|
SMLoc IDLoc = Lexer.getLoc();
|
|
MCAsmMacroParameter FA;
|
|
|
|
if (Lexer.is(AsmToken::Identifier) && 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;
|
|
}
|
|
|
|
if (NamedParametersFound && FA.Name.empty())
|
|
return Error(IDLoc, "cannot mix positional and keyword arguments");
|
|
|
|
unsigned PI = Parameter;
|
|
if (!FA.Name.empty()) {
|
|
assert(M && "expected macro to be defined");
|
|
unsigned FAI = 0;
|
|
for (FAI = 0; FAI < NParameters; ++FAI)
|
|
if (M->Parameters[FAI].Name == FA.Name)
|
|
break;
|
|
|
|
if (FAI >= NParameters) {
|
|
return Error(IDLoc, "parameter named '" + FA.Name +
|
|
"' does not exist for macro '" + M->Name + "'");
|
|
}
|
|
PI = FAI;
|
|
}
|
|
const MCAsmMacroParameter *MP = nullptr;
|
|
if (M && PI < NParameters)
|
|
MP = &M->Parameters[PI];
|
|
|
|
SMLoc StrLoc = Lexer.getLoc();
|
|
SMLoc EndLoc;
|
|
if (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 (parseMacroArgument(MP, FA.Value, EndTok)) {
|
|
if (M)
|
|
return addErrorSuffix(" in '" + M->Name + "' macro");
|
|
else
|
|
return true;
|
|
}
|
|
|
|
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(EndTok)) {
|
|
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;
|
|
}
|
|
|
|
if (Lexer.is(AsmToken::Comma))
|
|
Lex();
|
|
}
|
|
|
|
return TokError("too many positional arguments");
|
|
}
|
|
|
|
bool MasmParser::handleMacroEntry(const MCAsmMacro *M, SMLoc NameLoc,
|
|
AsmToken::TokenKind ArgumentEndTok) {
|
|
// 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, ArgumentEndTok))
|
|
return true;
|
|
|
|
// Macro instantiation is lexical, unfortunately. We construct a new buffer
|
|
// to hold the macro body with substitutions.
|
|
SmallString<256> Buf;
|
|
StringRef Body = M->Body;
|
|
raw_svector_ostream OS(Buf);
|
|
|
|
if (expandMacro(OS, Body, M->Parameters, A, M->Locals, getTok().getLoc()))
|
|
return true;
|
|
|
|
// We include the endm in the buffer as our cue to exit the macro
|
|
// instantiation.
|
|
OS << "endm\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());
|
|
EndStatementAtEOFStack.push_back(true);
|
|
Lex();
|
|
|
|
return false;
|
|
}
|
|
|
|
void MasmParser::handleMacroExit() {
|
|
// Jump to the token we should return to, and consume it.
|
|
EndStatementAtEOFStack.pop_back();
|
|
jumpToLoc(ActiveMacros.back()->ExitLoc, ActiveMacros.back()->ExitBuffer,
|
|
EndStatementAtEOFStack.back());
|
|
Lex();
|
|
|
|
// Pop the instantiation entry.
|
|
delete ActiveMacros.back();
|
|
ActiveMacros.pop_back();
|
|
}
|
|
|
|
bool MasmParser::handleMacroInvocation(const MCAsmMacro *M, SMLoc NameLoc) {
|
|
if (!M->IsFunction)
|
|
return Error(NameLoc, "cannot invoke macro procedure as function");
|
|
|
|
if (parseToken(AsmToken::LParen, "invoking macro function '" + M->Name +
|
|
"' requires arguments in parentheses") ||
|
|
handleMacroEntry(M, NameLoc, AsmToken::RParen))
|
|
return true;
|
|
|
|
// Parse all statements in the macro, retrieving the exit value when it ends.
|
|
std::string ExitValue;
|
|
SmallVector<AsmRewrite, 4> AsmStrRewrites;
|
|
while (Lexer.isNot(AsmToken::Eof)) {
|
|
ParseStatementInfo Info(&AsmStrRewrites);
|
|
bool Parsed = parseStatement(Info, nullptr);
|
|
|
|
if (!Parsed && Info.ExitValue.hasValue()) {
|
|
ExitValue = std::move(*Info.ExitValue);
|
|
break;
|
|
}
|
|
|
|
// 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 (Parsed && !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 (Parsed && !getLexer().isAtStartOfStatement())
|
|
eatToEndOfStatement();
|
|
}
|
|
|
|
// Consume the right-parenthesis on the other side of the arguments.
|
|
if (parseToken(AsmToken::RParen, "invoking macro function '" + M->Name +
|
|
"' requires arguments in parentheses"))
|
|
return true;
|
|
|
|
// Exit values may require lexing, unfortunately. We construct a new buffer to
|
|
// hold the exit value.
|
|
std::unique_ptr<MemoryBuffer> MacroValue =
|
|
MemoryBuffer::getMemBufferCopy(ExitValue, "<macro-value>");
|
|
|
|
// Jump from this location to the instantiated exit value, and prime the
|
|
// lexer.
|
|
CurBuffer = SrcMgr.AddNewSourceBuffer(std::move(MacroValue), Lexer.getLoc());
|
|
Lexer.setBuffer(SrcMgr.getMemoryBuffer(CurBuffer)->getBuffer(), nullptr,
|
|
/*EndStatementAtEOF=*/false);
|
|
EndStatementAtEOFStack.push_back(false);
|
|
Lex();
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseIdentifier:
|
|
/// ::= identifier
|
|
/// ::= string
|
|
bool MasmParser::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 nextTok = peekTok(false);
|
|
|
|
if (nextTok.isNot(AsmToken::Identifier))
|
|
return true;
|
|
|
|
// We have a '$' or '@' followed by an identifier, make sure they are adjacent.
|
|
if (PrefixLoc.getPointer() + 1 != nextTok.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().getIdentifier().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;
|
|
}
|
|
|
|
/// parseDirectiveEquate:
|
|
/// ::= name "=" expression
|
|
/// | name "equ" expression (not redefinable)
|
|
/// | name "equ" text-list
|
|
/// | name "textequ" text-list (redefinability unspecified)
|
|
bool MasmParser::parseDirectiveEquate(StringRef IDVal, StringRef Name,
|
|
DirectiveKind DirKind, SMLoc NameLoc) {
|
|
Variable &Var = Variables[Name.lower()];
|
|
if (Var.Name.empty()) {
|
|
Var.Name = Name;
|
|
}
|
|
|
|
SMLoc StartLoc = Lexer.getLoc();
|
|
if (DirKind == DK_EQU || DirKind == DK_TEXTEQU) {
|
|
// "equ" and "textequ" both allow text expressions.
|
|
std::string Value;
|
|
std::string TextItem;
|
|
if (!parseTextItem(TextItem)) {
|
|
Value += TextItem;
|
|
|
|
// Accept a text-list, not just one text-item.
|
|
auto parseItem = [&]() -> bool {
|
|
if (parseTextItem(TextItem))
|
|
return TokError("expected text item");
|
|
Value += TextItem;
|
|
return false;
|
|
};
|
|
if (parseOptionalToken(AsmToken::Comma) && parseMany(parseItem))
|
|
return addErrorSuffix(" in '" + Twine(IDVal) + "' directive");
|
|
|
|
if (!Var.IsText || Var.TextValue != Value) {
|
|
switch (Var.Redefinable) {
|
|
case Variable::NOT_REDEFINABLE:
|
|
return Error(getTok().getLoc(), "invalid variable redefinition");
|
|
case Variable::WARN_ON_REDEFINITION:
|
|
if (Warning(NameLoc, "redefining '" + Name +
|
|
"', already defined on the command line")) {
|
|
return true;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
Var.IsText = true;
|
|
Var.TextValue = Value;
|
|
Var.Redefinable = Variable::REDEFINABLE;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
if (DirKind == DK_TEXTEQU)
|
|
return TokError("expected <text> in '" + Twine(IDVal) + "' directive");
|
|
|
|
// Parse as expression assignment.
|
|
const MCExpr *Expr;
|
|
SMLoc EndLoc;
|
|
if (parseExpression(Expr, EndLoc))
|
|
return addErrorSuffix(" in '" + Twine(IDVal) + "' directive");
|
|
|
|
int64_t Value;
|
|
if (!Expr->evaluateAsAbsolute(Value, getStreamer().getAssemblerPtr())) {
|
|
// Not an absolute expression; define as a text replacement.
|
|
StringRef ExprAsString = StringRef(
|
|
StartLoc.getPointer(), EndLoc.getPointer() - StartLoc.getPointer());
|
|
if (!Var.IsText || Var.TextValue != ExprAsString) {
|
|
switch (Var.Redefinable) {
|
|
case Variable::NOT_REDEFINABLE:
|
|
return Error(getTok().getLoc(), "invalid variable redefinition");
|
|
case Variable::WARN_ON_REDEFINITION:
|
|
if (Warning(NameLoc, "redefining '" + Name +
|
|
"', already defined on the command line")) {
|
|
return true;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
Var.IsText = true;
|
|
Var.TextValue = ExprAsString.str();
|
|
} else {
|
|
if (Var.IsText || Var.NumericValue != Value) {
|
|
switch (Var.Redefinable) {
|
|
case Variable::NOT_REDEFINABLE:
|
|
return Error(getTok().getLoc(), "invalid variable redefinition");
|
|
case Variable::WARN_ON_REDEFINITION:
|
|
if (Warning(NameLoc, "redefining '" + Name +
|
|
"', already defined on the command line")) {
|
|
return true;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
Var.NumericValue = Value;
|
|
}
|
|
Var.Redefinable = (DirKind == DK_ASSIGN) ? Variable::REDEFINABLE
|
|
: Variable::NOT_REDEFINABLE;
|
|
|
|
MCSymbol *Sym = getContext().getOrCreateSymbol(Var.Name);
|
|
Sym->setRedefinable(Var.Redefinable != Variable::NOT_REDEFINABLE);
|
|
Sym->setVariableValue(Expr);
|
|
Sym->setExternal(false);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::parseEscapedString(std::string &Data) {
|
|
if (check(getTok().isNot(AsmToken::String), "expected string"))
|
|
return true;
|
|
|
|
Data = "";
|
|
char Quote = getTok().getString().front();
|
|
StringRef Str = getTok().getStringContents();
|
|
Data.reserve(Str.size());
|
|
for (size_t i = 0, e = Str.size(); i != e; ++i) {
|
|
Data.push_back(Str[i]);
|
|
if (Str[i] == Quote) {
|
|
// MASM treats doubled delimiting quotes as an escaped delimiting quote.
|
|
// If we're escaping the string's trailing delimiter, we're definitely
|
|
// missing a quotation mark.
|
|
if (i + 1 == Str.size())
|
|
return Error(getTok().getLoc(), "missing quotation mark in string");
|
|
if (Str[i + 1] == Quote)
|
|
++i;
|
|
}
|
|
}
|
|
|
|
Lex();
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::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, EndStatementAtEOFStack.back());
|
|
// Eat from '<' to '>'.
|
|
Lex();
|
|
|
|
Data = angleBracketString(StringRef(StartChar, EndChar - StartChar));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// textItem ::= textLiteral | textMacroID | % constExpr
|
|
bool MasmParser::parseTextItem(std::string &Data) {
|
|
switch (getTok().getKind()) {
|
|
default:
|
|
return true;
|
|
case AsmToken::Percent: {
|
|
int64_t Res;
|
|
if (parseToken(AsmToken::Percent) || parseAbsoluteExpression(Res))
|
|
return true;
|
|
Data = std::to_string(Res);
|
|
return false;
|
|
}
|
|
case AsmToken::Less:
|
|
case AsmToken::LessEqual:
|
|
case AsmToken::LessLess:
|
|
case AsmToken::LessGreater:
|
|
return parseAngleBracketString(Data);
|
|
case AsmToken::Identifier: {
|
|
// This must be a text macro; we need to expand it accordingly.
|
|
StringRef ID;
|
|
if (parseIdentifier(ID))
|
|
return true;
|
|
Data = ID.str();
|
|
|
|
auto it = Variables.find(ID.lower());
|
|
if (it == Variables.end()) {
|
|
// Not a variable; since we haven't used the token, put it back for better
|
|
// error recovery.
|
|
getLexer().UnLex(AsmToken(AsmToken::Identifier, ID));
|
|
return true;
|
|
}
|
|
|
|
while (it != Variables.end()) {
|
|
const Variable &Var = it->second;
|
|
if (!Var.IsText) {
|
|
// Not a text macro; not usable in TextItem context. Since we haven't
|
|
// used the token, put it back for better error recovery.
|
|
getLexer().UnLex(AsmToken(AsmToken::Identifier, ID));
|
|
return true;
|
|
}
|
|
Data = Var.TextValue;
|
|
it = Variables.find(StringRef(Data).lower());
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
llvm_unreachable("unhandled token kind");
|
|
}
|
|
|
|
/// parseDirectiveAscii:
|
|
/// ::= ( .ascii | .asciz | .string ) [ "string" ( , "string" )* ]
|
|
bool MasmParser::parseDirectiveAscii(StringRef IDVal, bool ZeroTerminated) {
|
|
auto parseOp = [&]() -> bool {
|
|
std::string Data;
|
|
if (checkForValidSection() || parseEscapedString(Data))
|
|
return true;
|
|
getStreamer().emitBytes(Data);
|
|
if (ZeroTerminated)
|
|
getStreamer().emitBytes(StringRef("\0", 1));
|
|
return false;
|
|
};
|
|
|
|
if (parseMany(parseOp))
|
|
return addErrorSuffix(" in '" + Twine(IDVal) + "' directive");
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::emitIntValue(const MCExpr *Value, unsigned Size) {
|
|
// Special case constant expressions to match code generator.
|
|
if (const MCConstantExpr *MCE = dyn_cast<MCConstantExpr>(Value)) {
|
|
assert(Size <= 8 && "Invalid size");
|
|
int64_t IntValue = MCE->getValue();
|
|
if (!isUIntN(8 * Size, IntValue) && !isIntN(8 * Size, IntValue))
|
|
return Error(MCE->getLoc(), "out of range literal value");
|
|
getStreamer().emitIntValue(IntValue, Size);
|
|
} else {
|
|
const MCSymbolRefExpr *MSE = dyn_cast<MCSymbolRefExpr>(Value);
|
|
if (MSE && MSE->getSymbol().getName() == "?") {
|
|
// ? initializer; treat as 0.
|
|
getStreamer().emitIntValue(0, Size);
|
|
} else {
|
|
getStreamer().emitValue(Value, Size, Value->getLoc());
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::parseScalarInitializer(unsigned Size,
|
|
SmallVectorImpl<const MCExpr *> &Values,
|
|
unsigned StringPadLength) {
|
|
if (Size == 1 && getTok().is(AsmToken::String)) {
|
|
std::string Value;
|
|
if (parseEscapedString(Value))
|
|
return true;
|
|
// Treat each character as an initializer.
|
|
for (const unsigned char CharVal : Value)
|
|
Values.push_back(MCConstantExpr::create(CharVal, getContext()));
|
|
|
|
// Pad the string with spaces to the specified length.
|
|
for (size_t i = Value.size(); i < StringPadLength; ++i)
|
|
Values.push_back(MCConstantExpr::create(' ', getContext()));
|
|
} else {
|
|
const MCExpr *Value;
|
|
if (parseExpression(Value))
|
|
return true;
|
|
if (getTok().is(AsmToken::Identifier) &&
|
|
getTok().getString().equals_insensitive("dup")) {
|
|
Lex(); // Eat 'dup'.
|
|
const MCConstantExpr *MCE = dyn_cast<MCConstantExpr>(Value);
|
|
if (!MCE)
|
|
return Error(Value->getLoc(),
|
|
"cannot repeat value a non-constant number of times");
|
|
const int64_t Repetitions = MCE->getValue();
|
|
if (Repetitions < 0)
|
|
return Error(Value->getLoc(),
|
|
"cannot repeat value a negative number of times");
|
|
|
|
SmallVector<const MCExpr *, 1> DuplicatedValues;
|
|
if (parseToken(AsmToken::LParen,
|
|
"parentheses required for 'dup' contents") ||
|
|
parseScalarInstList(Size, DuplicatedValues) ||
|
|
parseToken(AsmToken::RParen, "unmatched parentheses"))
|
|
return true;
|
|
|
|
for (int i = 0; i < Repetitions; ++i)
|
|
Values.append(DuplicatedValues.begin(), DuplicatedValues.end());
|
|
} else {
|
|
Values.push_back(Value);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::parseScalarInstList(unsigned Size,
|
|
SmallVectorImpl<const MCExpr *> &Values,
|
|
const AsmToken::TokenKind EndToken) {
|
|
while (getTok().isNot(EndToken) &&
|
|
(EndToken != AsmToken::Greater ||
|
|
getTok().isNot(AsmToken::GreaterGreater))) {
|
|
parseScalarInitializer(Size, Values);
|
|
|
|
// If we see a comma, continue, and allow line continuation.
|
|
if (!parseOptionalToken(AsmToken::Comma))
|
|
break;
|
|
parseOptionalToken(AsmToken::EndOfStatement);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::emitIntegralValues(unsigned Size, unsigned *Count) {
|
|
SmallVector<const MCExpr *, 1> Values;
|
|
if (checkForValidSection() || parseScalarInstList(Size, Values))
|
|
return true;
|
|
|
|
for (auto Value : Values) {
|
|
emitIntValue(Value, Size);
|
|
}
|
|
if (Count)
|
|
*Count = Values.size();
|
|
return false;
|
|
}
|
|
|
|
// Add a field to the current structure.
|
|
bool MasmParser::addIntegralField(StringRef Name, unsigned Size) {
|
|
StructInfo &Struct = StructInProgress.back();
|
|
FieldInfo &Field = Struct.addField(Name, FT_INTEGRAL, Size);
|
|
IntFieldInfo &IntInfo = Field.Contents.IntInfo;
|
|
|
|
Field.Type = Size;
|
|
|
|
if (parseScalarInstList(Size, IntInfo.Values))
|
|
return true;
|
|
|
|
Field.SizeOf = Field.Type * IntInfo.Values.size();
|
|
Field.LengthOf = IntInfo.Values.size();
|
|
const unsigned FieldEnd = Field.Offset + Field.SizeOf;
|
|
if (!Struct.IsUnion) {
|
|
Struct.NextOffset = FieldEnd;
|
|
}
|
|
Struct.Size = std::max(Struct.Size, FieldEnd);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveValue
|
|
/// ::= (byte | word | ... ) [ expression (, expression)* ]
|
|
bool MasmParser::parseDirectiveValue(StringRef IDVal, unsigned Size) {
|
|
if (StructInProgress.empty()) {
|
|
// Initialize data value.
|
|
if (emitIntegralValues(Size))
|
|
return addErrorSuffix(" in '" + Twine(IDVal) + "' directive");
|
|
} else if (addIntegralField("", Size)) {
|
|
return addErrorSuffix(" in '" + Twine(IDVal) + "' directive");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveNamedValue
|
|
/// ::= name (byte | word | ... ) [ expression (, expression)* ]
|
|
bool MasmParser::parseDirectiveNamedValue(StringRef TypeName, unsigned Size,
|
|
StringRef Name, SMLoc NameLoc) {
|
|
if (StructInProgress.empty()) {
|
|
// Initialize named data value.
|
|
MCSymbol *Sym = getContext().getOrCreateSymbol(Name);
|
|
getStreamer().emitLabel(Sym);
|
|
unsigned Count;
|
|
if (emitIntegralValues(Size, &Count))
|
|
return addErrorSuffix(" in '" + Twine(TypeName) + "' directive");
|
|
|
|
AsmTypeInfo Type;
|
|
Type.Name = TypeName;
|
|
Type.Size = Size * Count;
|
|
Type.ElementSize = Size;
|
|
Type.Length = Count;
|
|
KnownType[Name.lower()] = Type;
|
|
} else if (addIntegralField(Name, Size)) {
|
|
return addErrorSuffix(" in '" + Twine(TypeName) + "' directive");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool parseHexOcta(MasmParser &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;
|
|
}
|
|
|
|
bool MasmParser::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;
|
|
SMLoc SignLoc;
|
|
if (getLexer().is(AsmToken::Minus)) {
|
|
SignLoc = getLexer().getLoc();
|
|
Lexer.Lex();
|
|
IsNeg = true;
|
|
} else if (getLexer().is(AsmToken::Plus)) {
|
|
SignLoc = getLexer().getLoc();
|
|
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.equals_insensitive("infinity") || IDVal.equals_insensitive("inf"))
|
|
Value = APFloat::getInf(Semantics);
|
|
else if (IDVal.equals_insensitive("nan"))
|
|
Value = APFloat::getNaN(Semantics, false, ~0);
|
|
else if (IDVal.equals_insensitive("?"))
|
|
Value = APFloat::getZero(Semantics);
|
|
else
|
|
return TokError("invalid floating point literal");
|
|
} else if (IDVal.consume_back("r") || IDVal.consume_back("R")) {
|
|
// MASM hexadecimal floating-point literal; no APFloat conversion needed.
|
|
// To match ML64.exe, ignore the initial sign.
|
|
unsigned SizeInBits = Value.getSizeInBits(Semantics);
|
|
if (SizeInBits != (IDVal.size() << 2))
|
|
return TokError("invalid floating point literal");
|
|
|
|
// Consume the numeric token.
|
|
Lex();
|
|
|
|
Res = APInt(SizeInBits, IDVal, 16);
|
|
if (SignLoc.isValid())
|
|
return Warning(SignLoc, "MASM-style hex floats ignore explicit sign");
|
|
return false;
|
|
} 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;
|
|
}
|
|
|
|
bool MasmParser::parseRealInstList(const fltSemantics &Semantics,
|
|
SmallVectorImpl<APInt> &ValuesAsInt,
|
|
const AsmToken::TokenKind EndToken) {
|
|
while (getTok().isNot(EndToken) ||
|
|
(EndToken == AsmToken::Greater &&
|
|
getTok().isNot(AsmToken::GreaterGreater))) {
|
|
const AsmToken NextTok = peekTok();
|
|
if (NextTok.is(AsmToken::Identifier) &&
|
|
NextTok.getString().equals_insensitive("dup")) {
|
|
const MCExpr *Value;
|
|
if (parseExpression(Value) || parseToken(AsmToken::Identifier))
|
|
return true;
|
|
const MCConstantExpr *MCE = dyn_cast<MCConstantExpr>(Value);
|
|
if (!MCE)
|
|
return Error(Value->getLoc(),
|
|
"cannot repeat value a non-constant number of times");
|
|
const int64_t Repetitions = MCE->getValue();
|
|
if (Repetitions < 0)
|
|
return Error(Value->getLoc(),
|
|
"cannot repeat value a negative number of times");
|
|
|
|
SmallVector<APInt, 1> DuplicatedValues;
|
|
if (parseToken(AsmToken::LParen,
|
|
"parentheses required for 'dup' contents") ||
|
|
parseRealInstList(Semantics, DuplicatedValues) ||
|
|
parseToken(AsmToken::RParen, "unmatched parentheses"))
|
|
return true;
|
|
|
|
for (int i = 0; i < Repetitions; ++i)
|
|
ValuesAsInt.append(DuplicatedValues.begin(), DuplicatedValues.end());
|
|
} else {
|
|
APInt AsInt;
|
|
if (parseRealValue(Semantics, AsInt))
|
|
return true;
|
|
ValuesAsInt.push_back(AsInt);
|
|
}
|
|
|
|
// Continue if we see a comma. (Also, allow line continuation.)
|
|
if (!parseOptionalToken(AsmToken::Comma))
|
|
break;
|
|
parseOptionalToken(AsmToken::EndOfStatement);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Initialize real data values.
|
|
bool MasmParser::emitRealValues(const fltSemantics &Semantics,
|
|
unsigned *Count) {
|
|
if (checkForValidSection())
|
|
return true;
|
|
|
|
SmallVector<APInt, 1> ValuesAsInt;
|
|
if (parseRealInstList(Semantics, ValuesAsInt))
|
|
return true;
|
|
|
|
for (const APInt &AsInt : ValuesAsInt) {
|
|
getStreamer().emitIntValue(AsInt);
|
|
}
|
|
if (Count)
|
|
*Count = ValuesAsInt.size();
|
|
return false;
|
|
}
|
|
|
|
// Add a real field to the current struct.
|
|
bool MasmParser::addRealField(StringRef Name, const fltSemantics &Semantics,
|
|
size_t Size) {
|
|
StructInfo &Struct = StructInProgress.back();
|
|
FieldInfo &Field = Struct.addField(Name, FT_REAL, Size);
|
|
RealFieldInfo &RealInfo = Field.Contents.RealInfo;
|
|
|
|
Field.SizeOf = 0;
|
|
|
|
if (parseRealInstList(Semantics, RealInfo.AsIntValues))
|
|
return true;
|
|
|
|
Field.Type = RealInfo.AsIntValues.back().getBitWidth() / 8;
|
|
Field.LengthOf = RealInfo.AsIntValues.size();
|
|
Field.SizeOf = Field.Type * Field.LengthOf;
|
|
|
|
const unsigned FieldEnd = Field.Offset + Field.SizeOf;
|
|
if (!Struct.IsUnion) {
|
|
Struct.NextOffset = FieldEnd;
|
|
}
|
|
Struct.Size = std::max(Struct.Size, FieldEnd);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveRealValue
|
|
/// ::= (real4 | real8 | real10) [ expression (, expression)* ]
|
|
bool MasmParser::parseDirectiveRealValue(StringRef IDVal,
|
|
const fltSemantics &Semantics,
|
|
size_t Size) {
|
|
if (StructInProgress.empty()) {
|
|
// Initialize data value.
|
|
if (emitRealValues(Semantics))
|
|
return addErrorSuffix(" in '" + Twine(IDVal) + "' directive");
|
|
} else if (addRealField("", Semantics, Size)) {
|
|
return addErrorSuffix(" in '" + Twine(IDVal) + "' directive");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveNamedRealValue
|
|
/// ::= name (real4 | real8 | real10) [ expression (, expression)* ]
|
|
bool MasmParser::parseDirectiveNamedRealValue(StringRef TypeName,
|
|
const fltSemantics &Semantics,
|
|
unsigned Size, StringRef Name,
|
|
SMLoc NameLoc) {
|
|
if (StructInProgress.empty()) {
|
|
// Initialize named data value.
|
|
MCSymbol *Sym = getContext().getOrCreateSymbol(Name);
|
|
getStreamer().emitLabel(Sym);
|
|
unsigned Count;
|
|
if (emitRealValues(Semantics, &Count))
|
|
return addErrorSuffix(" in '" + TypeName + "' directive");
|
|
|
|
AsmTypeInfo Type;
|
|
Type.Name = TypeName;
|
|
Type.Size = Size * Count;
|
|
Type.ElementSize = Size;
|
|
Type.Length = Count;
|
|
KnownType[Name.lower()] = Type;
|
|
} else if (addRealField(Name, Semantics, Size)) {
|
|
return addErrorSuffix(" in '" + TypeName + "' directive");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::parseOptionalAngleBracketOpen() {
|
|
const AsmToken Tok = getTok();
|
|
if (parseOptionalToken(AsmToken::LessLess)) {
|
|
AngleBracketDepth++;
|
|
Lexer.UnLex(AsmToken(AsmToken::Less, Tok.getString().substr(1)));
|
|
return true;
|
|
} else if (parseOptionalToken(AsmToken::LessGreater)) {
|
|
AngleBracketDepth++;
|
|
Lexer.UnLex(AsmToken(AsmToken::Greater, Tok.getString().substr(1)));
|
|
return true;
|
|
} else if (parseOptionalToken(AsmToken::Less)) {
|
|
AngleBracketDepth++;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::parseAngleBracketClose(const Twine &Msg) {
|
|
const AsmToken Tok = getTok();
|
|
if (parseOptionalToken(AsmToken::GreaterGreater)) {
|
|
Lexer.UnLex(AsmToken(AsmToken::Greater, Tok.getString().substr(1)));
|
|
} else if (parseToken(AsmToken::Greater, Msg)) {
|
|
return true;
|
|
}
|
|
AngleBracketDepth--;
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::parseFieldInitializer(const FieldInfo &Field,
|
|
const IntFieldInfo &Contents,
|
|
FieldInitializer &Initializer) {
|
|
SMLoc Loc = getTok().getLoc();
|
|
|
|
SmallVector<const MCExpr *, 1> Values;
|
|
if (parseOptionalToken(AsmToken::LCurly)) {
|
|
if (Field.LengthOf == 1 && Field.Type > 1)
|
|
return Error(Loc, "Cannot initialize scalar field with array value");
|
|
if (parseScalarInstList(Field.Type, Values, AsmToken::RCurly) ||
|
|
parseToken(AsmToken::RCurly))
|
|
return true;
|
|
} else if (parseOptionalAngleBracketOpen()) {
|
|
if (Field.LengthOf == 1 && Field.Type > 1)
|
|
return Error(Loc, "Cannot initialize scalar field with array value");
|
|
if (parseScalarInstList(Field.Type, Values, AsmToken::Greater) ||
|
|
parseAngleBracketClose())
|
|
return true;
|
|
} else if (Field.LengthOf > 1 && Field.Type > 1) {
|
|
return Error(Loc, "Cannot initialize array field with scalar value");
|
|
} else if (parseScalarInitializer(Field.Type, Values,
|
|
/*StringPadLength=*/Field.LengthOf)) {
|
|
return true;
|
|
}
|
|
|
|
if (Values.size() > Field.LengthOf) {
|
|
return Error(Loc, "Initializer too long for field; expected at most " +
|
|
std::to_string(Field.LengthOf) + " elements, got " +
|
|
std::to_string(Values.size()));
|
|
}
|
|
// Default-initialize all remaining values.
|
|
Values.append(Contents.Values.begin() + Values.size(), Contents.Values.end());
|
|
|
|
Initializer = FieldInitializer(std::move(Values));
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::parseFieldInitializer(const FieldInfo &Field,
|
|
const RealFieldInfo &Contents,
|
|
FieldInitializer &Initializer) {
|
|
const fltSemantics *Semantics;
|
|
switch (Field.Type) {
|
|
case 4:
|
|
Semantics = &APFloat::IEEEsingle();
|
|
break;
|
|
case 8:
|
|
Semantics = &APFloat::IEEEdouble();
|
|
break;
|
|
case 10:
|
|
Semantics = &APFloat::x87DoubleExtended();
|
|
break;
|
|
default:
|
|
llvm_unreachable("unknown real field type");
|
|
}
|
|
|
|
SMLoc Loc = getTok().getLoc();
|
|
|
|
SmallVector<APInt, 1> AsIntValues;
|
|
if (parseOptionalToken(AsmToken::LCurly)) {
|
|
if (Field.LengthOf == 1)
|
|
return Error(Loc, "Cannot initialize scalar field with array value");
|
|
if (parseRealInstList(*Semantics, AsIntValues, AsmToken::RCurly) ||
|
|
parseToken(AsmToken::RCurly))
|
|
return true;
|
|
} else if (parseOptionalAngleBracketOpen()) {
|
|
if (Field.LengthOf == 1)
|
|
return Error(Loc, "Cannot initialize scalar field with array value");
|
|
if (parseRealInstList(*Semantics, AsIntValues, AsmToken::Greater) ||
|
|
parseAngleBracketClose())
|
|
return true;
|
|
} else if (Field.LengthOf > 1) {
|
|
return Error(Loc, "Cannot initialize array field with scalar value");
|
|
} else {
|
|
AsIntValues.emplace_back();
|
|
if (parseRealValue(*Semantics, AsIntValues.back()))
|
|
return true;
|
|
}
|
|
|
|
if (AsIntValues.size() > Field.LengthOf) {
|
|
return Error(Loc, "Initializer too long for field; expected at most " +
|
|
std::to_string(Field.LengthOf) + " elements, got " +
|
|
std::to_string(AsIntValues.size()));
|
|
}
|
|
// Default-initialize all remaining values.
|
|
AsIntValues.append(Contents.AsIntValues.begin() + AsIntValues.size(),
|
|
Contents.AsIntValues.end());
|
|
|
|
Initializer = FieldInitializer(std::move(AsIntValues));
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::parseFieldInitializer(const FieldInfo &Field,
|
|
const StructFieldInfo &Contents,
|
|
FieldInitializer &Initializer) {
|
|
SMLoc Loc = getTok().getLoc();
|
|
|
|
std::vector<StructInitializer> Initializers;
|
|
if (Field.LengthOf > 1) {
|
|
if (parseOptionalToken(AsmToken::LCurly)) {
|
|
if (parseStructInstList(Contents.Structure, Initializers,
|
|
AsmToken::RCurly) ||
|
|
parseToken(AsmToken::RCurly))
|
|
return true;
|
|
} else if (parseOptionalAngleBracketOpen()) {
|
|
if (parseStructInstList(Contents.Structure, Initializers,
|
|
AsmToken::Greater) ||
|
|
parseAngleBracketClose())
|
|
return true;
|
|
} else {
|
|
return Error(Loc, "Cannot initialize array field with scalar value");
|
|
}
|
|
} else {
|
|
Initializers.emplace_back();
|
|
if (parseStructInitializer(Contents.Structure, Initializers.back()))
|
|
return true;
|
|
}
|
|
|
|
if (Initializers.size() > Field.LengthOf) {
|
|
return Error(Loc, "Initializer too long for field; expected at most " +
|
|
std::to_string(Field.LengthOf) + " elements, got " +
|
|
std::to_string(Initializers.size()));
|
|
}
|
|
// Default-initialize all remaining values.
|
|
Initializers.insert(Initializers.end(),
|
|
Contents.Initializers.begin() + Initializers.size(),
|
|
Contents.Initializers.end());
|
|
|
|
Initializer = FieldInitializer(std::move(Initializers), Contents.Structure);
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::parseFieldInitializer(const FieldInfo &Field,
|
|
FieldInitializer &Initializer) {
|
|
switch (Field.Contents.FT) {
|
|
case FT_INTEGRAL:
|
|
return parseFieldInitializer(Field, Field.Contents.IntInfo, Initializer);
|
|
case FT_REAL:
|
|
return parseFieldInitializer(Field, Field.Contents.RealInfo, Initializer);
|
|
case FT_STRUCT:
|
|
return parseFieldInitializer(Field, Field.Contents.StructInfo, Initializer);
|
|
}
|
|
llvm_unreachable("Unhandled FieldType enum");
|
|
}
|
|
|
|
bool MasmParser::parseStructInitializer(const StructInfo &Structure,
|
|
StructInitializer &Initializer) {
|
|
const AsmToken FirstToken = getTok();
|
|
|
|
Optional<AsmToken::TokenKind> EndToken;
|
|
if (parseOptionalToken(AsmToken::LCurly)) {
|
|
EndToken = AsmToken::RCurly;
|
|
} else if (parseOptionalAngleBracketOpen()) {
|
|
EndToken = AsmToken::Greater;
|
|
AngleBracketDepth++;
|
|
} else if (FirstToken.is(AsmToken::Identifier) &&
|
|
FirstToken.getString() == "?") {
|
|
// ? initializer; leave EndToken uninitialized to treat as empty.
|
|
if (parseToken(AsmToken::Identifier))
|
|
return true;
|
|
} else {
|
|
return Error(FirstToken.getLoc(), "Expected struct initializer");
|
|
}
|
|
|
|
auto &FieldInitializers = Initializer.FieldInitializers;
|
|
size_t FieldIndex = 0;
|
|
if (EndToken.hasValue()) {
|
|
// Initialize all fields with given initializers.
|
|
while (getTok().isNot(EndToken.getValue()) &&
|
|
FieldIndex < Structure.Fields.size()) {
|
|
const FieldInfo &Field = Structure.Fields[FieldIndex++];
|
|
if (parseOptionalToken(AsmToken::Comma)) {
|
|
// Empty initializer; use the default and continue. (Also, allow line
|
|
// continuation.)
|
|
FieldInitializers.push_back(Field.Contents);
|
|
parseOptionalToken(AsmToken::EndOfStatement);
|
|
continue;
|
|
}
|
|
FieldInitializers.emplace_back(Field.Contents.FT);
|
|
if (parseFieldInitializer(Field, FieldInitializers.back()))
|
|
return true;
|
|
|
|
// Continue if we see a comma. (Also, allow line continuation.)
|
|
SMLoc CommaLoc = getTok().getLoc();
|
|
if (!parseOptionalToken(AsmToken::Comma))
|
|
break;
|
|
if (FieldIndex == Structure.Fields.size())
|
|
return Error(CommaLoc, "'" + Structure.Name +
|
|
"' initializer initializes too many fields");
|
|
parseOptionalToken(AsmToken::EndOfStatement);
|
|
}
|
|
}
|
|
// Default-initialize all remaining fields.
|
|
for (auto It = Structure.Fields.begin() + FieldIndex;
|
|
It != Structure.Fields.end(); ++It) {
|
|
const FieldInfo &Field = *It;
|
|
FieldInitializers.push_back(Field.Contents);
|
|
}
|
|
|
|
if (EndToken.hasValue()) {
|
|
if (EndToken.getValue() == AsmToken::Greater)
|
|
return parseAngleBracketClose();
|
|
|
|
return parseToken(EndToken.getValue());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::parseStructInstList(
|
|
const StructInfo &Structure, std::vector<StructInitializer> &Initializers,
|
|
const AsmToken::TokenKind EndToken) {
|
|
while (getTok().isNot(EndToken) ||
|
|
(EndToken == AsmToken::Greater &&
|
|
getTok().isNot(AsmToken::GreaterGreater))) {
|
|
const AsmToken NextTok = peekTok();
|
|
if (NextTok.is(AsmToken::Identifier) &&
|
|
NextTok.getString().equals_insensitive("dup")) {
|
|
const MCExpr *Value;
|
|
if (parseExpression(Value) || parseToken(AsmToken::Identifier))
|
|
return true;
|
|
const MCConstantExpr *MCE = dyn_cast<MCConstantExpr>(Value);
|
|
if (!MCE)
|
|
return Error(Value->getLoc(),
|
|
"cannot repeat value a non-constant number of times");
|
|
const int64_t Repetitions = MCE->getValue();
|
|
if (Repetitions < 0)
|
|
return Error(Value->getLoc(),
|
|
"cannot repeat value a negative number of times");
|
|
|
|
std::vector<StructInitializer> DuplicatedValues;
|
|
if (parseToken(AsmToken::LParen,
|
|
"parentheses required for 'dup' contents") ||
|
|
parseStructInstList(Structure, DuplicatedValues) ||
|
|
parseToken(AsmToken::RParen, "unmatched parentheses"))
|
|
return true;
|
|
|
|
for (int i = 0; i < Repetitions; ++i)
|
|
llvm::append_range(Initializers, DuplicatedValues);
|
|
} else {
|
|
Initializers.emplace_back();
|
|
if (parseStructInitializer(Structure, Initializers.back()))
|
|
return true;
|
|
}
|
|
|
|
// Continue if we see a comma. (Also, allow line continuation.)
|
|
if (!parseOptionalToken(AsmToken::Comma))
|
|
break;
|
|
parseOptionalToken(AsmToken::EndOfStatement);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::emitFieldValue(const FieldInfo &Field,
|
|
const IntFieldInfo &Contents) {
|
|
// Default-initialize all values.
|
|
for (const MCExpr *Value : Contents.Values) {
|
|
if (emitIntValue(Value, Field.Type))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::emitFieldValue(const FieldInfo &Field,
|
|
const RealFieldInfo &Contents) {
|
|
for (const APInt &AsInt : Contents.AsIntValues) {
|
|
getStreamer().emitIntValue(AsInt.getLimitedValue(),
|
|
AsInt.getBitWidth() / 8);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::emitFieldValue(const FieldInfo &Field,
|
|
const StructFieldInfo &Contents) {
|
|
for (const auto &Initializer : Contents.Initializers) {
|
|
size_t Index = 0, Offset = 0;
|
|
for (const auto &SubField : Contents.Structure.Fields) {
|
|
getStreamer().emitZeros(SubField.Offset - Offset);
|
|
Offset = SubField.Offset + SubField.SizeOf;
|
|
emitFieldInitializer(SubField, Initializer.FieldInitializers[Index++]);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::emitFieldValue(const FieldInfo &Field) {
|
|
switch (Field.Contents.FT) {
|
|
case FT_INTEGRAL:
|
|
return emitFieldValue(Field, Field.Contents.IntInfo);
|
|
case FT_REAL:
|
|
return emitFieldValue(Field, Field.Contents.RealInfo);
|
|
case FT_STRUCT:
|
|
return emitFieldValue(Field, Field.Contents.StructInfo);
|
|
}
|
|
llvm_unreachable("Unhandled FieldType enum");
|
|
}
|
|
|
|
bool MasmParser::emitFieldInitializer(const FieldInfo &Field,
|
|
const IntFieldInfo &Contents,
|
|
const IntFieldInfo &Initializer) {
|
|
for (const auto &Value : Initializer.Values) {
|
|
if (emitIntValue(Value, Field.Type))
|
|
return true;
|
|
}
|
|
// Default-initialize all remaining values.
|
|
for (auto it = Contents.Values.begin() + Initializer.Values.size();
|
|
it != Contents.Values.end(); ++it) {
|
|
const auto &Value = *it;
|
|
if (emitIntValue(Value, Field.Type))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::emitFieldInitializer(const FieldInfo &Field,
|
|
const RealFieldInfo &Contents,
|
|
const RealFieldInfo &Initializer) {
|
|
for (const auto &AsInt : Initializer.AsIntValues) {
|
|
getStreamer().emitIntValue(AsInt.getLimitedValue(),
|
|
AsInt.getBitWidth() / 8);
|
|
}
|
|
// Default-initialize all remaining values.
|
|
for (auto It = Contents.AsIntValues.begin() + Initializer.AsIntValues.size();
|
|
It != Contents.AsIntValues.end(); ++It) {
|
|
const auto &AsInt = *It;
|
|
getStreamer().emitIntValue(AsInt.getLimitedValue(),
|
|
AsInt.getBitWidth() / 8);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::emitFieldInitializer(const FieldInfo &Field,
|
|
const StructFieldInfo &Contents,
|
|
const StructFieldInfo &Initializer) {
|
|
for (const auto &Init : Initializer.Initializers) {
|
|
if (emitStructInitializer(Contents.Structure, Init))
|
|
return true;
|
|
}
|
|
// Default-initialize all remaining values.
|
|
for (auto It =
|
|
Contents.Initializers.begin() + Initializer.Initializers.size();
|
|
It != Contents.Initializers.end(); ++It) {
|
|
const auto &Init = *It;
|
|
if (emitStructInitializer(Contents.Structure, Init))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::emitFieldInitializer(const FieldInfo &Field,
|
|
const FieldInitializer &Initializer) {
|
|
switch (Field.Contents.FT) {
|
|
case FT_INTEGRAL:
|
|
return emitFieldInitializer(Field, Field.Contents.IntInfo,
|
|
Initializer.IntInfo);
|
|
case FT_REAL:
|
|
return emitFieldInitializer(Field, Field.Contents.RealInfo,
|
|
Initializer.RealInfo);
|
|
case FT_STRUCT:
|
|
return emitFieldInitializer(Field, Field.Contents.StructInfo,
|
|
Initializer.StructInfo);
|
|
}
|
|
llvm_unreachable("Unhandled FieldType enum");
|
|
}
|
|
|
|
bool MasmParser::emitStructInitializer(const StructInfo &Structure,
|
|
const StructInitializer &Initializer) {
|
|
if (!Structure.Initializable)
|
|
return Error(getLexer().getLoc(),
|
|
"cannot initialize a value of type '" + Structure.Name +
|
|
"'; 'org' was used in the type's declaration");
|
|
size_t Index = 0, Offset = 0;
|
|
for (const auto &Init : Initializer.FieldInitializers) {
|
|
const auto &Field = Structure.Fields[Index++];
|
|
getStreamer().emitZeros(Field.Offset - Offset);
|
|
Offset = Field.Offset + Field.SizeOf;
|
|
if (emitFieldInitializer(Field, Init))
|
|
return true;
|
|
}
|
|
// Default-initialize all remaining fields.
|
|
for (auto It =
|
|
Structure.Fields.begin() + Initializer.FieldInitializers.size();
|
|
It != Structure.Fields.end(); ++It) {
|
|
const auto &Field = *It;
|
|
getStreamer().emitZeros(Field.Offset - Offset);
|
|
Offset = Field.Offset + Field.SizeOf;
|
|
if (emitFieldValue(Field))
|
|
return true;
|
|
}
|
|
// Add final padding.
|
|
if (Offset != Structure.Size)
|
|
getStreamer().emitZeros(Structure.Size - Offset);
|
|
return false;
|
|
}
|
|
|
|
// Set data values from initializers.
|
|
bool MasmParser::emitStructValues(const StructInfo &Structure,
|
|
unsigned *Count) {
|
|
std::vector<StructInitializer> Initializers;
|
|
if (parseStructInstList(Structure, Initializers))
|
|
return true;
|
|
|
|
for (const auto &Initializer : Initializers) {
|
|
if (emitStructInitializer(Structure, Initializer))
|
|
return true;
|
|
}
|
|
|
|
if (Count)
|
|
*Count = Initializers.size();
|
|
return false;
|
|
}
|
|
|
|
// Declare a field in the current struct.
|
|
bool MasmParser::addStructField(StringRef Name, const StructInfo &Structure) {
|
|
StructInfo &OwningStruct = StructInProgress.back();
|
|
FieldInfo &Field =
|
|
OwningStruct.addField(Name, FT_STRUCT, Structure.AlignmentSize);
|
|
StructFieldInfo &StructInfo = Field.Contents.StructInfo;
|
|
|
|
StructInfo.Structure = Structure;
|
|
Field.Type = Structure.Size;
|
|
|
|
if (parseStructInstList(Structure, StructInfo.Initializers))
|
|
return true;
|
|
|
|
Field.LengthOf = StructInfo.Initializers.size();
|
|
Field.SizeOf = Field.Type * Field.LengthOf;
|
|
|
|
const unsigned FieldEnd = Field.Offset + Field.SizeOf;
|
|
if (!OwningStruct.IsUnion) {
|
|
OwningStruct.NextOffset = FieldEnd;
|
|
}
|
|
OwningStruct.Size = std::max(OwningStruct.Size, FieldEnd);
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveStructValue
|
|
/// ::= struct-id (<struct-initializer> | {struct-initializer})
|
|
/// [, (<struct-initializer> | {struct-initializer})]*
|
|
bool MasmParser::parseDirectiveStructValue(const StructInfo &Structure,
|
|
StringRef Directive, SMLoc DirLoc) {
|
|
if (StructInProgress.empty()) {
|
|
if (emitStructValues(Structure))
|
|
return true;
|
|
} else if (addStructField("", Structure)) {
|
|
return addErrorSuffix(" in '" + Twine(Directive) + "' directive");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveNamedValue
|
|
/// ::= name (byte | word | ... ) [ expression (, expression)* ]
|
|
bool MasmParser::parseDirectiveNamedStructValue(const StructInfo &Structure,
|
|
StringRef Directive,
|
|
SMLoc DirLoc, StringRef Name) {
|
|
if (StructInProgress.empty()) {
|
|
// Initialize named data value.
|
|
MCSymbol *Sym = getContext().getOrCreateSymbol(Name);
|
|
getStreamer().emitLabel(Sym);
|
|
unsigned Count;
|
|
if (emitStructValues(Structure, &Count))
|
|
return true;
|
|
AsmTypeInfo Type;
|
|
Type.Name = Structure.Name;
|
|
Type.Size = Structure.Size * Count;
|
|
Type.ElementSize = Structure.Size;
|
|
Type.Length = Count;
|
|
KnownType[Name.lower()] = Type;
|
|
} else if (addStructField(Name, Structure)) {
|
|
return addErrorSuffix(" in '" + Twine(Directive) + "' directive");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveStruct
|
|
/// ::= <name> (STRUC | STRUCT | UNION) [fieldAlign] [, NONUNIQUE]
|
|
/// (dataDir | generalDir | offsetDir | nestedStruct)+
|
|
/// <name> ENDS
|
|
////// dataDir = data declaration
|
|
////// offsetDir = EVEN, ORG, ALIGN
|
|
bool MasmParser::parseDirectiveStruct(StringRef Directive,
|
|
DirectiveKind DirKind, StringRef Name,
|
|
SMLoc NameLoc) {
|
|
// We ignore NONUNIQUE; we do not support OPTION M510 or OPTION OLDSTRUCTS
|
|
// anyway, so all field accesses must be qualified.
|
|
AsmToken NextTok = getTok();
|
|
int64_t AlignmentValue = 1;
|
|
if (NextTok.isNot(AsmToken::Comma) &&
|
|
NextTok.isNot(AsmToken::EndOfStatement) &&
|
|
parseAbsoluteExpression(AlignmentValue)) {
|
|
return addErrorSuffix(" in alignment value for '" + Twine(Directive) +
|
|
"' directive");
|
|
}
|
|
if (!isPowerOf2_64(AlignmentValue)) {
|
|
return Error(NextTok.getLoc(), "alignment must be a power of two; was " +
|
|
std::to_string(AlignmentValue));
|
|
}
|
|
|
|
StringRef Qualifier;
|
|
SMLoc QualifierLoc;
|
|
if (parseOptionalToken(AsmToken::Comma)) {
|
|
QualifierLoc = getTok().getLoc();
|
|
if (parseIdentifier(Qualifier))
|
|
return addErrorSuffix(" in '" + Twine(Directive) + "' directive");
|
|
if (!Qualifier.equals_insensitive("nonunique"))
|
|
return Error(QualifierLoc, "Unrecognized qualifier for '" +
|
|
Twine(Directive) +
|
|
"' directive; expected none or NONUNIQUE");
|
|
}
|
|
|
|
if (parseToken(AsmToken::EndOfStatement))
|
|
return addErrorSuffix(" in '" + Twine(Directive) + "' directive");
|
|
|
|
StructInProgress.emplace_back(Name, DirKind == DK_UNION, AlignmentValue);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveNestedStruct
|
|
/// ::= (STRUC | STRUCT | UNION) [name]
|
|
/// (dataDir | generalDir | offsetDir | nestedStruct)+
|
|
/// ENDS
|
|
bool MasmParser::parseDirectiveNestedStruct(StringRef Directive,
|
|
DirectiveKind DirKind) {
|
|
if (StructInProgress.empty())
|
|
return TokError("missing name in top-level '" + Twine(Directive) +
|
|
"' directive");
|
|
|
|
StringRef Name;
|
|
if (getTok().is(AsmToken::Identifier)) {
|
|
Name = getTok().getIdentifier();
|
|
parseToken(AsmToken::Identifier);
|
|
}
|
|
if (parseToken(AsmToken::EndOfStatement))
|
|
return addErrorSuffix(" in '" + Twine(Directive) + "' directive");
|
|
|
|
// Reserve space to ensure Alignment doesn't get invalidated when
|
|
// StructInProgress grows.
|
|
StructInProgress.reserve(StructInProgress.size() + 1);
|
|
StructInProgress.emplace_back(Name, DirKind == DK_UNION,
|
|
StructInProgress.back().Alignment);
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::parseDirectiveEnds(StringRef Name, SMLoc NameLoc) {
|
|
if (StructInProgress.empty())
|
|
return Error(NameLoc, "ENDS directive without matching STRUC/STRUCT/UNION");
|
|
if (StructInProgress.size() > 1)
|
|
return Error(NameLoc, "unexpected name in nested ENDS directive");
|
|
if (StructInProgress.back().Name.compare_insensitive(Name))
|
|
return Error(NameLoc, "mismatched name in ENDS directive; expected '" +
|
|
StructInProgress.back().Name + "'");
|
|
StructInfo Structure = StructInProgress.pop_back_val();
|
|
// Pad to make the structure's size divisible by the smaller of its alignment
|
|
// and the size of its largest field.
|
|
Structure.Size = llvm::alignTo(
|
|
Structure.Size, std::min(Structure.Alignment, Structure.AlignmentSize));
|
|
Structs[Name.lower()] = Structure;
|
|
|
|
if (parseToken(AsmToken::EndOfStatement))
|
|
return addErrorSuffix(" in ENDS directive");
|
|
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::parseDirectiveNestedEnds() {
|
|
if (StructInProgress.empty())
|
|
return TokError("ENDS directive without matching STRUC/STRUCT/UNION");
|
|
if (StructInProgress.size() == 1)
|
|
return TokError("missing name in top-level ENDS directive");
|
|
|
|
if (parseToken(AsmToken::EndOfStatement))
|
|
return addErrorSuffix(" in nested ENDS directive");
|
|
|
|
StructInfo Structure = StructInProgress.pop_back_val();
|
|
// Pad to make the structure's size divisible by its alignment.
|
|
Structure.Size = llvm::alignTo(Structure.Size, Structure.Alignment);
|
|
|
|
StructInfo &ParentStruct = StructInProgress.back();
|
|
if (Structure.Name.empty()) {
|
|
// Anonymous substructures' fields are addressed as if they belong to the
|
|
// parent structure - so we transfer them to the parent here.
|
|
const size_t OldFields = ParentStruct.Fields.size();
|
|
ParentStruct.Fields.insert(
|
|
ParentStruct.Fields.end(),
|
|
std::make_move_iterator(Structure.Fields.begin()),
|
|
std::make_move_iterator(Structure.Fields.end()));
|
|
for (const auto &FieldByName : Structure.FieldsByName) {
|
|
ParentStruct.FieldsByName[FieldByName.getKey()] =
|
|
FieldByName.getValue() + OldFields;
|
|
}
|
|
|
|
unsigned FirstFieldOffset = 0;
|
|
if (!Structure.Fields.empty() && !ParentStruct.IsUnion) {
|
|
FirstFieldOffset = llvm::alignTo(
|
|
ParentStruct.NextOffset,
|
|
std::min(ParentStruct.Alignment, Structure.AlignmentSize));
|
|
}
|
|
|
|
if (ParentStruct.IsUnion) {
|
|
ParentStruct.Size = std::max(ParentStruct.Size, Structure.Size);
|
|
} else {
|
|
for (auto FieldIter = ParentStruct.Fields.begin() + OldFields;
|
|
FieldIter != ParentStruct.Fields.end(); ++FieldIter) {
|
|
FieldIter->Offset += FirstFieldOffset;
|
|
}
|
|
|
|
const unsigned StructureEnd = FirstFieldOffset + Structure.Size;
|
|
if (!ParentStruct.IsUnion) {
|
|
ParentStruct.NextOffset = StructureEnd;
|
|
}
|
|
ParentStruct.Size = std::max(ParentStruct.Size, StructureEnd);
|
|
}
|
|
} else {
|
|
FieldInfo &Field = ParentStruct.addField(Structure.Name, FT_STRUCT,
|
|
Structure.AlignmentSize);
|
|
StructFieldInfo &StructInfo = Field.Contents.StructInfo;
|
|
Field.Type = Structure.Size;
|
|
Field.LengthOf = 1;
|
|
Field.SizeOf = Structure.Size;
|
|
|
|
const unsigned StructureEnd = Field.Offset + Field.SizeOf;
|
|
if (!ParentStruct.IsUnion) {
|
|
ParentStruct.NextOffset = StructureEnd;
|
|
}
|
|
ParentStruct.Size = std::max(ParentStruct.Size, StructureEnd);
|
|
|
|
StructInfo.Structure = Structure;
|
|
StructInfo.Initializers.emplace_back();
|
|
auto &FieldInitializers = StructInfo.Initializers.back().FieldInitializers;
|
|
for (const auto &SubField : Structure.Fields) {
|
|
FieldInitializers.push_back(SubField.Contents);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveOrg
|
|
/// ::= org expression
|
|
bool MasmParser::parseDirectiveOrg() {
|
|
const MCExpr *Offset;
|
|
SMLoc OffsetLoc = Lexer.getLoc();
|
|
if (checkForValidSection() || parseExpression(Offset))
|
|
return true;
|
|
if (parseToken(AsmToken::EndOfStatement))
|
|
return addErrorSuffix(" in 'org' directive");
|
|
|
|
if (StructInProgress.empty()) {
|
|
// Not in a struct; change the offset for the next instruction or data
|
|
if (checkForValidSection())
|
|
return addErrorSuffix(" in 'org' directive");
|
|
|
|
getStreamer().emitValueToOffset(Offset, 0, OffsetLoc);
|
|
} else {
|
|
// Offset the next field of this struct
|
|
StructInfo &Structure = StructInProgress.back();
|
|
int64_t OffsetRes;
|
|
if (!Offset->evaluateAsAbsolute(OffsetRes, getStreamer().getAssemblerPtr()))
|
|
return Error(OffsetLoc,
|
|
"expected absolute expression in 'org' directive");
|
|
if (OffsetRes < 0)
|
|
return Error(
|
|
OffsetLoc,
|
|
"expected non-negative value in struct's 'org' directive; was " +
|
|
std::to_string(OffsetRes));
|
|
Structure.NextOffset = static_cast<unsigned>(OffsetRes);
|
|
|
|
// ORG-affected structures cannot be initialized
|
|
Structure.Initializable = false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::emitAlignTo(int64_t Alignment) {
|
|
if (StructInProgress.empty()) {
|
|
// Not in a struct; align the next instruction or data
|
|
if (checkForValidSection())
|
|
return true;
|
|
|
|
// Check whether we should use optimal code alignment for this align
|
|
// directive.
|
|
const MCSection *Section = getStreamer().getCurrentSectionOnly();
|
|
assert(Section && "must have section to emit alignment");
|
|
if (Section->UseCodeAlign()) {
|
|
getStreamer().emitCodeAlignment(Alignment, /*MaxBytesToEmit=*/0);
|
|
} else {
|
|
// FIXME: Target specific behavior about how the "extra" bytes are filled.
|
|
getStreamer().emitValueToAlignment(Alignment, /*Value=*/0,
|
|
/*ValueSize=*/1,
|
|
/*MaxBytesToEmit=*/0);
|
|
}
|
|
} else {
|
|
// Align the next field of this struct
|
|
StructInfo &Structure = StructInProgress.back();
|
|
Structure.NextOffset = llvm::alignTo(Structure.NextOffset, Alignment);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveAlign
|
|
/// ::= align expression
|
|
bool MasmParser::parseDirectiveAlign() {
|
|
SMLoc AlignmentLoc = getLexer().getLoc();
|
|
int64_t Alignment;
|
|
|
|
// Ignore empty 'align' directives.
|
|
if (getTok().is(AsmToken::EndOfStatement)) {
|
|
return Warning(AlignmentLoc,
|
|
"align directive with no operand is ignored") &&
|
|
parseToken(AsmToken::EndOfStatement);
|
|
}
|
|
if (parseAbsoluteExpression(Alignment) ||
|
|
parseToken(AsmToken::EndOfStatement))
|
|
return addErrorSuffix(" in align directive");
|
|
|
|
// Always emit an alignment here even if we throw an error.
|
|
bool ReturnVal = false;
|
|
|
|
// Reject alignments that aren't either a power of two or zero, for ML.exe
|
|
// compatibility. Alignment of zero is silently rounded up to one.
|
|
if (Alignment == 0)
|
|
Alignment = 1;
|
|
if (!isPowerOf2_64(Alignment))
|
|
ReturnVal |= Error(AlignmentLoc, "alignment must be a power of 2; was " +
|
|
std::to_string(Alignment));
|
|
|
|
if (emitAlignTo(Alignment))
|
|
ReturnVal |= addErrorSuffix(" in align directive");
|
|
|
|
return ReturnVal;
|
|
}
|
|
|
|
/// parseDirectiveEven
|
|
/// ::= even
|
|
bool MasmParser::parseDirectiveEven() {
|
|
if (parseToken(AsmToken::EndOfStatement) || emitAlignTo(2))
|
|
return addErrorSuffix(" in even directive");
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveFile
|
|
/// ::= .file filename
|
|
/// ::= .file number [directory] filename [md5 checksum] [source source-text]
|
|
bool MasmParser::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 (check(getTok().isNot(AsmToken::String),
|
|
"unexpected token in '.file' directive") ||
|
|
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;
|
|
|
|
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);
|
|
}
|
|
|
|
Optional<MD5::MD5Result> CKMem;
|
|
if (HasMD5) {
|
|
MD5::MD5Result Sum;
|
|
for (unsigned i = 0; i != 8; ++i) {
|
|
Sum.Bytes[i] = uint8_t(MD5Hi >> ((7 - i) * 8));
|
|
Sum.Bytes[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) {
|
|
if (Ctx.getDwarfVersion() < 5)
|
|
return Warning(DirectiveLoc, "file 0 not supported prior to DWARF-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 MasmParser::parseDirectiveLine() {
|
|
int64_t LineNumber;
|
|
if (getLexer().is(AsmToken::Integer)) {
|
|
if (parseIntToken(LineNumber, "unexpected token in '.line' directive"))
|
|
return true;
|
|
(void)LineNumber;
|
|
// FIXME: Do something with the .line.
|
|
}
|
|
if (parseToken(AsmToken::EndOfStatement,
|
|
"unexpected token in '.line' directive"))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/// 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 MasmParser::parseDirectiveLoc() {
|
|
int64_t FileNumber = 0, LineNumber = 0;
|
|
SMLoc Loc = getTok().getLoc();
|
|
if (parseIntToken(FileNumber, "unexpected token in '.loc' directive") ||
|
|
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;
|
|
}
|
|
|
|
/// parseDirectiveStabs
|
|
/// ::= .stabs string, number, number, number
|
|
bool MasmParser::parseDirectiveStabs() {
|
|
return TokError("unsupported directive '.stabs'");
|
|
}
|
|
|
|
/// parseDirectiveCVFile
|
|
/// ::= .cv_file number filename [checksum] [checksumkind]
|
|
bool MasmParser::parseDirectiveCVFile() {
|
|
SMLoc FileNumberLoc = getTok().getLoc();
|
|
int64_t FileNumber;
|
|
std::string Filename;
|
|
std::string Checksum;
|
|
int64_t ChecksumKind = 0;
|
|
|
|
if (parseIntToken(FileNumber,
|
|
"expected file number in '.cv_file' directive") ||
|
|
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") ||
|
|
parseToken(AsmToken::EndOfStatement,
|
|
"unexpected token in '.cv_file' directive"))
|
|
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 MasmParser::parseCVFunctionId(int64_t &FunctionId,
|
|
StringRef DirectiveName) {
|
|
SMLoc Loc;
|
|
return parseTokenLoc(Loc) ||
|
|
parseIntToken(FunctionId, "expected function id in '" + DirectiveName +
|
|
"' directive") ||
|
|
check(FunctionId < 0 || FunctionId >= UINT_MAX, Loc,
|
|
"expected function id within range [0, UINT_MAX)");
|
|
}
|
|
|
|
bool MasmParser::parseCVFileId(int64_t &FileNumber, StringRef DirectiveName) {
|
|
SMLoc Loc;
|
|
return parseTokenLoc(Loc) ||
|
|
parseIntToken(FileNumber, "expected integer in '" + DirectiveName +
|
|
"' directive") ||
|
|
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 MasmParser::parseDirectiveCVFuncId() {
|
|
SMLoc FunctionIdLoc = getTok().getLoc();
|
|
int64_t FunctionId;
|
|
|
|
if (parseCVFunctionId(FunctionId, ".cv_func_id") ||
|
|
parseToken(AsmToken::EndOfStatement,
|
|
"unexpected token in '.cv_func_id' directive"))
|
|
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 MasmParser::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 (parseToken(AsmToken::EndOfStatement,
|
|
"unexpected token in '.cv_inline_site_id' directive"))
|
|
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 MasmParser::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 MasmParser::parseDirectiveCVLinetable() {
|
|
int64_t FunctionId;
|
|
StringRef FnStartName, FnEndName;
|
|
SMLoc Loc = getTok().getLoc();
|
|
if (parseCVFunctionId(FunctionId, ".cv_linetable") ||
|
|
parseToken(AsmToken::Comma,
|
|
"unexpected token in '.cv_linetable' directive") ||
|
|
parseTokenLoc(Loc) || check(parseIdentifier(FnStartName), Loc,
|
|
"expected identifier in directive") ||
|
|
parseToken(AsmToken::Comma,
|
|
"unexpected token in '.cv_linetable' directive") ||
|
|
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 MasmParser::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 in '.cv_inline_linetable' directive") ||
|
|
check(SourceFileId <= 0, Loc,
|
|
"File id less than zero in '.cv_inline_linetable' directive") ||
|
|
parseTokenLoc(Loc) ||
|
|
parseIntToken(
|
|
SourceLineNum,
|
|
"expected SourceLineNum in '.cv_inline_linetable' directive") ||
|
|
check(SourceLineNum < 0, Loc,
|
|
"Line number less than zero in '.cv_inline_linetable' directive") ||
|
|
parseTokenLoc(Loc) || check(parseIdentifier(FnStartName), Loc,
|
|
"expected identifier in directive") ||
|
|
parseTokenLoc(Loc) || check(parseIdentifier(FnEndName), Loc,
|
|
"expected identifier in directive"))
|
|
return true;
|
|
|
|
if (parseToken(AsmToken::EndOfStatement, "Expected End of Statement"))
|
|
return true;
|
|
|
|
MCSymbol *FnStartSym = getContext().getOrCreateSymbol(FnStartName);
|
|
MCSymbol *FnEndSym = getContext().getOrCreateSymbol(FnEndName);
|
|
getStreamer().emitCVInlineLinetableDirective(PrimaryFunctionId, SourceFileId,
|
|
SourceLineNum, FnStartSym,
|
|
FnEndSym);
|
|
return false;
|
|
}
|
|
|
|
void MasmParser::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 MasmParser::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 MasmParser::parseDirectiveCVString() {
|
|
std::string Data;
|
|
if (checkForValidSection() || parseEscapedString(Data))
|
|
return addErrorSuffix(" in '.cv_string' directive");
|
|
|
|
// Put the string in the table and emit the offset.
|
|
std::pair<StringRef, unsigned> Insertion =
|
|
getCVContext().addToStringTable(Data);
|
|
getStreamer().emitIntValue(Insertion.second, 4);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCVStringTable
|
|
/// ::= .cv_stringtable
|
|
bool MasmParser::parseDirectiveCVStringTable() {
|
|
getStreamer().emitCVStringTableDirective();
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCVFileChecksums
|
|
/// ::= .cv_filechecksums
|
|
bool MasmParser::parseDirectiveCVFileChecksums() {
|
|
getStreamer().emitCVFileChecksumsDirective();
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCVFileChecksumOffset
|
|
/// ::= .cv_filechecksumoffset fileno
|
|
bool MasmParser::parseDirectiveCVFileChecksumOffset() {
|
|
int64_t FileNo;
|
|
if (parseIntToken(FileNo, "expected identifier in directive"))
|
|
return true;
|
|
if (parseToken(AsmToken::EndOfStatement, "Expected End of Statement"))
|
|
return true;
|
|
getStreamer().emitCVFileChecksumOffsetDirective(FileNo);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCVFPOData
|
|
/// ::= .cv_fpo_data procsym
|
|
bool MasmParser::parseDirectiveCVFPOData() {
|
|
SMLoc DirLoc = getLexer().getLoc();
|
|
StringRef ProcName;
|
|
if (parseIdentifier(ProcName))
|
|
return TokError("expected symbol name");
|
|
if (parseEOL("unexpected tokens"))
|
|
return addErrorSuffix(" in '.cv_fpo_data' directive");
|
|
MCSymbol *ProcSym = getContext().getOrCreateSymbol(ProcName);
|
|
getStreamer().EmitCVFPOData(ProcSym, DirLoc);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFISections
|
|
/// ::= .cfi_sections section [, section]
|
|
bool MasmParser::parseDirectiveCFISections() {
|
|
StringRef Name;
|
|
bool EH = false;
|
|
bool Debug = false;
|
|
|
|
if (parseIdentifier(Name))
|
|
return TokError("Expected an identifier");
|
|
|
|
if (Name == ".eh_frame")
|
|
EH = true;
|
|
else if (Name == ".debug_frame")
|
|
Debug = true;
|
|
|
|
if (getLexer().is(AsmToken::Comma)) {
|
|
Lex();
|
|
|
|
if (parseIdentifier(Name))
|
|
return TokError("Expected an identifier");
|
|
|
|
if (Name == ".eh_frame")
|
|
EH = true;
|
|
else if (Name == ".debug_frame")
|
|
Debug = true;
|
|
}
|
|
|
|
getStreamer().emitCFISections(EH, Debug);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIStartProc
|
|
/// ::= .cfi_startproc [simple]
|
|
bool MasmParser::parseDirectiveCFIStartProc() {
|
|
StringRef Simple;
|
|
if (!parseOptionalToken(AsmToken::EndOfStatement)) {
|
|
if (check(parseIdentifier(Simple) || Simple != "simple",
|
|
"unexpected token") ||
|
|
parseToken(AsmToken::EndOfStatement))
|
|
return addErrorSuffix(" in '.cfi_startproc' directive");
|
|
}
|
|
|
|
// 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 MasmParser::parseDirectiveCFIEndProc() {
|
|
getStreamer().emitCFIEndProc();
|
|
return false;
|
|
}
|
|
|
|
/// parse register name or number.
|
|
bool MasmParser::parseRegisterOrRegisterNumber(int64_t &Register,
|
|
SMLoc DirectiveLoc) {
|
|
unsigned 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 MasmParser::parseDirectiveCFIDefCfa(SMLoc DirectiveLoc) {
|
|
int64_t Register = 0, Offset = 0;
|
|
if (parseRegisterOrRegisterNumber(Register, DirectiveLoc) ||
|
|
parseToken(AsmToken::Comma, "unexpected token in directive") ||
|
|
parseAbsoluteExpression(Offset))
|
|
return true;
|
|
|
|
getStreamer().emitCFIDefCfa(Register, Offset);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIDefCfaOffset
|
|
/// ::= .cfi_def_cfa_offset offset
|
|
bool MasmParser::parseDirectiveCFIDefCfaOffset() {
|
|
int64_t Offset = 0;
|
|
if (parseAbsoluteExpression(Offset))
|
|
return true;
|
|
|
|
getStreamer().emitCFIDefCfaOffset(Offset);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIRegister
|
|
/// ::= .cfi_register register, register
|
|
bool MasmParser::parseDirectiveCFIRegister(SMLoc DirectiveLoc) {
|
|
int64_t Register1 = 0, Register2 = 0;
|
|
if (parseRegisterOrRegisterNumber(Register1, DirectiveLoc) ||
|
|
parseToken(AsmToken::Comma, "unexpected token in directive") ||
|
|
parseRegisterOrRegisterNumber(Register2, DirectiveLoc))
|
|
return true;
|
|
|
|
getStreamer().emitCFIRegister(Register1, Register2);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIWindowSave
|
|
/// ::= .cfi_window_save
|
|
bool MasmParser::parseDirectiveCFIWindowSave() {
|
|
getStreamer().emitCFIWindowSave();
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIAdjustCfaOffset
|
|
/// ::= .cfi_adjust_cfa_offset adjustment
|
|
bool MasmParser::parseDirectiveCFIAdjustCfaOffset() {
|
|
int64_t Adjustment = 0;
|
|
if (parseAbsoluteExpression(Adjustment))
|
|
return true;
|
|
|
|
getStreamer().emitCFIAdjustCfaOffset(Adjustment);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIDefCfaRegister
|
|
/// ::= .cfi_def_cfa_register register
|
|
bool MasmParser::parseDirectiveCFIDefCfaRegister(SMLoc DirectiveLoc) {
|
|
int64_t Register = 0;
|
|
if (parseRegisterOrRegisterNumber(Register, DirectiveLoc))
|
|
return true;
|
|
|
|
getStreamer().emitCFIDefCfaRegister(Register);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIOffset
|
|
/// ::= .cfi_offset register, offset
|
|
bool MasmParser::parseDirectiveCFIOffset(SMLoc DirectiveLoc) {
|
|
int64_t Register = 0;
|
|
int64_t Offset = 0;
|
|
|
|
if (parseRegisterOrRegisterNumber(Register, DirectiveLoc) ||
|
|
parseToken(AsmToken::Comma, "unexpected token in directive") ||
|
|
parseAbsoluteExpression(Offset))
|
|
return true;
|
|
|
|
getStreamer().emitCFIOffset(Register, Offset);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIRelOffset
|
|
/// ::= .cfi_rel_offset register, offset
|
|
bool MasmParser::parseDirectiveCFIRelOffset(SMLoc DirectiveLoc) {
|
|
int64_t Register = 0, Offset = 0;
|
|
|
|
if (parseRegisterOrRegisterNumber(Register, DirectiveLoc) ||
|
|
parseToken(AsmToken::Comma, "unexpected token in directive") ||
|
|
parseAbsoluteExpression(Offset))
|
|
return true;
|
|
|
|
getStreamer().emitCFIRelOffset(Register, Offset);
|
|
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 MasmParser::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.") ||
|
|
parseToken(AsmToken::Comma, "unexpected token in directive") ||
|
|
check(parseIdentifier(Name), "expected identifier in directive"))
|
|
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 MasmParser::parseDirectiveCFIRememberState() {
|
|
getStreamer().emitCFIRememberState();
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIRestoreState
|
|
/// ::= .cfi_remember_state
|
|
bool MasmParser::parseDirectiveCFIRestoreState() {
|
|
getStreamer().emitCFIRestoreState();
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFISameValue
|
|
/// ::= .cfi_same_value register
|
|
bool MasmParser::parseDirectiveCFISameValue(SMLoc DirectiveLoc) {
|
|
int64_t Register = 0;
|
|
|
|
if (parseRegisterOrRegisterNumber(Register, DirectiveLoc))
|
|
return true;
|
|
|
|
getStreamer().emitCFISameValue(Register);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIRestore
|
|
/// ::= .cfi_restore register
|
|
bool MasmParser::parseDirectiveCFIRestore(SMLoc DirectiveLoc) {
|
|
int64_t Register = 0;
|
|
if (parseRegisterOrRegisterNumber(Register, DirectiveLoc))
|
|
return true;
|
|
|
|
getStreamer().emitCFIRestore(Register);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIEscape
|
|
/// ::= .cfi_escape expression[,...]
|
|
bool MasmParser::parseDirectiveCFIEscape() {
|
|
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);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIReturnColumn
|
|
/// ::= .cfi_return_column register
|
|
bool MasmParser::parseDirectiveCFIReturnColumn(SMLoc DirectiveLoc) {
|
|
int64_t Register = 0;
|
|
if (parseRegisterOrRegisterNumber(Register, DirectiveLoc))
|
|
return true;
|
|
getStreamer().emitCFIReturnColumn(Register);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFISignalFrame
|
|
/// ::= .cfi_signal_frame
|
|
bool MasmParser::parseDirectiveCFISignalFrame() {
|
|
if (parseToken(AsmToken::EndOfStatement,
|
|
"unexpected token in '.cfi_signal_frame'"))
|
|
return true;
|
|
|
|
getStreamer().emitCFISignalFrame();
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveCFIUndefined
|
|
/// ::= .cfi_undefined register
|
|
bool MasmParser::parseDirectiveCFIUndefined(SMLoc DirectiveLoc) {
|
|
int64_t Register = 0;
|
|
|
|
if (parseRegisterOrRegisterNumber(Register, DirectiveLoc))
|
|
return true;
|
|
|
|
getStreamer().emitCFIUndefined(Register);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveMacro
|
|
/// ::= name macro [parameters]
|
|
/// ["LOCAL" identifiers]
|
|
/// parameters ::= parameter [, parameter]*
|
|
/// parameter ::= name ":" qualifier
|
|
/// qualifier ::= "req" | "vararg" | "=" macro_argument
|
|
bool MasmParser::parseDirectiveMacro(StringRef Name, SMLoc NameLoc) {
|
|
MCAsmMacroParameters Parameters;
|
|
while (getLexer().isNot(AsmToken::EndOfStatement)) {
|
|
if (!Parameters.empty() && Parameters.back().Vararg)
|
|
return Error(Lexer.getLoc(),
|
|
"Vararg parameter '" + Parameters.back().Name +
|
|
"' should be last in the list of parameters");
|
|
|
|
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.equals_insensitive(Parameter.Name))
|
|
return TokError("macro '" + Name + "' has multiple parameters"
|
|
" named '" + Parameter.Name + "'");
|
|
|
|
if (Lexer.is(AsmToken::Colon)) {
|
|
Lex(); // consume ':'
|
|
|
|
if (parseOptionalToken(AsmToken::Equal)) {
|
|
// Default value
|
|
SMLoc ParamLoc;
|
|
|
|
ParamLoc = Lexer.getLoc();
|
|
if (parseMacroArgument(nullptr, Parameter.Value))
|
|
return true;
|
|
} else {
|
|
SMLoc QualLoc;
|
|
StringRef Qualifier;
|
|
|
|
QualLoc = Lexer.getLoc();
|
|
if (parseIdentifier(Qualifier))
|
|
return Error(QualLoc, "missing parameter qualifier for "
|
|
"'" +
|
|
Parameter.Name + "' in macro '" + Name +
|
|
"'");
|
|
|
|
if (Qualifier.equals_insensitive("req"))
|
|
Parameter.Required = true;
|
|
else if (Qualifier.equals_insensitive("vararg"))
|
|
Parameter.Vararg = true;
|
|
else
|
|
return Error(QualLoc,
|
|
Qualifier + " is not a valid parameter qualifier for '" +
|
|
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();
|
|
|
|
std::vector<std::string> Locals;
|
|
if (getTok().is(AsmToken::Identifier) &&
|
|
getTok().getIdentifier().equals_insensitive("local")) {
|
|
Lex(); // Eat the LOCAL directive.
|
|
|
|
StringRef ID;
|
|
while (true) {
|
|
if (parseIdentifier(ID))
|
|
return true;
|
|
Locals.push_back(ID.lower());
|
|
|
|
// If we see a comma, continue (and allow line continuation).
|
|
if (!parseOptionalToken(AsmToken::Comma))
|
|
break;
|
|
parseOptionalToken(AsmToken::EndOfStatement);
|
|
}
|
|
}
|
|
|
|
// Consuming deferred text, so use Lexer.Lex to ignore Lexing Errors.
|
|
AsmToken EndToken, StartToken = getTok();
|
|
unsigned MacroDepth = 0;
|
|
bool IsMacroFunction = false;
|
|
// 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(NameLoc, "no matching 'endm' in definition");
|
|
|
|
// Otherwise, check whether we have reached the 'endm'... and determine if
|
|
// this is a macro function.
|
|
if (getLexer().is(AsmToken::Identifier)) {
|
|
if (getTok().getIdentifier().equals_insensitive("endm")) {
|
|
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().equals_insensitive("exitm")) {
|
|
if (MacroDepth == 0 && peekTok().isNot(AsmToken::EndOfStatement)) {
|
|
IsMacroFunction = true;
|
|
}
|
|
} else if (isMacroLikeDirective()) {
|
|
// We allow nested macros. Those aren't instantiated until the
|
|
// outermost macro is expanded so just ignore them for now.
|
|
++MacroDepth;
|
|
}
|
|
}
|
|
|
|
// Otherwise, scan til the end of the statement.
|
|
eatToEndOfStatement();
|
|
}
|
|
|
|
if (getContext().lookupMacro(Name.lower())) {
|
|
return Error(NameLoc, "macro '" + Name + "' is already defined");
|
|
}
|
|
|
|
const char *BodyStart = StartToken.getLoc().getPointer();
|
|
const char *BodyEnd = EndToken.getLoc().getPointer();
|
|
StringRef Body = StringRef(BodyStart, BodyEnd - BodyStart);
|
|
MCAsmMacro Macro(Name, Body, std::move(Parameters), std::move(Locals),
|
|
IsMacroFunction);
|
|
DEBUG_WITH_TYPE("asm-macros", dbgs() << "Defining new macro:\n";
|
|
Macro.dump());
|
|
getContext().defineMacro(Name, std::move(Macro));
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveExitMacro
|
|
/// ::= "exitm" [textitem]
|
|
bool MasmParser::parseDirectiveExitMacro(SMLoc DirectiveLoc,
|
|
StringRef Directive,
|
|
std::string &Value) {
|
|
SMLoc EndLoc = getTok().getLoc();
|
|
if (getTok().isNot(AsmToken::EndOfStatement) && parseTextItem(Value))
|
|
return Error(EndLoc,
|
|
"unable to parse text item in '" + Directive + "' directive");
|
|
eatToEndOfStatement();
|
|
|
|
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
|
|
bool MasmParser::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
|
|
/// ::= purge identifier ( , identifier )*
|
|
bool MasmParser::parseDirectivePurgeMacro(SMLoc DirectiveLoc) {
|
|
StringRef Name;
|
|
while (true) {
|
|
SMLoc NameLoc;
|
|
if (parseTokenLoc(NameLoc) ||
|
|
check(parseIdentifier(Name), NameLoc,
|
|
"expected identifier in 'purge' directive"))
|
|
return true;
|
|
|
|
DEBUG_WITH_TYPE("asm-macros", dbgs()
|
|
<< "Un-defining macro: " << Name << "\n");
|
|
if (!getContext().lookupMacro(Name.lower()))
|
|
return Error(NameLoc, "macro '" + Name + "' is not defined");
|
|
getContext().undefineMacro(Name.lower());
|
|
|
|
if (!parseOptionalToken(AsmToken::Comma))
|
|
break;
|
|
parseOptionalToken(AsmToken::EndOfStatement);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveSymbolAttribute
|
|
/// ::= { ".globl", ".weak", ... } [ identifier ( , identifier )* ]
|
|
bool MasmParser::parseDirectiveSymbolAttribute(MCSymbolAttr Attr) {
|
|
auto parseOp = [&]() -> bool {
|
|
StringRef Name;
|
|
SMLoc Loc = getTok().getLoc();
|
|
if (parseIdentifier(Name))
|
|
return Error(Loc, "expected identifier");
|
|
MCSymbol *Sym = getContext().getOrCreateSymbol(Name);
|
|
|
|
// Assembler local symbols don't make any sense here. Complain loudly.
|
|
if (Sym->isTemporary())
|
|
return Error(Loc, "non-local symbol required");
|
|
|
|
if (!getStreamer().emitSymbolAttribute(Sym, Attr))
|
|
return Error(Loc, "unable to emit symbol attribute");
|
|
return false;
|
|
};
|
|
|
|
if (parseMany(parseOp))
|
|
return addErrorSuffix(" in directive");
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveComm
|
|
/// ::= ( .comm | .lcomm ) identifier , size_expression [ , align_expression ]
|
|
bool MasmParser::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 (getLexer().isNot(AsmToken::Comma))
|
|
return TokError("unexpected token in directive");
|
|
Lex();
|
|
|
|
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 (parseToken(AsmToken::EndOfStatement,
|
|
"unexpected token in '.comm' or '.lcomm' directive"))
|
|
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, "invalid '.comm' or '.lcomm' directive size, can't "
|
|
"be less than zero");
|
|
|
|
// NOTE: The alignment in the directive is a power of 2 value, the assembler
|
|
// may internally end up wanting an alignment in bytes.
|
|
// FIXME: Diagnose overflow.
|
|
if (Pow2Alignment < 0)
|
|
return Error(Pow2AlignmentLoc, "invalid '.comm' or '.lcomm' directive "
|
|
"alignment, can't be less than zero");
|
|
|
|
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, 1 << Pow2Alignment);
|
|
return false;
|
|
}
|
|
|
|
getStreamer().emitCommonSymbol(Sym, Size, 1 << Pow2Alignment);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveComment
|
|
/// ::= comment delimiter [[text]]
|
|
/// [[text]]
|
|
/// [[text]] delimiter [[text]]
|
|
bool MasmParser::parseDirectiveComment(SMLoc DirectiveLoc) {
|
|
std::string FirstLine = parseStringTo(AsmToken::EndOfStatement);
|
|
size_t DelimiterEnd = FirstLine.find_first_of("\b\t\v\f\r\x1A ");
|
|
StringRef Delimiter = StringRef(FirstLine).take_front(DelimiterEnd);
|
|
if (Delimiter.empty())
|
|
return Error(DirectiveLoc, "no delimiter in 'comment' directive");
|
|
do {
|
|
if (getTok().is(AsmToken::Eof))
|
|
return Error(DirectiveLoc, "unmatched delimiter in 'comment' directive");
|
|
Lex(); // eat end of statement
|
|
} while (
|
|
!StringRef(parseStringTo(AsmToken::EndOfStatement)).contains(Delimiter));
|
|
return parseToken(AsmToken::EndOfStatement,
|
|
"unexpected token in 'comment' directive");
|
|
}
|
|
|
|
/// parseDirectiveInclude
|
|
/// ::= include <filename>
|
|
/// | include filename
|
|
bool MasmParser::parseDirectiveInclude() {
|
|
// Allow the strings to have escaped octal character sequence.
|
|
std::string Filename;
|
|
SMLoc IncludeLoc = getTok().getLoc();
|
|
|
|
if (parseAngleBracketString(Filename))
|
|
Filename = parseStringTo(AsmToken::EndOfStatement);
|
|
if (check(Filename.empty(), "missing filename in 'include' directive") ||
|
|
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;
|
|
}
|
|
|
|
/// parseDirectiveIf
|
|
/// ::= .if{,eq,ge,gt,le,lt,ne} expression
|
|
bool MasmParser::parseDirectiveIf(SMLoc DirectiveLoc, DirectiveKind DirKind) {
|
|
TheCondStack.push_back(TheCondState);
|
|
TheCondState.TheCond = AsmCond::IfCond;
|
|
if (TheCondState.Ignore) {
|
|
eatToEndOfStatement();
|
|
} else {
|
|
int64_t ExprValue;
|
|
if (parseAbsoluteExpression(ExprValue) ||
|
|
parseToken(AsmToken::EndOfStatement,
|
|
"unexpected token in '.if' directive"))
|
|
return true;
|
|
|
|
switch (DirKind) {
|
|
default:
|
|
llvm_unreachable("unsupported directive");
|
|
case DK_IF:
|
|
break;
|
|
case DK_IFE:
|
|
ExprValue = ExprValue == 0;
|
|
break;
|
|
}
|
|
|
|
TheCondState.CondMet = ExprValue;
|
|
TheCondState.Ignore = !TheCondState.CondMet;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveIfb
|
|
/// ::= .ifb textitem
|
|
bool MasmParser::parseDirectiveIfb(SMLoc DirectiveLoc, bool ExpectBlank) {
|
|
TheCondStack.push_back(TheCondState);
|
|
TheCondState.TheCond = AsmCond::IfCond;
|
|
|
|
if (TheCondState.Ignore) {
|
|
eatToEndOfStatement();
|
|
} else {
|
|
std::string Str;
|
|
if (parseTextItem(Str))
|
|
return TokError("expected text item parameter for 'ifb' directive");
|
|
|
|
if (parseToken(AsmToken::EndOfStatement,
|
|
"unexpected token in 'ifb' directive"))
|
|
return true;
|
|
|
|
TheCondState.CondMet = ExpectBlank == Str.empty();
|
|
TheCondState.Ignore = !TheCondState.CondMet;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveIfidn
|
|
/// ::= ifidn textitem, textitem
|
|
bool MasmParser::parseDirectiveIfidn(SMLoc DirectiveLoc, bool ExpectEqual,
|
|
bool CaseInsensitive) {
|
|
std::string String1, String2;
|
|
|
|
if (parseTextItem(String1)) {
|
|
if (ExpectEqual)
|
|
return TokError("expected text item parameter for 'ifidn' directive");
|
|
return TokError("expected text item parameter for 'ifdif' directive");
|
|
}
|
|
|
|
if (Lexer.isNot(AsmToken::Comma)) {
|
|
if (ExpectEqual)
|
|
return TokError(
|
|
"expected comma after first string for 'ifidn' directive");
|
|
return TokError("expected comma after first string for 'ifdif' directive");
|
|
}
|
|
Lex();
|
|
|
|
if (parseTextItem(String2)) {
|
|
if (ExpectEqual)
|
|
return TokError("expected text item parameter for 'ifidn' directive");
|
|
return TokError("expected text item parameter for 'ifdif' directive");
|
|
}
|
|
|
|
TheCondStack.push_back(TheCondState);
|
|
TheCondState.TheCond = AsmCond::IfCond;
|
|
if (CaseInsensitive)
|
|
TheCondState.CondMet =
|
|
ExpectEqual == (StringRef(String1).equals_insensitive(String2));
|
|
else
|
|
TheCondState.CondMet = ExpectEqual == (String1 == String2);
|
|
TheCondState.Ignore = !TheCondState.CondMet;
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveIfdef
|
|
/// ::= ifdef symbol
|
|
/// | ifdef variable
|
|
bool MasmParser::parseDirectiveIfdef(SMLoc DirectiveLoc, bool expect_defined) {
|
|
TheCondStack.push_back(TheCondState);
|
|
TheCondState.TheCond = AsmCond::IfCond;
|
|
|
|
if (TheCondState.Ignore) {
|
|
eatToEndOfStatement();
|
|
} else {
|
|
bool is_defined = false;
|
|
unsigned RegNo;
|
|
SMLoc StartLoc, EndLoc;
|
|
is_defined = (getTargetParser().tryParseRegister(
|
|
RegNo, StartLoc, EndLoc) == MatchOperand_Success);
|
|
if (!is_defined) {
|
|
StringRef Name;
|
|
if (check(parseIdentifier(Name), "expected identifier after 'ifdef'") ||
|
|
parseToken(AsmToken::EndOfStatement, "unexpected token in 'ifdef'"))
|
|
return true;
|
|
|
|
if (Variables.find(Name.lower()) != Variables.end()) {
|
|
is_defined = true;
|
|
} else {
|
|
MCSymbol *Sym = getContext().lookupSymbol(Name.lower());
|
|
is_defined = (Sym && !Sym->isUndefined(false));
|
|
}
|
|
}
|
|
|
|
TheCondState.CondMet = (is_defined == expect_defined);
|
|
TheCondState.Ignore = !TheCondState.CondMet;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveElseIf
|
|
/// ::= elseif expression
|
|
bool MasmParser::parseDirectiveElseIf(SMLoc DirectiveLoc,
|
|
DirectiveKind DirKind) {
|
|
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 (parseToken(AsmToken::EndOfStatement,
|
|
"unexpected token in '.elseif' directive"))
|
|
return true;
|
|
|
|
switch (DirKind) {
|
|
default:
|
|
llvm_unreachable("unsupported directive");
|
|
case DK_ELSEIF:
|
|
break;
|
|
case DK_ELSEIFE:
|
|
ExprValue = ExprValue == 0;
|
|
break;
|
|
}
|
|
|
|
TheCondState.CondMet = ExprValue;
|
|
TheCondState.Ignore = !TheCondState.CondMet;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveElseIfb
|
|
/// ::= elseifb textitem
|
|
bool MasmParser::parseDirectiveElseIfb(SMLoc DirectiveLoc, bool ExpectBlank) {
|
|
if (TheCondState.TheCond != AsmCond::IfCond &&
|
|
TheCondState.TheCond != AsmCond::ElseIfCond)
|
|
return Error(DirectiveLoc, "Encountered an 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 {
|
|
std::string Str;
|
|
if (parseTextItem(Str)) {
|
|
if (ExpectBlank)
|
|
return TokError("expected text item parameter for 'elseifb' directive");
|
|
return TokError("expected text item parameter for 'elseifnb' directive");
|
|
}
|
|
|
|
if (parseToken(AsmToken::EndOfStatement,
|
|
"unexpected token in 'elseifb' directive"))
|
|
return true;
|
|
|
|
TheCondState.CondMet = ExpectBlank == Str.empty();
|
|
TheCondState.Ignore = !TheCondState.CondMet;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveElseIfdef
|
|
/// ::= elseifdef symbol
|
|
/// | elseifdef variable
|
|
bool MasmParser::parseDirectiveElseIfdef(SMLoc DirectiveLoc,
|
|
bool expect_defined) {
|
|
if (TheCondState.TheCond != AsmCond::IfCond &&
|
|
TheCondState.TheCond != AsmCond::ElseIfCond)
|
|
return Error(DirectiveLoc, "Encountered an 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 {
|
|
bool is_defined = false;
|
|
unsigned RegNo;
|
|
SMLoc StartLoc, EndLoc;
|
|
is_defined = (getTargetParser().tryParseRegister(RegNo, StartLoc, EndLoc) ==
|
|
MatchOperand_Success);
|
|
if (!is_defined) {
|
|
StringRef Name;
|
|
if (check(parseIdentifier(Name),
|
|
"expected identifier after 'elseifdef'") ||
|
|
parseToken(AsmToken::EndOfStatement,
|
|
"unexpected token in 'elseifdef'"))
|
|
return true;
|
|
|
|
if (Variables.find(Name.lower()) != Variables.end()) {
|
|
is_defined = true;
|
|
} else {
|
|
MCSymbol *Sym = getContext().lookupSymbol(Name);
|
|
is_defined = (Sym && !Sym->isUndefined(false));
|
|
}
|
|
}
|
|
|
|
TheCondState.CondMet = (is_defined == expect_defined);
|
|
TheCondState.Ignore = !TheCondState.CondMet;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveElseIfidn
|
|
/// ::= elseifidn textitem, textitem
|
|
bool MasmParser::parseDirectiveElseIfidn(SMLoc DirectiveLoc, bool ExpectEqual,
|
|
bool CaseInsensitive) {
|
|
if (TheCondState.TheCond != AsmCond::IfCond &&
|
|
TheCondState.TheCond != AsmCond::ElseIfCond)
|
|
return Error(DirectiveLoc, "Encountered an 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 {
|
|
std::string String1, String2;
|
|
|
|
if (parseTextItem(String1)) {
|
|
if (ExpectEqual)
|
|
return TokError(
|
|
"expected text item parameter for 'elseifidn' directive");
|
|
return TokError("expected text item parameter for 'elseifdif' directive");
|
|
}
|
|
|
|
if (Lexer.isNot(AsmToken::Comma)) {
|
|
if (ExpectEqual)
|
|
return TokError(
|
|
"expected comma after first string for 'elseifidn' directive");
|
|
return TokError(
|
|
"expected comma after first string for 'elseifdif' directive");
|
|
}
|
|
Lex();
|
|
|
|
if (parseTextItem(String2)) {
|
|
if (ExpectEqual)
|
|
return TokError(
|
|
"expected text item parameter for 'elseifidn' directive");
|
|
return TokError("expected text item parameter for 'elseifdif' directive");
|
|
}
|
|
|
|
if (CaseInsensitive)
|
|
TheCondState.CondMet =
|
|
ExpectEqual == (StringRef(String1).equals_insensitive(String2));
|
|
else
|
|
TheCondState.CondMet = ExpectEqual == (String1 == String2);
|
|
TheCondState.Ignore = !TheCondState.CondMet;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveElse
|
|
/// ::= else
|
|
bool MasmParser::parseDirectiveElse(SMLoc DirectiveLoc) {
|
|
if (parseToken(AsmToken::EndOfStatement,
|
|
"unexpected token in 'else' directive"))
|
|
return true;
|
|
|
|
if (TheCondState.TheCond != AsmCond::IfCond &&
|
|
TheCondState.TheCond != AsmCond::ElseIfCond)
|
|
return Error(DirectiveLoc, "Encountered an 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 MasmParser::parseDirectiveEnd(SMLoc DirectiveLoc) {
|
|
if (parseToken(AsmToken::EndOfStatement,
|
|
"unexpected token in 'end' directive"))
|
|
return true;
|
|
|
|
while (Lexer.isNot(AsmToken::Eof))
|
|
Lexer.Lex();
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveError
|
|
/// ::= .err [message]
|
|
bool MasmParser::parseDirectiveError(SMLoc DirectiveLoc) {
|
|
if (!TheCondStack.empty()) {
|
|
if (TheCondStack.back().Ignore) {
|
|
eatToEndOfStatement();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::string Message = ".err directive invoked in source file";
|
|
if (Lexer.isNot(AsmToken::EndOfStatement))
|
|
Message = parseStringTo(AsmToken::EndOfStatement);
|
|
Lex();
|
|
|
|
return Error(DirectiveLoc, Message);
|
|
}
|
|
|
|
/// parseDirectiveErrorIfb
|
|
/// ::= .errb textitem[, message]
|
|
bool MasmParser::parseDirectiveErrorIfb(SMLoc DirectiveLoc, bool ExpectBlank) {
|
|
if (!TheCondStack.empty()) {
|
|
if (TheCondStack.back().Ignore) {
|
|
eatToEndOfStatement();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::string Text;
|
|
if (parseTextItem(Text))
|
|
return Error(getTok().getLoc(), "missing text item in '.errb' directive");
|
|
|
|
std::string Message = ".errb directive invoked in source file";
|
|
if (Lexer.isNot(AsmToken::EndOfStatement)) {
|
|
if (parseToken(AsmToken::Comma))
|
|
return addErrorSuffix(" in '.errb' directive");
|
|
Message = parseStringTo(AsmToken::EndOfStatement);
|
|
}
|
|
Lex();
|
|
|
|
if (Text.empty() == ExpectBlank)
|
|
return Error(DirectiveLoc, Message);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveErrorIfdef
|
|
/// ::= .errdef name[, message]
|
|
bool MasmParser::parseDirectiveErrorIfdef(SMLoc DirectiveLoc,
|
|
bool ExpectDefined) {
|
|
if (!TheCondStack.empty()) {
|
|
if (TheCondStack.back().Ignore) {
|
|
eatToEndOfStatement();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool IsDefined = false;
|
|
unsigned RegNo;
|
|
SMLoc StartLoc, EndLoc;
|
|
IsDefined = (getTargetParser().tryParseRegister(RegNo, StartLoc, EndLoc) ==
|
|
MatchOperand_Success);
|
|
if (!IsDefined) {
|
|
StringRef Name;
|
|
if (check(parseIdentifier(Name), "expected identifier after '.errdef'"))
|
|
return true;
|
|
|
|
if (Variables.find(Name.lower()) != Variables.end()) {
|
|
IsDefined = true;
|
|
} else {
|
|
MCSymbol *Sym = getContext().lookupSymbol(Name);
|
|
IsDefined = (Sym && !Sym->isUndefined(false));
|
|
}
|
|
}
|
|
|
|
std::string Message = ".errdef directive invoked in source file";
|
|
if (Lexer.isNot(AsmToken::EndOfStatement)) {
|
|
if (parseToken(AsmToken::Comma))
|
|
return addErrorSuffix(" in '.errdef' directive");
|
|
Message = parseStringTo(AsmToken::EndOfStatement);
|
|
}
|
|
Lex();
|
|
|
|
if (IsDefined == ExpectDefined)
|
|
return Error(DirectiveLoc, Message);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveErrorIfidn
|
|
/// ::= .erridn textitem, textitem[, message]
|
|
bool MasmParser::parseDirectiveErrorIfidn(SMLoc DirectiveLoc, bool ExpectEqual,
|
|
bool CaseInsensitive) {
|
|
if (!TheCondStack.empty()) {
|
|
if (TheCondStack.back().Ignore) {
|
|
eatToEndOfStatement();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::string String1, String2;
|
|
|
|
if (parseTextItem(String1)) {
|
|
if (ExpectEqual)
|
|
return TokError("expected string parameter for '.erridn' directive");
|
|
return TokError("expected string parameter for '.errdif' directive");
|
|
}
|
|
|
|
if (Lexer.isNot(AsmToken::Comma)) {
|
|
if (ExpectEqual)
|
|
return TokError(
|
|
"expected comma after first string for '.erridn' directive");
|
|
return TokError(
|
|
"expected comma after first string for '.errdif' directive");
|
|
}
|
|
Lex();
|
|
|
|
if (parseTextItem(String2)) {
|
|
if (ExpectEqual)
|
|
return TokError("expected string parameter for '.erridn' directive");
|
|
return TokError("expected string parameter for '.errdif' directive");
|
|
}
|
|
|
|
std::string Message;
|
|
if (ExpectEqual)
|
|
Message = ".erridn directive invoked in source file";
|
|
else
|
|
Message = ".errdif directive invoked in source file";
|
|
if (Lexer.isNot(AsmToken::EndOfStatement)) {
|
|
if (parseToken(AsmToken::Comma))
|
|
return addErrorSuffix(" in '.erridn' directive");
|
|
Message = parseStringTo(AsmToken::EndOfStatement);
|
|
}
|
|
Lex();
|
|
|
|
if (CaseInsensitive)
|
|
TheCondState.CondMet =
|
|
ExpectEqual == (StringRef(String1).equals_insensitive(String2));
|
|
else
|
|
TheCondState.CondMet = ExpectEqual == (String1 == String2);
|
|
TheCondState.Ignore = !TheCondState.CondMet;
|
|
|
|
if ((CaseInsensitive &&
|
|
ExpectEqual == StringRef(String1).equals_insensitive(String2)) ||
|
|
(ExpectEqual == (String1 == String2)))
|
|
return Error(DirectiveLoc, Message);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveErrorIfe
|
|
/// ::= .erre expression[, message]
|
|
bool MasmParser::parseDirectiveErrorIfe(SMLoc DirectiveLoc, bool ExpectZero) {
|
|
if (!TheCondStack.empty()) {
|
|
if (TheCondStack.back().Ignore) {
|
|
eatToEndOfStatement();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int64_t ExprValue;
|
|
if (parseAbsoluteExpression(ExprValue))
|
|
return addErrorSuffix(" in '.erre' directive");
|
|
|
|
std::string Message = ".erre directive invoked in source file";
|
|
if (Lexer.isNot(AsmToken::EndOfStatement)) {
|
|
if (parseToken(AsmToken::Comma))
|
|
return addErrorSuffix(" in '.erre' directive");
|
|
Message = parseStringTo(AsmToken::EndOfStatement);
|
|
}
|
|
Lex();
|
|
|
|
if ((ExprValue == 0) == ExpectZero)
|
|
return Error(DirectiveLoc, Message);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveEndIf
|
|
/// ::= .endif
|
|
bool MasmParser::parseDirectiveEndIf(SMLoc DirectiveLoc) {
|
|
if (parseToken(AsmToken::EndOfStatement,
|
|
"unexpected token in '.endif' directive"))
|
|
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 MasmParser::initializeDirectiveKindMap() {
|
|
DirectiveKindMap["="] = DK_ASSIGN;
|
|
DirectiveKindMap["equ"] = DK_EQU;
|
|
DirectiveKindMap["textequ"] = DK_TEXTEQU;
|
|
// DirectiveKindMap[".ascii"] = DK_ASCII;
|
|
// DirectiveKindMap[".asciz"] = DK_ASCIZ;
|
|
// DirectiveKindMap[".string"] = DK_STRING;
|
|
DirectiveKindMap["byte"] = DK_BYTE;
|
|
DirectiveKindMap["sbyte"] = DK_SBYTE;
|
|
DirectiveKindMap["word"] = DK_WORD;
|
|
DirectiveKindMap["sword"] = DK_SWORD;
|
|
DirectiveKindMap["dword"] = DK_DWORD;
|
|
DirectiveKindMap["sdword"] = DK_SDWORD;
|
|
DirectiveKindMap["fword"] = DK_FWORD;
|
|
DirectiveKindMap["qword"] = DK_QWORD;
|
|
DirectiveKindMap["sqword"] = DK_SQWORD;
|
|
DirectiveKindMap["real4"] = DK_REAL4;
|
|
DirectiveKindMap["real8"] = DK_REAL8;
|
|
DirectiveKindMap["real10"] = DK_REAL10;
|
|
DirectiveKindMap["align"] = DK_ALIGN;
|
|
DirectiveKindMap["even"] = DK_EVEN;
|
|
DirectiveKindMap["org"] = DK_ORG;
|
|
DirectiveKindMap["extern"] = DK_EXTERN;
|
|
DirectiveKindMap["public"] = DK_PUBLIC;
|
|
// DirectiveKindMap[".comm"] = DK_COMM;
|
|
DirectiveKindMap["comment"] = DK_COMMENT;
|
|
DirectiveKindMap["include"] = DK_INCLUDE;
|
|
DirectiveKindMap["repeat"] = DK_REPEAT;
|
|
DirectiveKindMap["rept"] = DK_REPEAT;
|
|
DirectiveKindMap["while"] = DK_WHILE;
|
|
DirectiveKindMap["for"] = DK_FOR;
|
|
DirectiveKindMap["irp"] = DK_FOR;
|
|
DirectiveKindMap["forc"] = DK_FORC;
|
|
DirectiveKindMap["irpc"] = DK_FORC;
|
|
DirectiveKindMap["if"] = DK_IF;
|
|
DirectiveKindMap["ife"] = DK_IFE;
|
|
DirectiveKindMap["ifb"] = DK_IFB;
|
|
DirectiveKindMap["ifnb"] = DK_IFNB;
|
|
DirectiveKindMap["ifdef"] = DK_IFDEF;
|
|
DirectiveKindMap["ifndef"] = DK_IFNDEF;
|
|
DirectiveKindMap["ifdif"] = DK_IFDIF;
|
|
DirectiveKindMap["ifdifi"] = DK_IFDIFI;
|
|
DirectiveKindMap["ifidn"] = DK_IFIDN;
|
|
DirectiveKindMap["ifidni"] = DK_IFIDNI;
|
|
DirectiveKindMap["elseif"] = DK_ELSEIF;
|
|
DirectiveKindMap["elseifdef"] = DK_ELSEIFDEF;
|
|
DirectiveKindMap["elseifndef"] = DK_ELSEIFNDEF;
|
|
DirectiveKindMap["elseifdif"] = DK_ELSEIFDIF;
|
|
DirectiveKindMap["elseifidn"] = DK_ELSEIFIDN;
|
|
DirectiveKindMap["else"] = DK_ELSE;
|
|
DirectiveKindMap["end"] = DK_END;
|
|
DirectiveKindMap["endif"] = DK_ENDIF;
|
|
// DirectiveKindMap[".file"] = DK_FILE;
|
|
// DirectiveKindMap[".line"] = DK_LINE;
|
|
// DirectiveKindMap[".loc"] = DK_LOC;
|
|
// 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[".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_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_b_key_frame"] = DK_CFI_B_KEY_FRAME;
|
|
DirectiveKindMap["macro"] = DK_MACRO;
|
|
DirectiveKindMap["exitm"] = DK_EXITM;
|
|
DirectiveKindMap["endm"] = DK_ENDM;
|
|
DirectiveKindMap["purge"] = DK_PURGE;
|
|
DirectiveKindMap[".err"] = DK_ERR;
|
|
DirectiveKindMap[".errb"] = DK_ERRB;
|
|
DirectiveKindMap[".errnb"] = DK_ERRNB;
|
|
DirectiveKindMap[".errdef"] = DK_ERRDEF;
|
|
DirectiveKindMap[".errndef"] = DK_ERRNDEF;
|
|
DirectiveKindMap[".errdif"] = DK_ERRDIF;
|
|
DirectiveKindMap[".errdifi"] = DK_ERRDIFI;
|
|
DirectiveKindMap[".erridn"] = DK_ERRIDN;
|
|
DirectiveKindMap[".erridni"] = DK_ERRIDNI;
|
|
DirectiveKindMap[".erre"] = DK_ERRE;
|
|
DirectiveKindMap[".errnz"] = DK_ERRNZ;
|
|
DirectiveKindMap[".pushframe"] = DK_PUSHFRAME;
|
|
DirectiveKindMap[".pushreg"] = DK_PUSHREG;
|
|
DirectiveKindMap[".savereg"] = DK_SAVEREG;
|
|
DirectiveKindMap[".savexmm128"] = DK_SAVEXMM128;
|
|
DirectiveKindMap[".setframe"] = DK_SETFRAME;
|
|
DirectiveKindMap[".radix"] = DK_RADIX;
|
|
DirectiveKindMap["db"] = DK_DB;
|
|
DirectiveKindMap["dd"] = DK_DD;
|
|
DirectiveKindMap["df"] = DK_DF;
|
|
DirectiveKindMap["dq"] = DK_DQ;
|
|
DirectiveKindMap["dw"] = DK_DW;
|
|
DirectiveKindMap["echo"] = DK_ECHO;
|
|
DirectiveKindMap["struc"] = DK_STRUCT;
|
|
DirectiveKindMap["struct"] = DK_STRUCT;
|
|
DirectiveKindMap["union"] = DK_UNION;
|
|
DirectiveKindMap["ends"] = DK_ENDS;
|
|
}
|
|
|
|
bool MasmParser::isMacroLikeDirective() {
|
|
if (getLexer().is(AsmToken::Identifier)) {
|
|
bool IsMacroLike = StringSwitch<bool>(getTok().getIdentifier())
|
|
.CasesLower("repeat", "rept", true)
|
|
.CaseLower("while", true)
|
|
.CasesLower("for", "irp", true)
|
|
.CasesLower("forc", "irpc", true)
|
|
.Default(false);
|
|
if (IsMacroLike)
|
|
return true;
|
|
}
|
|
if (peekTok().is(AsmToken::Identifier) &&
|
|
peekTok().getIdentifier().equals_insensitive("macro"))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
MCAsmMacro *MasmParser::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 'endm' in definition");
|
|
return nullptr;
|
|
}
|
|
|
|
if (isMacroLikeDirective())
|
|
++NestLevel;
|
|
|
|
// Otherwise, check whether we have reached the endm.
|
|
if (Lexer.is(AsmToken::Identifier) &&
|
|
getTok().getIdentifier().equals_insensitive("endm")) {
|
|
if (NestLevel == 0) {
|
|
EndToken = getTok();
|
|
Lex();
|
|
if (Lexer.isNot(AsmToken::EndOfStatement)) {
|
|
printError(getTok().getLoc(), "unexpected token in 'endm' directive");
|
|
return nullptr;
|
|
}
|
|
break;
|
|
}
|
|
--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();
|
|
}
|
|
|
|
bool MasmParser::expandStatement(SMLoc Loc) {
|
|
std::string Body = parseStringTo(AsmToken::EndOfStatement);
|
|
SMLoc EndLoc = getTok().getLoc();
|
|
|
|
MCAsmMacroParameters Parameters;
|
|
MCAsmMacroArguments Arguments;
|
|
for (const auto &V : Variables) {
|
|
const Variable &Var = V.getValue();
|
|
if (Var.IsText) {
|
|
Parameters.emplace_back();
|
|
Arguments.emplace_back();
|
|
MCAsmMacroParameter &P = Parameters.back();
|
|
MCAsmMacroArgument &A = Arguments.back();
|
|
P.Name = Var.Name;
|
|
P.Required = true;
|
|
A.push_back(AsmToken(AsmToken::String, Var.TextValue));
|
|
}
|
|
}
|
|
MacroLikeBodies.emplace_back(StringRef(), Body, Parameters);
|
|
MCAsmMacro M = MacroLikeBodies.back();
|
|
|
|
// Expand the statement in a new buffer.
|
|
SmallString<80> Buf;
|
|
raw_svector_ostream OS(Buf);
|
|
if (expandMacro(OS, M.Body, M.Parameters, Arguments, M.Locals, EndLoc))
|
|
return true;
|
|
std::unique_ptr<MemoryBuffer> Expansion =
|
|
MemoryBuffer::getMemBufferCopy(OS.str(), "<expansion>");
|
|
|
|
// Jump to the expanded statement and prime the lexer.
|
|
CurBuffer = SrcMgr.AddNewSourceBuffer(std::move(Expansion), EndLoc);
|
|
Lexer.setBuffer(SrcMgr.getMemoryBuffer(CurBuffer)->getBuffer());
|
|
EndStatementAtEOFStack.push_back(false);
|
|
Lex();
|
|
return false;
|
|
}
|
|
|
|
void MasmParser::instantiateMacroLikeBody(MCAsmMacro *M, SMLoc DirectiveLoc,
|
|
raw_svector_ostream &OS) {
|
|
instantiateMacroLikeBody(M, DirectiveLoc, /*ExitLoc=*/getTok().getLoc(), OS);
|
|
}
|
|
void MasmParser::instantiateMacroLikeBody(MCAsmMacro *M, SMLoc DirectiveLoc,
|
|
SMLoc ExitLoc,
|
|
raw_svector_ostream &OS) {
|
|
OS << "endm\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,
|
|
ExitLoc, 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());
|
|
EndStatementAtEOFStack.push_back(true);
|
|
Lex();
|
|
}
|
|
|
|
/// parseDirectiveRepeat
|
|
/// ::= ("repeat" | "rept") count
|
|
/// body
|
|
/// endm
|
|
bool MasmParser::parseDirectiveRepeat(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") ||
|
|
parseToken(AsmToken::EndOfStatement,
|
|
"unexpected token in '" + Dir + "' directive"))
|
|
return true;
|
|
|
|
// Lex the repeat 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--) {
|
|
if (expandMacro(OS, M->Body, None, None, M->Locals, getTok().getLoc()))
|
|
return true;
|
|
}
|
|
instantiateMacroLikeBody(M, DirectiveLoc, OS);
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveWhile
|
|
/// ::= "while" expression
|
|
/// body
|
|
/// endm
|
|
bool MasmParser::parseDirectiveWhile(SMLoc DirectiveLoc) {
|
|
const MCExpr *CondExpr;
|
|
SMLoc CondLoc = getTok().getLoc();
|
|
if (parseExpression(CondExpr))
|
|
return true;
|
|
|
|
// Lex the repeat 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);
|
|
int64_t Condition;
|
|
if (!CondExpr->evaluateAsAbsolute(Condition, getStreamer().getAssemblerPtr()))
|
|
return Error(CondLoc, "expected absolute expression in 'while' directive");
|
|
if (Condition) {
|
|
// Instantiate the macro, then resume at this directive to recheck the
|
|
// condition.
|
|
if (expandMacro(OS, M->Body, None, None, M->Locals, getTok().getLoc()))
|
|
return true;
|
|
instantiateMacroLikeBody(M, DirectiveLoc, /*ExitLoc=*/DirectiveLoc, OS);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveFor
|
|
/// ::= ("for" | "irp") symbol [":" qualifier], <values>
|
|
/// body
|
|
/// endm
|
|
bool MasmParser::parseDirectiveFor(SMLoc DirectiveLoc, StringRef Dir) {
|
|
MCAsmMacroParameter Parameter;
|
|
MCAsmMacroArguments A;
|
|
if (check(parseIdentifier(Parameter.Name),
|
|
"expected identifier in '" + Dir + "' directive"))
|
|
return true;
|
|
|
|
// Parse optional qualifier (default value, or "req")
|
|
if (parseOptionalToken(AsmToken::Colon)) {
|
|
if (parseOptionalToken(AsmToken::Equal)) {
|
|
// Default value
|
|
SMLoc ParamLoc;
|
|
|
|
ParamLoc = Lexer.getLoc();
|
|
if (parseMacroArgument(nullptr, Parameter.Value))
|
|
return true;
|
|
} else {
|
|
SMLoc QualLoc;
|
|
StringRef Qualifier;
|
|
|
|
QualLoc = Lexer.getLoc();
|
|
if (parseIdentifier(Qualifier))
|
|
return Error(QualLoc, "missing parameter qualifier for "
|
|
"'" +
|
|
Parameter.Name + "' in '" + Dir +
|
|
"' directive");
|
|
|
|
if (Qualifier.equals_insensitive("req"))
|
|
Parameter.Required = true;
|
|
else
|
|
return Error(QualLoc,
|
|
Qualifier + " is not a valid parameter qualifier for '" +
|
|
Parameter.Name + "' in '" + Dir + "' directive");
|
|
}
|
|
}
|
|
|
|
if (parseToken(AsmToken::Comma,
|
|
"expected comma in '" + Dir + "' directive") ||
|
|
parseToken(AsmToken::Less,
|
|
"values in '" + Dir +
|
|
"' directive must be enclosed in angle brackets"))
|
|
return true;
|
|
|
|
while (true) {
|
|
A.emplace_back();
|
|
if (parseMacroArgument(&Parameter, A.back(), /*EndTok=*/AsmToken::Greater))
|
|
return addErrorSuffix(" in arguments for '" + Dir + "' directive");
|
|
|
|
// If we see a comma, continue, and allow line continuation.
|
|
if (!parseOptionalToken(AsmToken::Comma))
|
|
break;
|
|
parseOptionalToken(AsmToken::EndOfStatement);
|
|
}
|
|
|
|
if (parseToken(AsmToken::Greater,
|
|
"values in '" + Dir +
|
|
"' directive must be enclosed in angle brackets") ||
|
|
parseToken(AsmToken::EndOfStatement, "expected End of Statement"))
|
|
return true;
|
|
|
|
// Lex the for 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) {
|
|
if (expandMacro(OS, M->Body, Parameter, Arg, M->Locals, getTok().getLoc()))
|
|
return true;
|
|
}
|
|
|
|
instantiateMacroLikeBody(M, DirectiveLoc, OS);
|
|
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveForc
|
|
/// ::= ("forc" | "irpc") symbol, <string>
|
|
/// body
|
|
/// endm
|
|
bool MasmParser::parseDirectiveForc(SMLoc DirectiveLoc, StringRef Directive) {
|
|
MCAsmMacroParameter Parameter;
|
|
|
|
std::string Argument;
|
|
if (check(parseIdentifier(Parameter.Name),
|
|
"expected identifier in '" + Directive + "' directive") ||
|
|
parseToken(AsmToken::Comma,
|
|
"expected comma in '" + Directive + "' directive"))
|
|
return true;
|
|
if (parseAngleBracketString(Argument)) {
|
|
// Match ml64.exe; treat all characters to end of statement as a string,
|
|
// ignoring comment markers, then discard anything following a space (using
|
|
// the C locale).
|
|
Argument = parseStringTo(AsmToken::EndOfStatement);
|
|
if (getTok().is(AsmToken::EndOfStatement))
|
|
Argument += getTok().getString();
|
|
size_t End = 0;
|
|
for (; End < Argument.size(); ++End) {
|
|
if (isSpace(Argument[End]))
|
|
break;
|
|
}
|
|
Argument.resize(End);
|
|
}
|
|
if (parseToken(AsmToken::EndOfStatement, "expected end of statement"))
|
|
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(Argument);
|
|
for (std::size_t I = 0, End = Values.size(); I != End; ++I) {
|
|
MCAsmMacroArgument Arg;
|
|
Arg.emplace_back(AsmToken::Identifier, Values.slice(I, I + 1));
|
|
|
|
if (expandMacro(OS, M->Body, Parameter, Arg, M->Locals, getTok().getLoc()))
|
|
return true;
|
|
}
|
|
|
|
instantiateMacroLikeBody(M, DirectiveLoc, OS);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::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 MasmParser::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 MasmParser::parseDirectiveRadix(SMLoc DirectiveLoc) {
|
|
const SMLoc Loc = getLexer().getLoc();
|
|
std::string RadixStringRaw = parseStringTo(AsmToken::EndOfStatement);
|
|
StringRef RadixString = StringRef(RadixStringRaw).trim();
|
|
unsigned Radix;
|
|
if (RadixString.getAsInteger(10, Radix)) {
|
|
return Error(Loc,
|
|
"radix must be a decimal number in the range 2 to 16; was " +
|
|
RadixString);
|
|
}
|
|
if (Radix < 2 || Radix > 16)
|
|
return Error(Loc, "radix must be in the range 2 to 16; was " +
|
|
std::to_string(Radix));
|
|
getLexer().setMasmDefaultRadix(Radix);
|
|
return false;
|
|
}
|
|
|
|
/// parseDirectiveEcho
|
|
/// ::= "echo" message
|
|
bool MasmParser::parseDirectiveEcho() {
|
|
// We're called before the directive is parsed, to avoid triggering lexical
|
|
// substitutions in the message. Assert that the next token is the directive,
|
|
// then eat it without using the Parser's Lex method.
|
|
assert(getTok().is(AsmToken::Identifier) &&
|
|
getTok().getString().equals_insensitive("echo"));
|
|
Lexer.Lex();
|
|
|
|
std::string Message = parseStringTo(AsmToken::EndOfStatement);
|
|
llvm::outs() << Message;
|
|
if (!StringRef(Message).endswith("\n"))
|
|
llvm::outs() << '\n';
|
|
return false;
|
|
}
|
|
|
|
// 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 MasmParser::defineMacro(StringRef Name, StringRef Value) {
|
|
Variable &Var = Variables[Name.lower()];
|
|
if (Var.Name.empty()) {
|
|
Var.Name = Name;
|
|
} else if (Var.Redefinable == Variable::NOT_REDEFINABLE) {
|
|
return Error(SMLoc(), "invalid variable redefinition");
|
|
} else if (Var.Redefinable == Variable::WARN_ON_REDEFINITION &&
|
|
Warning(SMLoc(), "redefining '" + Name +
|
|
"', already defined on the command line")) {
|
|
return true;
|
|
}
|
|
Var.Redefinable = Variable::WARN_ON_REDEFINITION;
|
|
Var.IsText = true;
|
|
Var.TextValue = Value.str();
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::lookUpField(StringRef Name, AsmFieldInfo &Info) const {
|
|
const std::pair<StringRef, StringRef> BaseMember = Name.split('.');
|
|
const StringRef Base = BaseMember.first, Member = BaseMember.second;
|
|
return lookUpField(Base, Member, Info);
|
|
}
|
|
|
|
bool MasmParser::lookUpField(StringRef Base, StringRef Member,
|
|
AsmFieldInfo &Info) const {
|
|
if (Base.empty())
|
|
return true;
|
|
|
|
AsmFieldInfo BaseInfo;
|
|
if (Base.contains('.') && !lookUpField(Base, BaseInfo))
|
|
Base = BaseInfo.Type.Name;
|
|
|
|
auto StructIt = Structs.find(Base.lower());
|
|
auto TypeIt = KnownType.find(Base.lower());
|
|
if (TypeIt != KnownType.end()) {
|
|
StructIt = Structs.find(TypeIt->second.Name.lower());
|
|
}
|
|
if (StructIt != Structs.end())
|
|
return lookUpField(StructIt->second, Member, Info);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool MasmParser::lookUpField(const StructInfo &Structure, StringRef Member,
|
|
AsmFieldInfo &Info) const {
|
|
if (Member.empty()) {
|
|
Info.Type.Name = Structure.Name;
|
|
Info.Type.Size = Structure.Size;
|
|
Info.Type.ElementSize = Structure.Size;
|
|
Info.Type.Length = 1;
|
|
return false;
|
|
}
|
|
|
|
std::pair<StringRef, StringRef> Split = Member.split('.');
|
|
const StringRef FieldName = Split.first, FieldMember = Split.second;
|
|
|
|
auto StructIt = Structs.find(FieldName.lower());
|
|
if (StructIt != Structs.end())
|
|
return lookUpField(StructIt->second, FieldMember, Info);
|
|
|
|
auto FieldIt = Structure.FieldsByName.find(FieldName.lower());
|
|
if (FieldIt == Structure.FieldsByName.end())
|
|
return true;
|
|
|
|
const FieldInfo &Field = Structure.Fields[FieldIt->second];
|
|
if (FieldMember.empty()) {
|
|
Info.Offset += Field.Offset;
|
|
Info.Type.Size = Field.SizeOf;
|
|
Info.Type.ElementSize = Field.Type;
|
|
Info.Type.Length = Field.LengthOf;
|
|
if (Field.Contents.FT == FT_STRUCT)
|
|
Info.Type.Name = Field.Contents.StructInfo.Structure.Name;
|
|
else
|
|
Info.Type.Name = "";
|
|
return false;
|
|
}
|
|
|
|
if (Field.Contents.FT != FT_STRUCT)
|
|
return true;
|
|
const StructFieldInfo &StructInfo = Field.Contents.StructInfo;
|
|
|
|
if (lookUpField(StructInfo.Structure, FieldMember, Info))
|
|
return true;
|
|
|
|
Info.Offset += Field.Offset;
|
|
return false;
|
|
}
|
|
|
|
bool MasmParser::lookUpType(StringRef Name, AsmTypeInfo &Info) const {
|
|
unsigned Size = StringSwitch<unsigned>(Name)
|
|
.CasesLower("byte", "db", "sbyte", 1)
|
|
.CasesLower("word", "dw", "sword", 2)
|
|
.CasesLower("dword", "dd", "sdword", 4)
|
|
.CasesLower("fword", "df", 6)
|
|
.CasesLower("qword", "dq", "sqword", 8)
|
|
.CaseLower("real4", 4)
|
|
.CaseLower("real8", 8)
|
|
.CaseLower("real10", 10)
|
|
.Default(0);
|
|
if (Size) {
|
|
Info.Name = Name;
|
|
Info.ElementSize = Size;
|
|
Info.Length = 1;
|
|
Info.Size = Size;
|
|
return false;
|
|
}
|
|
|
|
auto StructIt = Structs.find(Name.lower());
|
|
if (StructIt != Structs.end()) {
|
|
const StructInfo &Structure = StructIt->second;
|
|
Info.Name = Name;
|
|
Info.ElementSize = Structure.Size;
|
|
Info.Length = 1;
|
|
Info.Size = Structure.Size;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool MasmParser::parseMSInlineAsm(
|
|
void *AsmLoc, std::string &AsmString, unsigned &NumOutputs,
|
|
unsigned &NumInputs, SmallVectorImpl<std::pair<void *, bool>> &OpDecls,
|
|
SmallVectorImpl<std::string> &Constraints,
|
|
SmallVectorImpl<std::string> &Clobbers, const MCInstrInfo *MII,
|
|
const 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<unsigned, 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();
|
|
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());
|
|
} else {
|
|
InputDecls.push_back(OpDecl);
|
|
InputDeclsAddressOf.push_back(Operand.needAddressOf());
|
|
InputConstraints.push_back(Constraint.str());
|
|
if (Desc.OpInfo[i - 1].isBranchTarget())
|
|
AsmStrRewrites.emplace_back(AOK_CallInput, Start, SymName.size());
|
|
else
|
|
AsmStrRewrites.emplace_back(AOK_Input, Start, SymName.size());
|
|
}
|
|
}
|
|
|
|
// Consider implicit defs to be clobbers. Think of cpuid and push.
|
|
ArrayRef<MCPhysReg> ImpDefs(Desc.getImplicitDefs(),
|
|
Desc.getNumImplicitDefs());
|
|
llvm::append_range(ClobberRegs, ImpDefs);
|
|
}
|
|
|
|
// 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(std::unique(ClobberRegs.begin(), ClobberRegs.end()),
|
|
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 it = AsmStrRewrites.begin(); it != AsmStrRewrites.end(); ++it) {
|
|
const AsmRewrite &AR = *it;
|
|
// 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(
|
|
it, 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:
|
|
OS << '$' << InputIdx++;
|
|
break;
|
|
case AOK_CallInput:
|
|
OS << "${" << InputIdx++ << ":P}";
|
|
break;
|
|
case AOK_Output:
|
|
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 = OS.str();
|
|
return false;
|
|
}
|
|
|
|
/// Create an MCAsmParser instance.
|
|
MCAsmParser *llvm::createMCMasmParser(SourceMgr &SM, MCContext &C,
|
|
MCStreamer &Out, const MCAsmInfo &MAI,
|
|
unsigned CB) {
|
|
return new MasmParser(SM, C, Out, MAI, CB);
|
|
}
|