123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963 |
- //===---- 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,
- /*ObjectHasErrors=*/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 (true);
- 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 (true) {
- 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 (true) {
- 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 (true) {
- // 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;
- }
|