| //===---- 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; | 
 | } |