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