901 lines · cpp
1//===---- ParseStmtAsm.cpp - Assembly Statement Parser --------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements parsing for GCC and Microsoft inline assembly.10//11//===----------------------------------------------------------------------===//12 13#include "clang/AST/ASTContext.h"14#include "clang/Basic/Diagnostic.h"15#include "clang/Basic/TargetInfo.h"16#include "clang/Parse/Parser.h"17#include "clang/Parse/RAIIObjectsForParser.h"18#include "llvm/ADT/StringExtras.h"19#include "llvm/MC/MCAsmInfo.h"20#include "llvm/MC/MCContext.h"21#include "llvm/MC/MCInstPrinter.h"22#include "llvm/MC/MCInstrInfo.h"23#include "llvm/MC/MCObjectFileInfo.h"24#include "llvm/MC/MCParser/MCAsmParser.h"25#include "llvm/MC/MCParser/MCTargetAsmParser.h"26#include "llvm/MC/MCRegisterInfo.h"27#include "llvm/MC/MCStreamer.h"28#include "llvm/MC/MCSubtargetInfo.h"29#include "llvm/MC/MCTargetOptions.h"30#include "llvm/MC/TargetRegistry.h"31#include "llvm/Support/SourceMgr.h"32using namespace clang;33 34namespace {35class ClangAsmParserCallback : public llvm::MCAsmParserSemaCallback {36 Parser &TheParser;37 SourceLocation AsmLoc;38 StringRef AsmString;39 40 /// The tokens we streamed into AsmString and handed off to MC.41 ArrayRef<Token> AsmToks;42 43 /// The offset of each token in AsmToks within AsmString.44 ArrayRef<unsigned> AsmTokOffsets;45 46public:47 ClangAsmParserCallback(Parser &P, SourceLocation Loc, StringRef AsmString,48 ArrayRef<Token> Toks, ArrayRef<unsigned> Offsets)49 : TheParser(P), AsmLoc(Loc), AsmString(AsmString), AsmToks(Toks),50 AsmTokOffsets(Offsets) {51 assert(AsmToks.size() == AsmTokOffsets.size());52 }53 54 void LookupInlineAsmIdentifier(StringRef &LineBuf,55 llvm::InlineAsmIdentifierInfo &Info,56 bool IsUnevaluatedContext) override;57 58 StringRef LookupInlineAsmLabel(StringRef Identifier, llvm::SourceMgr &LSM,59 llvm::SMLoc Location,60 bool Create) override;61 62 bool LookupInlineAsmField(StringRef Base, StringRef Member,63 unsigned &Offset) override {64 return TheParser.getActions().LookupInlineAsmField(Base, Member, Offset,65 AsmLoc);66 }67 68 static void DiagHandlerCallback(const llvm::SMDiagnostic &D, void *Context) {69 ((ClangAsmParserCallback *)Context)->handleDiagnostic(D);70 }71 72private:73 /// Collect the appropriate tokens for the given string.74 void findTokensForString(StringRef Str, SmallVectorImpl<Token> &TempToks,75 const Token *&FirstOrigToken) const;76 77 SourceLocation translateLocation(const llvm::SourceMgr &LSM,78 llvm::SMLoc SMLoc);79 80 void handleDiagnostic(const llvm::SMDiagnostic &D);81};82}83 84void ClangAsmParserCallback::LookupInlineAsmIdentifier(85 StringRef &LineBuf, llvm::InlineAsmIdentifierInfo &Info,86 bool IsUnevaluatedContext) {87 // Collect the desired tokens.88 SmallVector<Token, 16> LineToks;89 const Token *FirstOrigToken = nullptr;90 findTokensForString(LineBuf, LineToks, FirstOrigToken);91 92 unsigned NumConsumedToks;93 ExprResult Result = TheParser.ParseMSAsmIdentifier(LineToks, NumConsumedToks,94 IsUnevaluatedContext);95 96 // If we consumed the entire line, tell MC that.97 // Also do this if we consumed nothing as a way of reporting failure.98 if (NumConsumedToks == 0 || NumConsumedToks == LineToks.size()) {99 // By not modifying LineBuf, we're implicitly consuming it all.100 101 // Otherwise, consume up to the original tokens.102 } else {103 assert(FirstOrigToken && "not using original tokens?");104 105 // Since we're using original tokens, apply that offset.106 assert(FirstOrigToken[NumConsumedToks].getLocation() ==107 LineToks[NumConsumedToks].getLocation());108 unsigned FirstIndex = FirstOrigToken - AsmToks.begin();109 unsigned LastIndex = FirstIndex + NumConsumedToks - 1;110 111 // The total length we've consumed is the relative offset112 // of the last token we consumed plus its length.113 unsigned TotalOffset =114 (AsmTokOffsets[LastIndex] + AsmToks[LastIndex].getLength() -115 AsmTokOffsets[FirstIndex]);116 LineBuf = LineBuf.substr(0, TotalOffset);117 }118 119 // Initialize Info with the lookup result.120 if (!Result.isUsable())121 return;122 TheParser.getActions().FillInlineAsmIdentifierInfo(Result.get(), Info);123}124 125StringRef ClangAsmParserCallback::LookupInlineAsmLabel(StringRef Identifier,126 llvm::SourceMgr &LSM,127 llvm::SMLoc Location,128 bool Create) {129 SourceLocation Loc = translateLocation(LSM, Location);130 LabelDecl *Label =131 TheParser.getActions().GetOrCreateMSAsmLabel(Identifier, Loc, Create);132 return Label->getMSAsmLabel();133}134 135void ClangAsmParserCallback::findTokensForString(136 StringRef Str, SmallVectorImpl<Token> &TempToks,137 const Token *&FirstOrigToken) const {138 // For now, assert that the string we're working with is a substring139 // of what we gave to MC. This lets us use the original tokens.140 assert(!std::less<const char *>()(Str.begin(), AsmString.begin()) &&141 !std::less<const char *>()(AsmString.end(), Str.end()));142 143 // Try to find a token whose offset matches the first token.144 unsigned FirstCharOffset = Str.begin() - AsmString.begin();145 const unsigned *FirstTokOffset =146 llvm::lower_bound(AsmTokOffsets, FirstCharOffset);147 148 // For now, assert that the start of the string exactly149 // corresponds to the start of a token.150 assert(*FirstTokOffset == FirstCharOffset);151 152 // Use all the original tokens for this line. (We assume the153 // end of the line corresponds cleanly to a token break.)154 unsigned FirstTokIndex = FirstTokOffset - AsmTokOffsets.begin();155 FirstOrigToken = &AsmToks[FirstTokIndex];156 unsigned LastCharOffset = Str.end() - AsmString.begin();157 for (unsigned i = FirstTokIndex, e = AsmTokOffsets.size(); i != e; ++i) {158 if (AsmTokOffsets[i] >= LastCharOffset)159 break;160 TempToks.push_back(AsmToks[i]);161 }162}163 164SourceLocation165ClangAsmParserCallback::translateLocation(const llvm::SourceMgr &LSM,166 llvm::SMLoc SMLoc) {167 // Compute an offset into the inline asm buffer.168 // FIXME: This isn't right if .macro is involved (but hopefully, no169 // real-world code does that).170 const llvm::MemoryBuffer *LBuf =171 LSM.getMemoryBuffer(LSM.FindBufferContainingLoc(SMLoc));172 unsigned Offset = SMLoc.getPointer() - LBuf->getBufferStart();173 174 // Figure out which token that offset points into.175 const unsigned *TokOffsetPtr = llvm::lower_bound(AsmTokOffsets, Offset);176 unsigned TokIndex = TokOffsetPtr - AsmTokOffsets.begin();177 unsigned TokOffset = *TokOffsetPtr;178 179 // If we come up with an answer which seems sane, use it; otherwise,180 // just point at the __asm keyword.181 // FIXME: Assert the answer is sane once we handle .macro correctly.182 SourceLocation Loc = AsmLoc;183 if (TokIndex < AsmToks.size()) {184 const Token &Tok = AsmToks[TokIndex];185 Loc = Tok.getLocation();186 Loc = Loc.getLocWithOffset(Offset - TokOffset);187 }188 return Loc;189}190 191void ClangAsmParserCallback::handleDiagnostic(const llvm::SMDiagnostic &D) {192 const llvm::SourceMgr &LSM = *D.getSourceMgr();193 SourceLocation Loc = translateLocation(LSM, D.getLoc());194 TheParser.Diag(Loc, diag::err_inline_ms_asm_parsing) << D.getMessage();195}196 197ExprResult Parser::ParseMSAsmIdentifier(llvm::SmallVectorImpl<Token> &LineToks,198 unsigned &NumLineToksConsumed,199 bool IsUnevaluatedContext) {200 // Push a fake token on the end so that we don't overrun the token201 // stream. We use ';' because it expression-parsing should never202 // overrun it.203 const tok::TokenKind EndOfStream = tok::semi;204 Token EndOfStreamTok;205 EndOfStreamTok.startToken();206 EndOfStreamTok.setKind(EndOfStream);207 LineToks.push_back(EndOfStreamTok);208 209 // Also copy the current token over.210 LineToks.push_back(Tok);211 212 PP.EnterTokenStream(LineToks, /*DisableMacroExpansions*/ true,213 /*IsReinject*/ true);214 215 // Clear the current token and advance to the first token in LineToks.216 ConsumeAnyToken();217 218 // Parse an optional scope-specifier if we're in C++.219 CXXScopeSpec SS;220 if (getLangOpts().CPlusPlus)221 ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,222 /*ObjectHasErrors=*/false,223 /*EnteringContext=*/false);224 225 // Require an identifier here.226 SourceLocation TemplateKWLoc;227 UnqualifiedId Id;228 bool Invalid = true;229 ExprResult Result;230 if (Tok.is(tok::kw_this)) {231 Result = ParseCXXThis();232 Invalid = false;233 } else {234 Invalid =235 ParseUnqualifiedId(SS, /*ObjectType=*/nullptr,236 /*ObjectHadErrors=*/false,237 /*EnteringContext=*/false,238 /*AllowDestructorName=*/false,239 /*AllowConstructorName=*/false,240 /*AllowDeductionGuide=*/false, &TemplateKWLoc, Id);241 // Perform the lookup.242 Result = Actions.LookupInlineAsmIdentifier(SS, TemplateKWLoc, Id,243 IsUnevaluatedContext);244 }245 // While the next two tokens are 'period' 'identifier', repeatedly parse it as246 // a field access. We have to avoid consuming assembler directives that look247 // like '.' 'else'.248 while (Result.isUsable() && Tok.is(tok::period)) {249 Token IdTok = PP.LookAhead(0);250 if (IdTok.isNot(tok::identifier))251 break;252 ConsumeToken(); // Consume the period.253 IdentifierInfo *Id = Tok.getIdentifierInfo();254 ConsumeToken(); // Consume the identifier.255 Result = Actions.LookupInlineAsmVarDeclField(Result.get(), Id->getName(),256 Tok.getLocation());257 }258 259 // Figure out how many tokens we are into LineToks.260 unsigned LineIndex = 0;261 if (Tok.is(EndOfStream)) {262 LineIndex = LineToks.size() - 2;263 } else {264 while (LineToks[LineIndex].getLocation() != Tok.getLocation()) {265 LineIndex++;266 assert(LineIndex < LineToks.size() - 2); // we added two extra tokens267 }268 }269 270 // If we've run into the poison token we inserted before, or there271 // was a parsing error, then claim the entire line.272 if (Invalid || Tok.is(EndOfStream)) {273 NumLineToksConsumed = LineToks.size() - 2;274 } else {275 // Otherwise, claim up to the start of the next token.276 NumLineToksConsumed = LineIndex;277 }278 279 // Finally, restore the old parsing state by consuming all the tokens we280 // staged before, implicitly killing off the token-lexer we pushed.281 for (unsigned i = 0, e = LineToks.size() - LineIndex - 2; i != e; ++i) {282 ConsumeAnyToken();283 }284 assert(Tok.is(EndOfStream));285 ConsumeToken();286 287 // Leave LineToks in its original state.288 LineToks.pop_back();289 LineToks.pop_back();290 291 return Result;292}293 294/// Turn a sequence of our tokens back into a string that we can hand295/// to the MC asm parser.296static bool buildMSAsmString(Preprocessor &PP, SourceLocation AsmLoc,297 ArrayRef<Token> AsmToks,298 SmallVectorImpl<unsigned> &TokOffsets,299 SmallString<512> &Asm) {300 assert(!AsmToks.empty() && "Didn't expect an empty AsmToks!");301 302 // Is this the start of a new assembly statement?303 bool isNewStatement = true;304 305 for (unsigned i = 0, e = AsmToks.size(); i < e; ++i) {306 const Token &Tok = AsmToks[i];307 308 // Start each new statement with a newline and a tab.309 if (!isNewStatement && (Tok.is(tok::kw_asm) || Tok.isAtStartOfLine())) {310 Asm += "\n\t";311 isNewStatement = true;312 }313 314 // Preserve the existence of leading whitespace except at the315 // start of a statement.316 if (!isNewStatement && Tok.hasLeadingSpace())317 Asm += ' ';318 319 // Remember the offset of this token.320 TokOffsets.push_back(Asm.size());321 322 // Don't actually write '__asm' into the assembly stream.323 if (Tok.is(tok::kw_asm)) {324 // Complain about __asm at the end of the stream.325 if (i + 1 == e) {326 PP.Diag(AsmLoc, diag::err_asm_empty);327 return true;328 }329 330 continue;331 }332 333 // Append the spelling of the token.334 SmallString<32> SpellingBuffer;335 bool SpellingInvalid = false;336 Asm += PP.getSpelling(Tok, SpellingBuffer, &SpellingInvalid);337 assert(!SpellingInvalid && "spelling was invalid after correct parse?");338 339 // We are no longer at the start of a statement.340 isNewStatement = false;341 }342 343 // Ensure that the buffer is null-terminated.344 Asm.push_back('\0');345 Asm.pop_back();346 347 assert(TokOffsets.size() == AsmToks.size());348 return false;349}350 351bool Parser::isGCCAsmStatement(const Token &TokAfterAsm) const {352 return TokAfterAsm.is(tok::l_paren) || isGNUAsmQualifier(TokAfterAsm);353}354 355bool Parser::isGNUAsmQualifier(const Token &TokAfterAsm) const {356 return getGNUAsmQualifier(TokAfterAsm) != GNUAsmQualifiers::AQ_unspecified;357}358 359StmtResult Parser::ParseMicrosoftAsmStatement(SourceLocation AsmLoc) {360 SourceManager &SrcMgr = PP.getSourceManager();361 SourceLocation EndLoc = AsmLoc;362 SmallVector<Token, 4> AsmToks;363 364 bool SingleLineMode = true;365 unsigned BraceNesting = 0;366 unsigned short savedBraceCount = BraceCount;367 bool InAsmComment = false;368 FileID FID;369 unsigned LineNo = 0;370 unsigned NumTokensRead = 0;371 SmallVector<SourceLocation, 4> LBraceLocs;372 bool SkippedStartOfLine = false;373 374 if (Tok.is(tok::l_brace)) {375 // Braced inline asm: consume the opening brace.376 SingleLineMode = false;377 BraceNesting = 1;378 EndLoc = ConsumeBrace();379 LBraceLocs.push_back(EndLoc);380 ++NumTokensRead;381 } else {382 // Single-line inline asm; compute which line it is on.383 FileIDAndOffset ExpAsmLoc = SrcMgr.getDecomposedExpansionLoc(EndLoc);384 FID = ExpAsmLoc.first;385 LineNo = SrcMgr.getLineNumber(FID, ExpAsmLoc.second);386 LBraceLocs.push_back(SourceLocation());387 }388 389 SourceLocation TokLoc = Tok.getLocation();390 do {391 // If we hit EOF, we're done, period.392 if (isEofOrEom())393 break;394 395 if (!InAsmComment && Tok.is(tok::l_brace)) {396 // Consume the opening brace.397 SkippedStartOfLine = Tok.isAtStartOfLine();398 AsmToks.push_back(Tok);399 EndLoc = ConsumeBrace();400 BraceNesting++;401 LBraceLocs.push_back(EndLoc);402 TokLoc = Tok.getLocation();403 ++NumTokensRead;404 continue;405 } else if (!InAsmComment && Tok.is(tok::semi)) {406 // A semicolon in an asm is the start of a comment.407 InAsmComment = true;408 if (!SingleLineMode) {409 // Compute which line the comment is on.410 FileIDAndOffset ExpSemiLoc = SrcMgr.getDecomposedExpansionLoc(TokLoc);411 FID = ExpSemiLoc.first;412 LineNo = SrcMgr.getLineNumber(FID, ExpSemiLoc.second);413 }414 } else if (SingleLineMode || InAsmComment) {415 // If end-of-line is significant, check whether this token is on a416 // new line.417 FileIDAndOffset ExpLoc = SrcMgr.getDecomposedExpansionLoc(TokLoc);418 if (ExpLoc.first != FID ||419 SrcMgr.getLineNumber(ExpLoc.first, ExpLoc.second) != LineNo) {420 // If this is a single-line __asm, we're done, except if the next421 // line is MS-style asm too, in which case we finish a comment422 // if needed and then keep processing the next line as a single423 // line __asm.424 bool isAsm = Tok.is(tok::kw_asm);425 if (SingleLineMode && (!isAsm || isGCCAsmStatement(NextToken())))426 break;427 // We're no longer in a comment.428 InAsmComment = false;429 if (isAsm) {430 // If this is a new __asm {} block we want to process it separately431 // from the single-line __asm statements432 if (PP.LookAhead(0).is(tok::l_brace))433 break;434 LineNo = SrcMgr.getLineNumber(ExpLoc.first, ExpLoc.second);435 SkippedStartOfLine = Tok.isAtStartOfLine();436 } else if (Tok.is(tok::semi)) {437 // A multi-line asm-statement, where next line is a comment438 InAsmComment = true;439 FID = ExpLoc.first;440 LineNo = SrcMgr.getLineNumber(FID, ExpLoc.second);441 }442 } else if (!InAsmComment && Tok.is(tok::r_brace)) {443 // In MSVC mode, braces only participate in brace matching and444 // separating the asm statements. This is an intentional445 // departure from the Apple gcc behavior.446 if (!BraceNesting)447 break;448 }449 }450 if (!InAsmComment && BraceNesting && Tok.is(tok::r_brace) &&451 BraceCount == (savedBraceCount + BraceNesting)) {452 // Consume the closing brace.453 SkippedStartOfLine = Tok.isAtStartOfLine();454 // Don't want to add the closing brace of the whole asm block455 if (SingleLineMode || BraceNesting > 1) {456 Tok.clearFlag(Token::LeadingSpace);457 AsmToks.push_back(Tok);458 }459 EndLoc = ConsumeBrace();460 BraceNesting--;461 // Finish if all of the opened braces in the inline asm section were462 // consumed.463 if (BraceNesting == 0 && !SingleLineMode)464 break;465 else {466 LBraceLocs.pop_back();467 TokLoc = Tok.getLocation();468 ++NumTokensRead;469 continue;470 }471 }472 473 // Consume the next token; make sure we don't modify the brace count etc.474 // if we are in a comment.475 EndLoc = TokLoc;476 if (InAsmComment)477 PP.Lex(Tok);478 else {479 // Set the token as the start of line if we skipped the original start480 // of line token in case it was a nested brace.481 if (SkippedStartOfLine)482 Tok.setFlag(Token::StartOfLine);483 AsmToks.push_back(Tok);484 ConsumeAnyToken();485 }486 TokLoc = Tok.getLocation();487 ++NumTokensRead;488 SkippedStartOfLine = false;489 } while (true);490 491 if (BraceNesting && BraceCount != savedBraceCount) {492 // __asm without closing brace (this can happen at EOF).493 for (unsigned i = 0; i < BraceNesting; ++i) {494 Diag(Tok, diag::err_expected) << tok::r_brace;495 Diag(LBraceLocs.back(), diag::note_matching) << tok::l_brace;496 LBraceLocs.pop_back();497 }498 return StmtError();499 } else if (NumTokensRead == 0) {500 // Empty __asm.501 Diag(Tok, diag::err_expected) << tok::l_brace;502 return StmtError();503 }504 505 // Okay, prepare to use MC to parse the assembly.506 SmallVector<StringRef, 4> ConstraintRefs;507 SmallVector<Expr *, 4> Exprs;508 SmallVector<StringRef, 4> ClobberRefs;509 510 // We need an actual supported target.511 const llvm::Triple &TheTriple = Actions.Context.getTargetInfo().getTriple();512 const llvm::Target *TheTarget = nullptr;513 if (!TheTriple.isX86()) {514 Diag(AsmLoc, diag::err_msasm_unsupported_arch) << TheTriple.getArchName();515 } else {516 std::string Error;517 TheTarget = llvm::TargetRegistry::lookupTarget(TheTriple, Error);518 if (!TheTarget)519 Diag(AsmLoc, diag::err_msasm_unable_to_create_target) << Error;520 }521 522 assert(!LBraceLocs.empty() && "Should have at least one location here");523 524 SmallString<512> AsmString;525 auto EmptyStmt = [&] {526 return Actions.ActOnMSAsmStmt(AsmLoc, LBraceLocs[0], AsmToks, AsmString,527 /*NumOutputs*/ 0, /*NumInputs*/ 0,528 ConstraintRefs, ClobberRefs, Exprs, EndLoc);529 };530 // If we don't support assembly, or the assembly is empty, we don't531 // need to instantiate the AsmParser, etc.532 if (!TheTarget || AsmToks.empty()) {533 return EmptyStmt();534 }535 536 // Expand the tokens into a string buffer.537 SmallVector<unsigned, 8> TokOffsets;538 if (buildMSAsmString(PP, AsmLoc, AsmToks, TokOffsets, AsmString))539 return StmtError();540 541 const TargetOptions &TO = Actions.Context.getTargetInfo().getTargetOpts();542 std::string FeaturesStr =543 llvm::join(TO.Features.begin(), TO.Features.end(), ",");544 545 std::unique_ptr<llvm::MCRegisterInfo> MRI(546 TheTarget->createMCRegInfo(TheTriple));547 if (!MRI) {548 Diag(AsmLoc, diag::err_msasm_unable_to_create_target)549 << "target MC unavailable";550 return EmptyStmt();551 }552 // FIXME: init MCOptions from sanitizer flags here.553 llvm::MCTargetOptions MCOptions;554 std::unique_ptr<llvm::MCAsmInfo> MAI(555 TheTarget->createMCAsmInfo(*MRI, TheTriple, MCOptions));556 // Get the instruction descriptor.557 std::unique_ptr<llvm::MCInstrInfo> MII(TheTarget->createMCInstrInfo());558 std::unique_ptr<llvm::MCSubtargetInfo> STI(559 TheTarget->createMCSubtargetInfo(TheTriple, TO.CPU, FeaturesStr));560 // Target MCTargetDesc may not be linked in clang-based tools.561 562 if (!MAI || !MII || !STI) {563 Diag(AsmLoc, diag::err_msasm_unable_to_create_target)564 << "target MC unavailable";565 return EmptyStmt();566 }567 568 llvm::SourceMgr TempSrcMgr;569 llvm::MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &TempSrcMgr);570 std::unique_ptr<llvm::MCObjectFileInfo> MOFI(571 TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false));572 Ctx.setObjectFileInfo(MOFI.get());573 574 std::unique_ptr<llvm::MemoryBuffer> Buffer =575 llvm::MemoryBuffer::getMemBuffer(AsmString, "<MS inline asm>");576 577 // Tell SrcMgr about this buffer, which is what the parser will pick up.578 TempSrcMgr.AddNewSourceBuffer(std::move(Buffer), llvm::SMLoc());579 580 std::unique_ptr<llvm::MCStreamer> Str(createNullStreamer(Ctx));581 std::unique_ptr<llvm::MCAsmParser> Parser(582 createMCAsmParser(TempSrcMgr, Ctx, *Str, *MAI));583 584 std::unique_ptr<llvm::MCTargetAsmParser> TargetParser(585 TheTarget->createMCAsmParser(*STI, *Parser, *MII, MCOptions));586 // Target AsmParser may not be linked in clang-based tools.587 if (!TargetParser) {588 Diag(AsmLoc, diag::err_msasm_unable_to_create_target)589 << "target ASM parser unavailable";590 return EmptyStmt();591 }592 593 std::unique_ptr<llvm::MCInstPrinter> IP(594 TheTarget->createMCInstPrinter(TheTriple, 1, *MAI, *MII, *MRI));595 596 // Change to the Intel dialect.597 Parser->setAssemblerDialect(1);598 Parser->setTargetParser(*TargetParser);599 Parser->setParsingMSInlineAsm(true);600 TargetParser->setParsingMSInlineAsm(true);601 602 ClangAsmParserCallback Callback(*this, AsmLoc, AsmString, AsmToks,603 TokOffsets);604 TargetParser->setSemaCallback(&Callback);605 TempSrcMgr.setDiagHandler(ClangAsmParserCallback::DiagHandlerCallback,606 &Callback);607 608 unsigned NumOutputs;609 unsigned NumInputs;610 std::string AsmStringIR;611 SmallVector<std::pair<void *, bool>, 4> OpExprs;612 SmallVector<std::string, 4> Constraints;613 SmallVector<std::string, 4> Clobbers;614 if (Parser->parseMSInlineAsm(AsmStringIR, NumOutputs, NumInputs, OpExprs,615 Constraints, Clobbers, MII.get(), IP.get(),616 Callback))617 return StmtError();618 619 // Filter out "fpsw" and "mxcsr". They aren't valid GCC asm clobber620 // constraints. Clang always adds fpsr to the clobber list anyway.621 llvm::erase_if(Clobbers, [](const std::string &C) {622 return C == "fpsr" || C == "mxcsr";623 });624 625 // Build the vector of clobber StringRefs.626 llvm::append_range(ClobberRefs, Clobbers);627 628 // Recast the void pointers and build the vector of constraint StringRefs.629 unsigned NumExprs = NumOutputs + NumInputs;630 ConstraintRefs.resize(NumExprs);631 Exprs.resize(NumExprs);632 for (unsigned i = 0, e = NumExprs; i != e; ++i) {633 Expr *OpExpr = static_cast<Expr *>(OpExprs[i].first);634 if (!OpExpr)635 return StmtError();636 637 // Need address of variable.638 if (OpExprs[i].second)639 OpExpr =640 Actions.BuildUnaryOp(getCurScope(), AsmLoc, UO_AddrOf, OpExpr).get();641 642 ConstraintRefs[i] = StringRef(Constraints[i]);643 Exprs[i] = OpExpr;644 }645 646 // FIXME: We should be passing source locations for better diagnostics.647 return Actions.ActOnMSAsmStmt(AsmLoc, LBraceLocs[0], AsmToks, AsmStringIR,648 NumOutputs, NumInputs, ConstraintRefs,649 ClobberRefs, Exprs, EndLoc);650}651 652bool Parser::parseGNUAsmQualifierListOpt(GNUAsmQualifiers &AQ) {653 while (true) {654 const GNUAsmQualifiers::AQ A = getGNUAsmQualifier(Tok);655 if (A == GNUAsmQualifiers::AQ_unspecified) {656 if (Tok.isNot(tok::l_paren)) {657 Diag(Tok.getLocation(), diag::err_asm_qualifier_ignored);658 SkipUntil(tok::r_paren, StopAtSemi);659 return true;660 }661 return false;662 }663 if (AQ.setAsmQualifier(A))664 Diag(Tok.getLocation(), diag::err_asm_duplicate_qual)665 << GNUAsmQualifiers::getQualifierName(A);666 ConsumeToken();667 }668 return false;669}670 671StmtResult Parser::ParseAsmStatement(bool &msAsm) {672 assert(Tok.is(tok::kw_asm) && "Not an asm stmt");673 SourceLocation AsmLoc = ConsumeToken();674 675 if (getLangOpts().AsmBlocks && !isGCCAsmStatement(Tok)) {676 msAsm = true;677 return ParseMicrosoftAsmStatement(AsmLoc);678 }679 680 SourceLocation Loc = Tok.getLocation();681 GNUAsmQualifiers GAQ;682 if (parseGNUAsmQualifierListOpt(GAQ))683 return StmtError();684 685 if (GAQ.isGoto() && getLangOpts().SpeculativeLoadHardening)686 Diag(Loc, diag::warn_slh_does_not_support_asm_goto);687 688 BalancedDelimiterTracker T(*this, tok::l_paren);689 T.consumeOpen();690 691 ExprResult AsmString(ParseAsmStringLiteral(/*ForAsmLabel*/ false));692 693 // Check if GNU-style InlineAsm is disabled.694 // Error on anything other than empty string.695 if (!(getLangOpts().GNUAsm || AsmString.isInvalid())) {696 const auto *SL = cast<StringLiteral>(AsmString.get());697 if (!SL->getString().trim().empty())698 Diag(Loc, diag::err_gnu_inline_asm_disabled);699 }700 701 if (AsmString.isInvalid()) {702 // Consume up to and including the closing paren.703 T.skipToEnd();704 return StmtError();705 }706 707 SmallVector<IdentifierInfo *, 4> Names;708 ExprVector Constraints;709 ExprVector Exprs;710 ExprVector Clobbers;711 712 if (Tok.is(tok::r_paren)) {713 // We have a simple asm expression like 'asm("foo")'.714 T.consumeClose();715 return Actions.ActOnGCCAsmStmt(716 AsmLoc, /*isSimple*/ true, GAQ.isVolatile(),717 /*NumOutputs*/ 0, /*NumInputs*/ 0, nullptr, Constraints, Exprs,718 AsmString.get(), Clobbers, /*NumLabels*/ 0, T.getCloseLocation());719 }720 721 // Parse Outputs, if present.722 bool AteExtraColon = false;723 if (Tok.is(tok::colon) || Tok.is(tok::coloncolon)) {724 // In C++ mode, parse "::" like ": :".725 AteExtraColon = Tok.is(tok::coloncolon);726 ConsumeToken();727 728 if (!AteExtraColon && ParseAsmOperandsOpt(Names, Constraints, Exprs))729 return StmtError();730 }731 732 unsigned NumOutputs = Names.size();733 734 // Parse Inputs, if present.735 if (AteExtraColon || Tok.is(tok::colon) || Tok.is(tok::coloncolon)) {736 // In C++ mode, parse "::" like ": :".737 if (AteExtraColon)738 AteExtraColon = false;739 else {740 AteExtraColon = Tok.is(tok::coloncolon);741 ConsumeToken();742 }743 744 if (!AteExtraColon && ParseAsmOperandsOpt(Names, Constraints, Exprs))745 return StmtError();746 }747 748 assert(Names.size() == Constraints.size() &&749 Constraints.size() == Exprs.size() && "Input operand size mismatch!");750 751 unsigned NumInputs = Names.size() - NumOutputs;752 753 // Parse the clobbers, if present.754 if (AteExtraColon || Tok.is(tok::colon) || Tok.is(tok::coloncolon)) {755 if (AteExtraColon)756 AteExtraColon = false;757 else {758 AteExtraColon = Tok.is(tok::coloncolon);759 ConsumeToken();760 }761 // Parse the asm-string list for clobbers if present.762 if (!AteExtraColon && (isTokenStringLiteral() || Tok.is(tok::l_paren))) {763 while (true) {764 ExprResult Clobber(ParseAsmStringLiteral(/*ForAsmLabel*/ false));765 766 if (Clobber.isInvalid())767 break;768 769 Clobbers.push_back(Clobber.get());770 771 if (!TryConsumeToken(tok::comma))772 break;773 }774 }775 }776 if (!GAQ.isGoto() && (Tok.isNot(tok::r_paren) || AteExtraColon)) {777 Diag(Tok, diag::err_expected) << tok::r_paren;778 SkipUntil(tok::r_paren, StopAtSemi);779 return StmtError();780 }781 782 // Parse the goto label, if present.783 unsigned NumLabels = 0;784 if (AteExtraColon || Tok.is(tok::colon)) {785 if (!AteExtraColon)786 ConsumeToken();787 788 while (true) {789 if (Tok.isNot(tok::identifier)) {790 Diag(Tok, diag::err_expected) << tok::identifier;791 SkipUntil(tok::r_paren, StopAtSemi);792 return StmtError();793 }794 LabelDecl *LD = Actions.LookupOrCreateLabel(Tok.getIdentifierInfo(),795 Tok.getLocation());796 Names.push_back(Tok.getIdentifierInfo());797 if (!LD) {798 SkipUntil(tok::r_paren, StopAtSemi);799 return StmtError();800 }801 ExprResult Res =802 Actions.ActOnAddrLabel(Tok.getLocation(), Tok.getLocation(), LD);803 Exprs.push_back(Res.get());804 NumLabels++;805 ConsumeToken();806 if (!TryConsumeToken(tok::comma))807 break;808 }809 } else if (GAQ.isGoto()) {810 Diag(Tok, diag::err_expected) << tok::colon;811 SkipUntil(tok::r_paren, StopAtSemi);812 return StmtError();813 }814 T.consumeClose();815 return Actions.ActOnGCCAsmStmt(AsmLoc, false, GAQ.isVolatile(), NumOutputs,816 NumInputs, Names.data(), Constraints, Exprs,817 AsmString.get(), Clobbers, NumLabels,818 T.getCloseLocation());819}820 821bool Parser::ParseAsmOperandsOpt(SmallVectorImpl<IdentifierInfo *> &Names,822 SmallVectorImpl<Expr *> &Constraints,823 SmallVectorImpl<Expr *> &Exprs) {824 // 'asm-operands' isn't present825 if (Tok.isOneOf(tok::colon, tok::coloncolon, tok::r_paren))826 return false;827 828 while (true) {829 // Read the [id] if present.830 if (Tok.is(tok::l_square)) {831 BalancedDelimiterTracker T(*this, tok::l_square);832 T.consumeOpen();833 834 if (Tok.isNot(tok::identifier)) {835 Diag(Tok, diag::err_expected) << tok::identifier;836 SkipUntil(tok::r_paren, StopAtSemi);837 return true;838 }839 840 IdentifierInfo *II = Tok.getIdentifierInfo();841 ConsumeToken();842 843 Names.push_back(II);844 T.consumeClose();845 } else846 Names.push_back(nullptr);847 848 ExprResult Constraint(ParseAsmStringLiteral(/*ForAsmLabel*/ false));849 if (Constraint.isInvalid()) {850 SkipUntil(tok::r_paren, StopAtSemi);851 return true;852 }853 Constraints.push_back(Constraint.get());854 855 if (Tok.isNot(tok::l_paren)) {856 Diag(Tok, diag::err_expected_lparen_after) << "asm operand";857 SkipUntil(tok::r_paren, StopAtSemi);858 return true;859 }860 861 // Read the parenthesized expression.862 BalancedDelimiterTracker T(*this, tok::l_paren);863 T.consumeOpen();864 ExprResult Res = ParseExpression();865 T.consumeClose();866 if (Res.isInvalid()) {867 SkipUntil(tok::r_paren, StopAtSemi);868 return true;869 }870 Exprs.push_back(Res.get());871 // Eat the comma and continue parsing if it exists.872 if (!TryConsumeToken(tok::comma))873 return false;874 }875}876 877const char *Parser::GNUAsmQualifiers::getQualifierName(AQ Qualifier) {878 switch (Qualifier) {879 case AQ_volatile: return "volatile";880 case AQ_inline: return "inline";881 case AQ_goto: return "goto";882 case AQ_unspecified: return "unspecified";883 }884 llvm_unreachable("Unknown GNUAsmQualifier");885}886 887Parser::GNUAsmQualifiers::AQ888Parser::getGNUAsmQualifier(const Token &Tok) const {889 switch (Tok.getKind()) {890 case tok::kw_volatile: return GNUAsmQualifiers::AQ_volatile;891 case tok::kw_inline: return GNUAsmQualifiers::AQ_inline;892 case tok::kw_goto: return GNUAsmQualifiers::AQ_goto;893 default: return GNUAsmQualifiers::AQ_unspecified;894 }895}896bool Parser::GNUAsmQualifiers::setAsmQualifier(AQ Qualifier) {897 bool IsDuplicate = Qualifiers & Qualifier;898 Qualifiers |= Qualifier;899 return IsDuplicate;900}901