//===---- 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/Parse/Parser.h" #include "clang/AST/ASTContext.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/TargetInfo.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/Support/SourceMgr.h" #include "llvm/Support/TargetRegistry.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 AsmToks; /// The offset of each token in AsmToks within AsmString. ArrayRef AsmTokOffsets; public: ClangAsmParserCallback(Parser &P, SourceLocation Loc, StringRef AsmString, ArrayRef Toks, ArrayRef 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 &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 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 &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()(Str.begin(), AsmString.begin()) && !std::less()(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 &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 AsmToks, SmallVectorImpl &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 AsmToks; bool SingleLineMode = true; unsigned BraceNesting = 0; unsigned short savedBraceCount = BraceCount; bool InAsmComment = false; FileID FID; unsigned LineNo = 0; unsigned NumTokensRead = 0; SmallVector 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 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 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 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 ConstraintRefs; SmallVector Exprs; SmallVector 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 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 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 MAI( TheTarget->createMCAsmInfo(*MRI, TT, MCOptions)); // Get the instruction descriptor. std::unique_ptr MII(TheTarget->createMCInstrInfo()); std::unique_ptr 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 MOFI( TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false)); Ctx.setObjectFileInfo(MOFI.get()); std::unique_ptr Buffer = llvm::MemoryBuffer::getMemBuffer(AsmString, ""); // Tell SrcMgr about this buffer, which is what the parser will pick up. TempSrcMgr.AddNewSourceBuffer(std::move(Buffer), llvm::SMLoc()); std::unique_ptr Str(createNullStreamer(Ctx)); std::unique_ptr Parser( createMCAsmParser(TempSrcMgr, Ctx, *Str.get(), *MAI)); std::unique_ptr 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 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, 4> OpExprs; SmallVector Constraints; SmallVector 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(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(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 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 &Names, SmallVectorImpl &Constraints, SmallVectorImpl &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; }