mirror of
https://github.com/llvm/llvm-project.git
synced 2025-05-01 11:06:06 +00:00

This moves the registry higher in the LLVM library dependency stack. Every client of the target registry needs to link against MC anyway to actually use the target, so we might as well move this out of Support. This allows us to ensure that Support doesn't have includes from MC/*. Differential Revision: https://reviews.llvm.org/D111454
964 lines
33 KiB
C++
964 lines
33 KiB
C++
//===---- ParseStmtAsm.cpp - Assembly Statement Parser --------------------===//
|
|
//
|
|
// 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 file implements parsing for GCC and Microsoft inline assembly.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/Basic/Diagnostic.h"
|
|
#include "clang/Basic/TargetInfo.h"
|
|
#include "clang/Parse/Parser.h"
|
|
#include "clang/Parse/RAIIObjectsForParser.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/MC/MCAsmInfo.h"
|
|
#include "llvm/MC/MCContext.h"
|
|
#include "llvm/MC/MCInstPrinter.h"
|
|
#include "llvm/MC/MCInstrInfo.h"
|
|
#include "llvm/MC/MCObjectFileInfo.h"
|
|
#include "llvm/MC/MCParser/MCAsmParser.h"
|
|
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
|
|
#include "llvm/MC/MCRegisterInfo.h"
|
|
#include "llvm/MC/MCStreamer.h"
|
|
#include "llvm/MC/MCSubtargetInfo.h"
|
|
#include "llvm/MC/MCTargetOptions.h"
|
|
#include "llvm/MC/TargetRegistry.h"
|
|
#include "llvm/Support/SourceMgr.h"
|
|
#include "llvm/Support/TargetSelect.h"
|
|
using namespace clang;
|
|
|
|
namespace {
|
|
class ClangAsmParserCallback : public llvm::MCAsmParserSemaCallback {
|
|
Parser &TheParser;
|
|
SourceLocation AsmLoc;
|
|
StringRef AsmString;
|
|
|
|
/// The tokens we streamed into AsmString and handed off to MC.
|
|
ArrayRef<Token> AsmToks;
|
|
|
|
/// The offset of each token in AsmToks within AsmString.
|
|
ArrayRef<unsigned> AsmTokOffsets;
|
|
|
|
public:
|
|
ClangAsmParserCallback(Parser &P, SourceLocation Loc, StringRef AsmString,
|
|
ArrayRef<Token> Toks, ArrayRef<unsigned> Offsets)
|
|
: TheParser(P), AsmLoc(Loc), AsmString(AsmString), AsmToks(Toks),
|
|
AsmTokOffsets(Offsets) {
|
|
assert(AsmToks.size() == AsmTokOffsets.size());
|
|
}
|
|
|
|
void LookupInlineAsmIdentifier(StringRef &LineBuf,
|
|
llvm::InlineAsmIdentifierInfo &Info,
|
|
bool IsUnevaluatedContext) override;
|
|
|
|
StringRef LookupInlineAsmLabel(StringRef Identifier, llvm::SourceMgr &LSM,
|
|
llvm::SMLoc Location,
|
|
bool Create) override;
|
|
|
|
bool LookupInlineAsmField(StringRef Base, StringRef Member,
|
|
unsigned &Offset) override {
|
|
return TheParser.getActions().LookupInlineAsmField(Base, Member, Offset,
|
|
AsmLoc);
|
|
}
|
|
|
|
static void DiagHandlerCallback(const llvm::SMDiagnostic &D, void *Context) {
|
|
((ClangAsmParserCallback *)Context)->handleDiagnostic(D);
|
|
}
|
|
|
|
private:
|
|
/// Collect the appropriate tokens for the given string.
|
|
void findTokensForString(StringRef Str, SmallVectorImpl<Token> &TempToks,
|
|
const Token *&FirstOrigToken) const;
|
|
|
|
SourceLocation translateLocation(const llvm::SourceMgr &LSM,
|
|
llvm::SMLoc SMLoc);
|
|
|
|
void handleDiagnostic(const llvm::SMDiagnostic &D);
|
|
};
|
|
}
|
|
|
|
void ClangAsmParserCallback::LookupInlineAsmIdentifier(
|
|
StringRef &LineBuf, llvm::InlineAsmIdentifierInfo &Info,
|
|
bool IsUnevaluatedContext) {
|
|
// Collect the desired tokens.
|
|
SmallVector<Token, 16> LineToks;
|
|
const Token *FirstOrigToken = nullptr;
|
|
findTokensForString(LineBuf, LineToks, FirstOrigToken);
|
|
|
|
unsigned NumConsumedToks;
|
|
ExprResult Result = TheParser.ParseMSAsmIdentifier(LineToks, NumConsumedToks,
|
|
IsUnevaluatedContext);
|
|
|
|
// If we consumed the entire line, tell MC that.
|
|
// Also do this if we consumed nothing as a way of reporting failure.
|
|
if (NumConsumedToks == 0 || NumConsumedToks == LineToks.size()) {
|
|
// By not modifying LineBuf, we're implicitly consuming it all.
|
|
|
|
// Otherwise, consume up to the original tokens.
|
|
} else {
|
|
assert(FirstOrigToken && "not using original tokens?");
|
|
|
|
// Since we're using original tokens, apply that offset.
|
|
assert(FirstOrigToken[NumConsumedToks].getLocation() ==
|
|
LineToks[NumConsumedToks].getLocation());
|
|
unsigned FirstIndex = FirstOrigToken - AsmToks.begin();
|
|
unsigned LastIndex = FirstIndex + NumConsumedToks - 1;
|
|
|
|
// The total length we've consumed is the relative offset
|
|
// of the last token we consumed plus its length.
|
|
unsigned TotalOffset =
|
|
(AsmTokOffsets[LastIndex] + AsmToks[LastIndex].getLength() -
|
|
AsmTokOffsets[FirstIndex]);
|
|
LineBuf = LineBuf.substr(0, TotalOffset);
|
|
}
|
|
|
|
// Initialize Info with the lookup result.
|
|
if (!Result.isUsable())
|
|
return;
|
|
TheParser.getActions().FillInlineAsmIdentifierInfo(Result.get(), Info);
|
|
}
|
|
|
|
StringRef ClangAsmParserCallback::LookupInlineAsmLabel(StringRef Identifier,
|
|
llvm::SourceMgr &LSM,
|
|
llvm::SMLoc Location,
|
|
bool Create) {
|
|
SourceLocation Loc = translateLocation(LSM, Location);
|
|
LabelDecl *Label =
|
|
TheParser.getActions().GetOrCreateMSAsmLabel(Identifier, Loc, Create);
|
|
return Label->getMSAsmLabel();
|
|
}
|
|
|
|
void ClangAsmParserCallback::findTokensForString(
|
|
StringRef Str, SmallVectorImpl<Token> &TempToks,
|
|
const Token *&FirstOrigToken) const {
|
|
// For now, assert that the string we're working with is a substring
|
|
// of what we gave to MC. This lets us use the original tokens.
|
|
assert(!std::less<const char *>()(Str.begin(), AsmString.begin()) &&
|
|
!std::less<const char *>()(AsmString.end(), Str.end()));
|
|
|
|
// Try to find a token whose offset matches the first token.
|
|
unsigned FirstCharOffset = Str.begin() - AsmString.begin();
|
|
const unsigned *FirstTokOffset =
|
|
llvm::lower_bound(AsmTokOffsets, FirstCharOffset);
|
|
|
|
// For now, assert that the start of the string exactly
|
|
// corresponds to the start of a token.
|
|
assert(*FirstTokOffset == FirstCharOffset);
|
|
|
|
// Use all the original tokens for this line. (We assume the
|
|
// end of the line corresponds cleanly to a token break.)
|
|
unsigned FirstTokIndex = FirstTokOffset - AsmTokOffsets.begin();
|
|
FirstOrigToken = &AsmToks[FirstTokIndex];
|
|
unsigned LastCharOffset = Str.end() - AsmString.begin();
|
|
for (unsigned i = FirstTokIndex, e = AsmTokOffsets.size(); i != e; ++i) {
|
|
if (AsmTokOffsets[i] >= LastCharOffset)
|
|
break;
|
|
TempToks.push_back(AsmToks[i]);
|
|
}
|
|
}
|
|
|
|
SourceLocation
|
|
ClangAsmParserCallback::translateLocation(const llvm::SourceMgr &LSM,
|
|
llvm::SMLoc SMLoc) {
|
|
// Compute an offset into the inline asm buffer.
|
|
// FIXME: This isn't right if .macro is involved (but hopefully, no
|
|
// real-world code does that).
|
|
const llvm::MemoryBuffer *LBuf =
|
|
LSM.getMemoryBuffer(LSM.FindBufferContainingLoc(SMLoc));
|
|
unsigned Offset = SMLoc.getPointer() - LBuf->getBufferStart();
|
|
|
|
// Figure out which token that offset points into.
|
|
const unsigned *TokOffsetPtr = llvm::lower_bound(AsmTokOffsets, Offset);
|
|
unsigned TokIndex = TokOffsetPtr - AsmTokOffsets.begin();
|
|
unsigned TokOffset = *TokOffsetPtr;
|
|
|
|
// If we come up with an answer which seems sane, use it; otherwise,
|
|
// just point at the __asm keyword.
|
|
// FIXME: Assert the answer is sane once we handle .macro correctly.
|
|
SourceLocation Loc = AsmLoc;
|
|
if (TokIndex < AsmToks.size()) {
|
|
const Token &Tok = AsmToks[TokIndex];
|
|
Loc = Tok.getLocation();
|
|
Loc = Loc.getLocWithOffset(Offset - TokOffset);
|
|
}
|
|
return Loc;
|
|
}
|
|
|
|
void ClangAsmParserCallback::handleDiagnostic(const llvm::SMDiagnostic &D) {
|
|
const llvm::SourceMgr &LSM = *D.getSourceMgr();
|
|
SourceLocation Loc = translateLocation(LSM, D.getLoc());
|
|
TheParser.Diag(Loc, diag::err_inline_ms_asm_parsing) << D.getMessage();
|
|
}
|
|
|
|
/// Parse an identifier in an MS-style inline assembly block.
|
|
ExprResult Parser::ParseMSAsmIdentifier(llvm::SmallVectorImpl<Token> &LineToks,
|
|
unsigned &NumLineToksConsumed,
|
|
bool IsUnevaluatedContext) {
|
|
// Push a fake token on the end so that we don't overrun the token
|
|
// stream. We use ';' because it expression-parsing should never
|
|
// overrun it.
|
|
const tok::TokenKind EndOfStream = tok::semi;
|
|
Token EndOfStreamTok;
|
|
EndOfStreamTok.startToken();
|
|
EndOfStreamTok.setKind(EndOfStream);
|
|
LineToks.push_back(EndOfStreamTok);
|
|
|
|
// Also copy the current token over.
|
|
LineToks.push_back(Tok);
|
|
|
|
PP.EnterTokenStream(LineToks, /*DisableMacroExpansions*/ true,
|
|
/*IsReinject*/ true);
|
|
|
|
// Clear the current token and advance to the first token in LineToks.
|
|
ConsumeAnyToken();
|
|
|
|
// Parse an optional scope-specifier if we're in C++.
|
|
CXXScopeSpec SS;
|
|
if (getLangOpts().CPlusPlus)
|
|
ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
|
|
/*ObjectHadErrors=*/false,
|
|
/*EnteringContext=*/false);
|
|
|
|
// Require an identifier here.
|
|
SourceLocation TemplateKWLoc;
|
|
UnqualifiedId Id;
|
|
bool Invalid = true;
|
|
ExprResult Result;
|
|
if (Tok.is(tok::kw_this)) {
|
|
Result = ParseCXXThis();
|
|
Invalid = false;
|
|
} else {
|
|
Invalid =
|
|
ParseUnqualifiedId(SS, /*ObjectType=*/nullptr,
|
|
/*ObjectHadErrors=*/false,
|
|
/*EnteringContext=*/false,
|
|
/*AllowDestructorName=*/false,
|
|
/*AllowConstructorName=*/false,
|
|
/*AllowDeductionGuide=*/false, &TemplateKWLoc, Id);
|
|
// Perform the lookup.
|
|
Result = Actions.LookupInlineAsmIdentifier(SS, TemplateKWLoc, Id,
|
|
IsUnevaluatedContext);
|
|
}
|
|
// While the next two tokens are 'period' 'identifier', repeatedly parse it as
|
|
// a field access. We have to avoid consuming assembler directives that look
|
|
// like '.' 'else'.
|
|
while (Result.isUsable() && Tok.is(tok::period)) {
|
|
Token IdTok = PP.LookAhead(0);
|
|
if (IdTok.isNot(tok::identifier))
|
|
break;
|
|
ConsumeToken(); // Consume the period.
|
|
IdentifierInfo *Id = Tok.getIdentifierInfo();
|
|
ConsumeToken(); // Consume the identifier.
|
|
Result = Actions.LookupInlineAsmVarDeclField(Result.get(), Id->getName(),
|
|
Tok.getLocation());
|
|
}
|
|
|
|
// Figure out how many tokens we are into LineToks.
|
|
unsigned LineIndex = 0;
|
|
if (Tok.is(EndOfStream)) {
|
|
LineIndex = LineToks.size() - 2;
|
|
} else {
|
|
while (LineToks[LineIndex].getLocation() != Tok.getLocation()) {
|
|
LineIndex++;
|
|
assert(LineIndex < LineToks.size() - 2); // we added two extra tokens
|
|
}
|
|
}
|
|
|
|
// If we've run into the poison token we inserted before, or there
|
|
// was a parsing error, then claim the entire line.
|
|
if (Invalid || Tok.is(EndOfStream)) {
|
|
NumLineToksConsumed = LineToks.size() - 2;
|
|
} else {
|
|
// Otherwise, claim up to the start of the next token.
|
|
NumLineToksConsumed = LineIndex;
|
|
}
|
|
|
|
// Finally, restore the old parsing state by consuming all the tokens we
|
|
// staged before, implicitly killing off the token-lexer we pushed.
|
|
for (unsigned i = 0, e = LineToks.size() - LineIndex - 2; i != e; ++i) {
|
|
ConsumeAnyToken();
|
|
}
|
|
assert(Tok.is(EndOfStream));
|
|
ConsumeToken();
|
|
|
|
// Leave LineToks in its original state.
|
|
LineToks.pop_back();
|
|
LineToks.pop_back();
|
|
|
|
return Result;
|
|
}
|
|
|
|
/// Turn a sequence of our tokens back into a string that we can hand
|
|
/// to the MC asm parser.
|
|
static bool buildMSAsmString(Preprocessor &PP, SourceLocation AsmLoc,
|
|
ArrayRef<Token> AsmToks,
|
|
SmallVectorImpl<unsigned> &TokOffsets,
|
|
SmallString<512> &Asm) {
|
|
assert(!AsmToks.empty() && "Didn't expect an empty AsmToks!");
|
|
|
|
// Is this the start of a new assembly statement?
|
|
bool isNewStatement = true;
|
|
|
|
for (unsigned i = 0, e = AsmToks.size(); i < e; ++i) {
|
|
const Token &Tok = AsmToks[i];
|
|
|
|
// Start each new statement with a newline and a tab.
|
|
if (!isNewStatement && (Tok.is(tok::kw_asm) || Tok.isAtStartOfLine())) {
|
|
Asm += "\n\t";
|
|
isNewStatement = true;
|
|
}
|
|
|
|
// Preserve the existence of leading whitespace except at the
|
|
// start of a statement.
|
|
if (!isNewStatement && Tok.hasLeadingSpace())
|
|
Asm += ' ';
|
|
|
|
// Remember the offset of this token.
|
|
TokOffsets.push_back(Asm.size());
|
|
|
|
// Don't actually write '__asm' into the assembly stream.
|
|
if (Tok.is(tok::kw_asm)) {
|
|
// Complain about __asm at the end of the stream.
|
|
if (i + 1 == e) {
|
|
PP.Diag(AsmLoc, diag::err_asm_empty);
|
|
return true;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// Append the spelling of the token.
|
|
SmallString<32> SpellingBuffer;
|
|
bool SpellingInvalid = false;
|
|
Asm += PP.getSpelling(Tok, SpellingBuffer, &SpellingInvalid);
|
|
assert(!SpellingInvalid && "spelling was invalid after correct parse?");
|
|
|
|
// We are no longer at the start of a statement.
|
|
isNewStatement = false;
|
|
}
|
|
|
|
// Ensure that the buffer is null-terminated.
|
|
Asm.push_back('\0');
|
|
Asm.pop_back();
|
|
|
|
assert(TokOffsets.size() == AsmToks.size());
|
|
return false;
|
|
}
|
|
|
|
// Determine if this is a GCC-style asm statement.
|
|
bool Parser::isGCCAsmStatement(const Token &TokAfterAsm) const {
|
|
return TokAfterAsm.is(tok::l_paren) || isGNUAsmQualifier(TokAfterAsm);
|
|
}
|
|
|
|
bool Parser::isGNUAsmQualifier(const Token &TokAfterAsm) const {
|
|
return getGNUAsmQualifier(TokAfterAsm) != GNUAsmQualifiers::AQ_unspecified;
|
|
}
|
|
|
|
/// ParseMicrosoftAsmStatement. When -fms-extensions/-fasm-blocks is enabled,
|
|
/// this routine is called to collect the tokens for an MS asm statement.
|
|
///
|
|
/// [MS] ms-asm-statement:
|
|
/// ms-asm-block
|
|
/// ms-asm-block ms-asm-statement
|
|
///
|
|
/// [MS] ms-asm-block:
|
|
/// '__asm' ms-asm-line '\n'
|
|
/// '__asm' '{' ms-asm-instruction-block[opt] '}' ';'[opt]
|
|
///
|
|
/// [MS] ms-asm-instruction-block
|
|
/// ms-asm-line
|
|
/// ms-asm-line '\n' ms-asm-instruction-block
|
|
///
|
|
StmtResult Parser::ParseMicrosoftAsmStatement(SourceLocation AsmLoc) {
|
|
SourceManager &SrcMgr = PP.getSourceManager();
|
|
SourceLocation EndLoc = AsmLoc;
|
|
SmallVector<Token, 4> AsmToks;
|
|
|
|
bool SingleLineMode = true;
|
|
unsigned BraceNesting = 0;
|
|
unsigned short savedBraceCount = BraceCount;
|
|
bool InAsmComment = false;
|
|
FileID FID;
|
|
unsigned LineNo = 0;
|
|
unsigned NumTokensRead = 0;
|
|
SmallVector<SourceLocation, 4> LBraceLocs;
|
|
bool SkippedStartOfLine = false;
|
|
|
|
if (Tok.is(tok::l_brace)) {
|
|
// Braced inline asm: consume the opening brace.
|
|
SingleLineMode = false;
|
|
BraceNesting = 1;
|
|
EndLoc = ConsumeBrace();
|
|
LBraceLocs.push_back(EndLoc);
|
|
++NumTokensRead;
|
|
} else {
|
|
// Single-line inline asm; compute which line it is on.
|
|
std::pair<FileID, unsigned> ExpAsmLoc =
|
|
SrcMgr.getDecomposedExpansionLoc(EndLoc);
|
|
FID = ExpAsmLoc.first;
|
|
LineNo = SrcMgr.getLineNumber(FID, ExpAsmLoc.second);
|
|
LBraceLocs.push_back(SourceLocation());
|
|
}
|
|
|
|
SourceLocation TokLoc = Tok.getLocation();
|
|
do {
|
|
// If we hit EOF, we're done, period.
|
|
if (isEofOrEom())
|
|
break;
|
|
|
|
if (!InAsmComment && Tok.is(tok::l_brace)) {
|
|
// Consume the opening brace.
|
|
SkippedStartOfLine = Tok.isAtStartOfLine();
|
|
AsmToks.push_back(Tok);
|
|
EndLoc = ConsumeBrace();
|
|
BraceNesting++;
|
|
LBraceLocs.push_back(EndLoc);
|
|
TokLoc = Tok.getLocation();
|
|
++NumTokensRead;
|
|
continue;
|
|
} else if (!InAsmComment && Tok.is(tok::semi)) {
|
|
// A semicolon in an asm is the start of a comment.
|
|
InAsmComment = true;
|
|
if (!SingleLineMode) {
|
|
// Compute which line the comment is on.
|
|
std::pair<FileID, unsigned> ExpSemiLoc =
|
|
SrcMgr.getDecomposedExpansionLoc(TokLoc);
|
|
FID = ExpSemiLoc.first;
|
|
LineNo = SrcMgr.getLineNumber(FID, ExpSemiLoc.second);
|
|
}
|
|
} else if (SingleLineMode || InAsmComment) {
|
|
// If end-of-line is significant, check whether this token is on a
|
|
// new line.
|
|
std::pair<FileID, unsigned> ExpLoc =
|
|
SrcMgr.getDecomposedExpansionLoc(TokLoc);
|
|
if (ExpLoc.first != FID ||
|
|
SrcMgr.getLineNumber(ExpLoc.first, ExpLoc.second) != LineNo) {
|
|
// If this is a single-line __asm, we're done, except if the next
|
|
// line is MS-style asm too, in which case we finish a comment
|
|
// if needed and then keep processing the next line as a single
|
|
// line __asm.
|
|
bool isAsm = Tok.is(tok::kw_asm);
|
|
if (SingleLineMode && (!isAsm || isGCCAsmStatement(NextToken())))
|
|
break;
|
|
// We're no longer in a comment.
|
|
InAsmComment = false;
|
|
if (isAsm) {
|
|
// If this is a new __asm {} block we want to process it separately
|
|
// from the single-line __asm statements
|
|
if (PP.LookAhead(0).is(tok::l_brace))
|
|
break;
|
|
LineNo = SrcMgr.getLineNumber(ExpLoc.first, ExpLoc.second);
|
|
SkippedStartOfLine = Tok.isAtStartOfLine();
|
|
} else if (Tok.is(tok::semi)) {
|
|
// A multi-line asm-statement, where next line is a comment
|
|
InAsmComment = true;
|
|
FID = ExpLoc.first;
|
|
LineNo = SrcMgr.getLineNumber(FID, ExpLoc.second);
|
|
}
|
|
} else if (!InAsmComment && Tok.is(tok::r_brace)) {
|
|
// In MSVC mode, braces only participate in brace matching and
|
|
// separating the asm statements. This is an intentional
|
|
// departure from the Apple gcc behavior.
|
|
if (!BraceNesting)
|
|
break;
|
|
}
|
|
}
|
|
if (!InAsmComment && BraceNesting && Tok.is(tok::r_brace) &&
|
|
BraceCount == (savedBraceCount + BraceNesting)) {
|
|
// Consume the closing brace.
|
|
SkippedStartOfLine = Tok.isAtStartOfLine();
|
|
// Don't want to add the closing brace of the whole asm block
|
|
if (SingleLineMode || BraceNesting > 1) {
|
|
Tok.clearFlag(Token::LeadingSpace);
|
|
AsmToks.push_back(Tok);
|
|
}
|
|
EndLoc = ConsumeBrace();
|
|
BraceNesting--;
|
|
// Finish if all of the opened braces in the inline asm section were
|
|
// consumed.
|
|
if (BraceNesting == 0 && !SingleLineMode)
|
|
break;
|
|
else {
|
|
LBraceLocs.pop_back();
|
|
TokLoc = Tok.getLocation();
|
|
++NumTokensRead;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Consume the next token; make sure we don't modify the brace count etc.
|
|
// if we are in a comment.
|
|
EndLoc = TokLoc;
|
|
if (InAsmComment)
|
|
PP.Lex(Tok);
|
|
else {
|
|
// Set the token as the start of line if we skipped the original start
|
|
// of line token in case it was a nested brace.
|
|
if (SkippedStartOfLine)
|
|
Tok.setFlag(Token::StartOfLine);
|
|
AsmToks.push_back(Tok);
|
|
ConsumeAnyToken();
|
|
}
|
|
TokLoc = Tok.getLocation();
|
|
++NumTokensRead;
|
|
SkippedStartOfLine = false;
|
|
} while (1);
|
|
|
|
if (BraceNesting && BraceCount != savedBraceCount) {
|
|
// __asm without closing brace (this can happen at EOF).
|
|
for (unsigned i = 0; i < BraceNesting; ++i) {
|
|
Diag(Tok, diag::err_expected) << tok::r_brace;
|
|
Diag(LBraceLocs.back(), diag::note_matching) << tok::l_brace;
|
|
LBraceLocs.pop_back();
|
|
}
|
|
return StmtError();
|
|
} else if (NumTokensRead == 0) {
|
|
// Empty __asm.
|
|
Diag(Tok, diag::err_expected) << tok::l_brace;
|
|
return StmtError();
|
|
}
|
|
|
|
// Okay, prepare to use MC to parse the assembly.
|
|
SmallVector<StringRef, 4> ConstraintRefs;
|
|
SmallVector<Expr *, 4> Exprs;
|
|
SmallVector<StringRef, 4> ClobberRefs;
|
|
|
|
// We need an actual supported target.
|
|
const llvm::Triple &TheTriple = Actions.Context.getTargetInfo().getTriple();
|
|
const std::string &TT = TheTriple.getTriple();
|
|
const llvm::Target *TheTarget = nullptr;
|
|
if (!TheTriple.isX86()) {
|
|
Diag(AsmLoc, diag::err_msasm_unsupported_arch) << TheTriple.getArchName();
|
|
} else {
|
|
std::string Error;
|
|
TheTarget = llvm::TargetRegistry::lookupTarget(TT, Error);
|
|
if (!TheTarget)
|
|
Diag(AsmLoc, diag::err_msasm_unable_to_create_target) << Error;
|
|
}
|
|
|
|
assert(!LBraceLocs.empty() && "Should have at least one location here");
|
|
|
|
SmallString<512> AsmString;
|
|
auto EmptyStmt = [&] {
|
|
return Actions.ActOnMSAsmStmt(AsmLoc, LBraceLocs[0], AsmToks, AsmString,
|
|
/*NumOutputs*/ 0, /*NumInputs*/ 0,
|
|
ConstraintRefs, ClobberRefs, Exprs, EndLoc);
|
|
};
|
|
// If we don't support assembly, or the assembly is empty, we don't
|
|
// need to instantiate the AsmParser, etc.
|
|
if (!TheTarget || AsmToks.empty()) {
|
|
return EmptyStmt();
|
|
}
|
|
|
|
// Expand the tokens into a string buffer.
|
|
SmallVector<unsigned, 8> TokOffsets;
|
|
if (buildMSAsmString(PP, AsmLoc, AsmToks, TokOffsets, AsmString))
|
|
return StmtError();
|
|
|
|
const TargetOptions &TO = Actions.Context.getTargetInfo().getTargetOpts();
|
|
std::string FeaturesStr =
|
|
llvm::join(TO.Features.begin(), TO.Features.end(), ",");
|
|
|
|
std::unique_ptr<llvm::MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TT));
|
|
if (!MRI) {
|
|
Diag(AsmLoc, diag::err_msasm_unable_to_create_target)
|
|
<< "target MC unavailable";
|
|
return EmptyStmt();
|
|
}
|
|
// FIXME: init MCOptions from sanitizer flags here.
|
|
llvm::MCTargetOptions MCOptions;
|
|
std::unique_ptr<llvm::MCAsmInfo> MAI(
|
|
TheTarget->createMCAsmInfo(*MRI, TT, MCOptions));
|
|
// Get the instruction descriptor.
|
|
std::unique_ptr<llvm::MCInstrInfo> MII(TheTarget->createMCInstrInfo());
|
|
std::unique_ptr<llvm::MCSubtargetInfo> STI(
|
|
TheTarget->createMCSubtargetInfo(TT, TO.CPU, FeaturesStr));
|
|
// Target MCTargetDesc may not be linked in clang-based tools.
|
|
|
|
if (!MAI || !MII || !STI) {
|
|
Diag(AsmLoc, diag::err_msasm_unable_to_create_target)
|
|
<< "target MC unavailable";
|
|
return EmptyStmt();
|
|
}
|
|
|
|
llvm::SourceMgr TempSrcMgr;
|
|
llvm::MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &TempSrcMgr);
|
|
std::unique_ptr<llvm::MCObjectFileInfo> MOFI(
|
|
TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false));
|
|
Ctx.setObjectFileInfo(MOFI.get());
|
|
|
|
std::unique_ptr<llvm::MemoryBuffer> Buffer =
|
|
llvm::MemoryBuffer::getMemBuffer(AsmString, "<MS inline asm>");
|
|
|
|
// Tell SrcMgr about this buffer, which is what the parser will pick up.
|
|
TempSrcMgr.AddNewSourceBuffer(std::move(Buffer), llvm::SMLoc());
|
|
|
|
std::unique_ptr<llvm::MCStreamer> Str(createNullStreamer(Ctx));
|
|
std::unique_ptr<llvm::MCAsmParser> Parser(
|
|
createMCAsmParser(TempSrcMgr, Ctx, *Str.get(), *MAI));
|
|
|
|
std::unique_ptr<llvm::MCTargetAsmParser> TargetParser(
|
|
TheTarget->createMCAsmParser(*STI, *Parser, *MII, MCOptions));
|
|
// Target AsmParser may not be linked in clang-based tools.
|
|
if (!TargetParser) {
|
|
Diag(AsmLoc, diag::err_msasm_unable_to_create_target)
|
|
<< "target ASM parser unavailable";
|
|
return EmptyStmt();
|
|
}
|
|
|
|
std::unique_ptr<llvm::MCInstPrinter> IP(
|
|
TheTarget->createMCInstPrinter(llvm::Triple(TT), 1, *MAI, *MII, *MRI));
|
|
|
|
// Change to the Intel dialect.
|
|
Parser->setAssemblerDialect(1);
|
|
Parser->setTargetParser(*TargetParser.get());
|
|
Parser->setParsingMSInlineAsm(true);
|
|
TargetParser->setParsingMSInlineAsm(true);
|
|
|
|
ClangAsmParserCallback Callback(*this, AsmLoc, AsmString, AsmToks,
|
|
TokOffsets);
|
|
TargetParser->setSemaCallback(&Callback);
|
|
TempSrcMgr.setDiagHandler(ClangAsmParserCallback::DiagHandlerCallback,
|
|
&Callback);
|
|
|
|
unsigned NumOutputs;
|
|
unsigned NumInputs;
|
|
std::string AsmStringIR;
|
|
SmallVector<std::pair<void *, bool>, 4> OpExprs;
|
|
SmallVector<std::string, 4> Constraints;
|
|
SmallVector<std::string, 4> Clobbers;
|
|
if (Parser->parseMSInlineAsm(AsmStringIR, NumOutputs, NumInputs, OpExprs,
|
|
Constraints, Clobbers, MII.get(), IP.get(),
|
|
Callback))
|
|
return StmtError();
|
|
|
|
// Filter out "fpsw" and "mxcsr". They aren't valid GCC asm clobber
|
|
// constraints. Clang always adds fpsr to the clobber list anyway.
|
|
llvm::erase_if(Clobbers, [](const std::string &C) {
|
|
return C == "fpsr" || C == "mxcsr";
|
|
});
|
|
|
|
// Build the vector of clobber StringRefs.
|
|
ClobberRefs.insert(ClobberRefs.end(), Clobbers.begin(), Clobbers.end());
|
|
|
|
// Recast the void pointers and build the vector of constraint StringRefs.
|
|
unsigned NumExprs = NumOutputs + NumInputs;
|
|
ConstraintRefs.resize(NumExprs);
|
|
Exprs.resize(NumExprs);
|
|
for (unsigned i = 0, e = NumExprs; i != e; ++i) {
|
|
Expr *OpExpr = static_cast<Expr *>(OpExprs[i].first);
|
|
if (!OpExpr)
|
|
return StmtError();
|
|
|
|
// Need address of variable.
|
|
if (OpExprs[i].second)
|
|
OpExpr =
|
|
Actions.BuildUnaryOp(getCurScope(), AsmLoc, UO_AddrOf, OpExpr).get();
|
|
|
|
ConstraintRefs[i] = StringRef(Constraints[i]);
|
|
Exprs[i] = OpExpr;
|
|
}
|
|
|
|
// FIXME: We should be passing source locations for better diagnostics.
|
|
return Actions.ActOnMSAsmStmt(AsmLoc, LBraceLocs[0], AsmToks, AsmStringIR,
|
|
NumOutputs, NumInputs, ConstraintRefs,
|
|
ClobberRefs, Exprs, EndLoc);
|
|
}
|
|
|
|
/// parseGNUAsmQualifierListOpt - Parse a GNU extended asm qualifier list.
|
|
/// asm-qualifier:
|
|
/// volatile
|
|
/// inline
|
|
/// goto
|
|
///
|
|
/// asm-qualifier-list:
|
|
/// asm-qualifier
|
|
/// asm-qualifier-list asm-qualifier
|
|
bool Parser::parseGNUAsmQualifierListOpt(GNUAsmQualifiers &AQ) {
|
|
while (1) {
|
|
const GNUAsmQualifiers::AQ A = getGNUAsmQualifier(Tok);
|
|
if (A == GNUAsmQualifiers::AQ_unspecified) {
|
|
if (Tok.isNot(tok::l_paren)) {
|
|
Diag(Tok.getLocation(), diag::err_asm_qualifier_ignored);
|
|
SkipUntil(tok::r_paren, StopAtSemi);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
if (AQ.setAsmQualifier(A))
|
|
Diag(Tok.getLocation(), diag::err_asm_duplicate_qual)
|
|
<< GNUAsmQualifiers::getQualifierName(A);
|
|
ConsumeToken();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// ParseAsmStatement - Parse a GNU extended asm statement.
|
|
/// asm-statement:
|
|
/// gnu-asm-statement
|
|
/// ms-asm-statement
|
|
///
|
|
/// [GNU] gnu-asm-statement:
|
|
/// 'asm' asm-qualifier-list[opt] '(' asm-argument ')' ';'
|
|
///
|
|
/// [GNU] asm-argument:
|
|
/// asm-string-literal
|
|
/// asm-string-literal ':' asm-operands[opt]
|
|
/// asm-string-literal ':' asm-operands[opt] ':' asm-operands[opt]
|
|
/// asm-string-literal ':' asm-operands[opt] ':' asm-operands[opt]
|
|
/// ':' asm-clobbers
|
|
///
|
|
/// [GNU] asm-clobbers:
|
|
/// asm-string-literal
|
|
/// asm-clobbers ',' asm-string-literal
|
|
///
|
|
StmtResult Parser::ParseAsmStatement(bool &msAsm) {
|
|
assert(Tok.is(tok::kw_asm) && "Not an asm stmt");
|
|
SourceLocation AsmLoc = ConsumeToken();
|
|
|
|
if (getLangOpts().AsmBlocks && !isGCCAsmStatement(Tok)) {
|
|
msAsm = true;
|
|
return ParseMicrosoftAsmStatement(AsmLoc);
|
|
}
|
|
|
|
SourceLocation Loc = Tok.getLocation();
|
|
GNUAsmQualifiers GAQ;
|
|
if (parseGNUAsmQualifierListOpt(GAQ))
|
|
return StmtError();
|
|
|
|
if (GAQ.isGoto() && getLangOpts().SpeculativeLoadHardening)
|
|
Diag(Loc, diag::warn_slh_does_not_support_asm_goto);
|
|
|
|
BalancedDelimiterTracker T(*this, tok::l_paren);
|
|
T.consumeOpen();
|
|
|
|
ExprResult AsmString(ParseAsmStringLiteral(/*ForAsmLabel*/ false));
|
|
|
|
// Check if GNU-style InlineAsm is disabled.
|
|
// Error on anything other than empty string.
|
|
if (!(getLangOpts().GNUAsm || AsmString.isInvalid())) {
|
|
const auto *SL = cast<StringLiteral>(AsmString.get());
|
|
if (!SL->getString().trim().empty())
|
|
Diag(Loc, diag::err_gnu_inline_asm_disabled);
|
|
}
|
|
|
|
if (AsmString.isInvalid()) {
|
|
// Consume up to and including the closing paren.
|
|
T.skipToEnd();
|
|
return StmtError();
|
|
}
|
|
|
|
SmallVector<IdentifierInfo *, 4> Names;
|
|
ExprVector Constraints;
|
|
ExprVector Exprs;
|
|
ExprVector Clobbers;
|
|
|
|
if (Tok.is(tok::r_paren)) {
|
|
// We have a simple asm expression like 'asm("foo")'.
|
|
T.consumeClose();
|
|
return Actions.ActOnGCCAsmStmt(
|
|
AsmLoc, /*isSimple*/ true, GAQ.isVolatile(),
|
|
/*NumOutputs*/ 0, /*NumInputs*/ 0, nullptr, Constraints, Exprs,
|
|
AsmString.get(), Clobbers, /*NumLabels*/ 0, T.getCloseLocation());
|
|
}
|
|
|
|
// Parse Outputs, if present.
|
|
bool AteExtraColon = false;
|
|
if (Tok.is(tok::colon) || Tok.is(tok::coloncolon)) {
|
|
// In C++ mode, parse "::" like ": :".
|
|
AteExtraColon = Tok.is(tok::coloncolon);
|
|
ConsumeToken();
|
|
|
|
if (!AteExtraColon && ParseAsmOperandsOpt(Names, Constraints, Exprs))
|
|
return StmtError();
|
|
}
|
|
|
|
unsigned NumOutputs = Names.size();
|
|
|
|
// Parse Inputs, if present.
|
|
if (AteExtraColon || Tok.is(tok::colon) || Tok.is(tok::coloncolon)) {
|
|
// In C++ mode, parse "::" like ": :".
|
|
if (AteExtraColon)
|
|
AteExtraColon = false;
|
|
else {
|
|
AteExtraColon = Tok.is(tok::coloncolon);
|
|
ConsumeToken();
|
|
}
|
|
|
|
if (!AteExtraColon && ParseAsmOperandsOpt(Names, Constraints, Exprs))
|
|
return StmtError();
|
|
}
|
|
|
|
assert(Names.size() == Constraints.size() &&
|
|
Constraints.size() == Exprs.size() && "Input operand size mismatch!");
|
|
|
|
unsigned NumInputs = Names.size() - NumOutputs;
|
|
|
|
// Parse the clobbers, if present.
|
|
if (AteExtraColon || Tok.is(tok::colon) || Tok.is(tok::coloncolon)) {
|
|
if (AteExtraColon)
|
|
AteExtraColon = false;
|
|
else {
|
|
AteExtraColon = Tok.is(tok::coloncolon);
|
|
ConsumeToken();
|
|
}
|
|
// Parse the asm-string list for clobbers if present.
|
|
if (!AteExtraColon && isTokenStringLiteral()) {
|
|
while (1) {
|
|
ExprResult Clobber(ParseAsmStringLiteral(/*ForAsmLabel*/ false));
|
|
|
|
if (Clobber.isInvalid())
|
|
break;
|
|
|
|
Clobbers.push_back(Clobber.get());
|
|
|
|
if (!TryConsumeToken(tok::comma))
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!GAQ.isGoto() && (Tok.isNot(tok::r_paren) || AteExtraColon)) {
|
|
Diag(Tok, diag::err_expected) << tok::r_paren;
|
|
SkipUntil(tok::r_paren, StopAtSemi);
|
|
return StmtError();
|
|
}
|
|
|
|
// Parse the goto label, if present.
|
|
unsigned NumLabels = 0;
|
|
if (AteExtraColon || Tok.is(tok::colon)) {
|
|
if (!AteExtraColon)
|
|
ConsumeToken();
|
|
|
|
while (true) {
|
|
if (Tok.isNot(tok::identifier)) {
|
|
Diag(Tok, diag::err_expected) << tok::identifier;
|
|
SkipUntil(tok::r_paren, StopAtSemi);
|
|
return StmtError();
|
|
}
|
|
LabelDecl *LD = Actions.LookupOrCreateLabel(Tok.getIdentifierInfo(),
|
|
Tok.getLocation());
|
|
Names.push_back(Tok.getIdentifierInfo());
|
|
if (!LD) {
|
|
SkipUntil(tok::r_paren, StopAtSemi);
|
|
return StmtError();
|
|
}
|
|
ExprResult Res =
|
|
Actions.ActOnAddrLabel(Tok.getLocation(), Tok.getLocation(), LD);
|
|
Exprs.push_back(Res.get());
|
|
NumLabels++;
|
|
ConsumeToken();
|
|
if (!TryConsumeToken(tok::comma))
|
|
break;
|
|
}
|
|
} else if (GAQ.isGoto()) {
|
|
Diag(Tok, diag::err_expected) << tok::colon;
|
|
SkipUntil(tok::r_paren, StopAtSemi);
|
|
return StmtError();
|
|
}
|
|
T.consumeClose();
|
|
return Actions.ActOnGCCAsmStmt(AsmLoc, false, GAQ.isVolatile(), NumOutputs,
|
|
NumInputs, Names.data(), Constraints, Exprs,
|
|
AsmString.get(), Clobbers, NumLabels,
|
|
T.getCloseLocation());
|
|
}
|
|
|
|
/// ParseAsmOperands - Parse the asm-operands production as used by
|
|
/// asm-statement, assuming the leading ':' token was eaten.
|
|
///
|
|
/// [GNU] asm-operands:
|
|
/// asm-operand
|
|
/// asm-operands ',' asm-operand
|
|
///
|
|
/// [GNU] asm-operand:
|
|
/// asm-string-literal '(' expression ')'
|
|
/// '[' identifier ']' asm-string-literal '(' expression ')'
|
|
///
|
|
//
|
|
// FIXME: Avoid unnecessary std::string trashing.
|
|
bool Parser::ParseAsmOperandsOpt(SmallVectorImpl<IdentifierInfo *> &Names,
|
|
SmallVectorImpl<Expr *> &Constraints,
|
|
SmallVectorImpl<Expr *> &Exprs) {
|
|
// 'asm-operands' isn't present?
|
|
if (!isTokenStringLiteral() && Tok.isNot(tok::l_square))
|
|
return false;
|
|
|
|
while (1) {
|
|
// Read the [id] if present.
|
|
if (Tok.is(tok::l_square)) {
|
|
BalancedDelimiterTracker T(*this, tok::l_square);
|
|
T.consumeOpen();
|
|
|
|
if (Tok.isNot(tok::identifier)) {
|
|
Diag(Tok, diag::err_expected) << tok::identifier;
|
|
SkipUntil(tok::r_paren, StopAtSemi);
|
|
return true;
|
|
}
|
|
|
|
IdentifierInfo *II = Tok.getIdentifierInfo();
|
|
ConsumeToken();
|
|
|
|
Names.push_back(II);
|
|
T.consumeClose();
|
|
} else
|
|
Names.push_back(nullptr);
|
|
|
|
ExprResult Constraint(ParseAsmStringLiteral(/*ForAsmLabel*/ false));
|
|
if (Constraint.isInvalid()) {
|
|
SkipUntil(tok::r_paren, StopAtSemi);
|
|
return true;
|
|
}
|
|
Constraints.push_back(Constraint.get());
|
|
|
|
if (Tok.isNot(tok::l_paren)) {
|
|
Diag(Tok, diag::err_expected_lparen_after) << "asm operand";
|
|
SkipUntil(tok::r_paren, StopAtSemi);
|
|
return true;
|
|
}
|
|
|
|
// Read the parenthesized expression.
|
|
BalancedDelimiterTracker T(*this, tok::l_paren);
|
|
T.consumeOpen();
|
|
ExprResult Res = Actions.CorrectDelayedTyposInExpr(ParseExpression());
|
|
T.consumeClose();
|
|
if (Res.isInvalid()) {
|
|
SkipUntil(tok::r_paren, StopAtSemi);
|
|
return true;
|
|
}
|
|
Exprs.push_back(Res.get());
|
|
// Eat the comma and continue parsing if it exists.
|
|
if (!TryConsumeToken(tok::comma))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const char *Parser::GNUAsmQualifiers::getQualifierName(AQ Qualifier) {
|
|
switch (Qualifier) {
|
|
case AQ_volatile: return "volatile";
|
|
case AQ_inline: return "inline";
|
|
case AQ_goto: return "goto";
|
|
case AQ_unspecified: return "unspecified";
|
|
}
|
|
llvm_unreachable("Unknown GNUAsmQualifier");
|
|
}
|
|
|
|
Parser::GNUAsmQualifiers::AQ
|
|
Parser::getGNUAsmQualifier(const Token &Tok) const {
|
|
switch (Tok.getKind()) {
|
|
case tok::kw_volatile: return GNUAsmQualifiers::AQ_volatile;
|
|
case tok::kw_inline: return GNUAsmQualifiers::AQ_inline;
|
|
case tok::kw_goto: return GNUAsmQualifiers::AQ_goto;
|
|
default: return GNUAsmQualifiers::AQ_unspecified;
|
|
}
|
|
}
|
|
bool Parser::GNUAsmQualifiers::setAsmQualifier(AQ Qualifier) {
|
|
bool IsDuplicate = Qualifiers & Qualifier;
|
|
Qualifiers |= Qualifier;
|
|
return IsDuplicate;
|
|
}
|