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" 34 using namespace clang; 35 36 namespace { 37 class 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 48 public: 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 74 private: 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 86 void 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 127 StringRef 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 137 void 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 166 SourceLocation 167 ClangAsmParserCallback::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 193 void 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. 200 ExprResult 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. 297 static 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. 354 static 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. 374 static 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 /// 394 StmtResult 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 // FIXME: init MCOptions from sanitizer flags here. 586 llvm::MCTargetOptions MCOptions; 587 std::unique_ptr<llvm::MCAsmInfo> MAI( 588 TheTarget->createMCAsmInfo(*MRI, TT, MCOptions)); 589 // Get the instruction descriptor. 590 std::unique_ptr<llvm::MCInstrInfo> MII(TheTarget->createMCInstrInfo()); 591 std::unique_ptr<llvm::MCObjectFileInfo> MOFI(new llvm::MCObjectFileInfo()); 592 std::unique_ptr<llvm::MCSubtargetInfo> STI( 593 TheTarget->createMCSubtargetInfo(TT, TO.CPU, FeaturesStr)); 594 595 llvm::SourceMgr TempSrcMgr; 596 llvm::MCContext Ctx(MAI.get(), MRI.get(), MOFI.get(), &TempSrcMgr); 597 MOFI->InitMCObjectFileInfo(TheTriple, /*PIC*/ false, Ctx); 598 std::unique_ptr<llvm::MemoryBuffer> Buffer = 599 llvm::MemoryBuffer::getMemBuffer(AsmString, "<MS inline asm>"); 600 601 // Tell SrcMgr about this buffer, which is what the parser will pick up. 602 TempSrcMgr.AddNewSourceBuffer(std::move(Buffer), llvm::SMLoc()); 603 604 std::unique_ptr<llvm::MCStreamer> Str(createNullStreamer(Ctx)); 605 std::unique_ptr<llvm::MCAsmParser> Parser( 606 createMCAsmParser(TempSrcMgr, Ctx, *Str.get(), *MAI)); 607 608 std::unique_ptr<llvm::MCTargetAsmParser> TargetParser( 609 TheTarget->createMCAsmParser(*STI, *Parser, *MII, MCOptions)); 610 611 std::unique_ptr<llvm::MCInstPrinter> IP( 612 TheTarget->createMCInstPrinter(llvm::Triple(TT), 1, *MAI, *MII, *MRI)); 613 614 // Change to the Intel dialect. 615 Parser->setAssemblerDialect(1); 616 Parser->setTargetParser(*TargetParser.get()); 617 Parser->setParsingInlineAsm(true); 618 TargetParser->setParsingInlineAsm(true); 619 620 ClangAsmParserCallback Callback(*this, AsmLoc, AsmString, AsmToks, 621 TokOffsets); 622 TargetParser->setSemaCallback(&Callback); 623 TempSrcMgr.setDiagHandler(ClangAsmParserCallback::DiagHandlerCallback, 624 &Callback); 625 626 unsigned NumOutputs; 627 unsigned NumInputs; 628 std::string AsmStringIR; 629 SmallVector<std::pair<void *, bool>, 4> OpExprs; 630 SmallVector<std::string, 4> Constraints; 631 SmallVector<std::string, 4> Clobbers; 632 if (Parser->parseMSInlineAsm(AsmLoc.getPtrEncoding(), AsmStringIR, NumOutputs, 633 NumInputs, OpExprs, Constraints, Clobbers, 634 MII.get(), IP.get(), Callback)) 635 return StmtError(); 636 637 // Filter out "fpsw" and "mxcsr". They aren't valid GCC asm clobber 638 // constraints. Clang always adds fpsr to the clobber list anyway. 639 llvm::erase_if(Clobbers, [](const std::string &C) { 640 return C == "fpsr" || C == "mxcsr"; 641 }); 642 643 // Build the vector of clobber StringRefs. 644 ClobberRefs.insert(ClobberRefs.end(), Clobbers.begin(), Clobbers.end()); 645 646 // Recast the void pointers and build the vector of constraint StringRefs. 647 unsigned NumExprs = NumOutputs + NumInputs; 648 ConstraintRefs.resize(NumExprs); 649 Exprs.resize(NumExprs); 650 for (unsigned i = 0, e = NumExprs; i != e; ++i) { 651 Expr *OpExpr = static_cast<Expr *>(OpExprs[i].first); 652 if (!OpExpr) 653 return StmtError(); 654 655 // Need address of variable. 656 if (OpExprs[i].second) 657 OpExpr = 658 Actions.BuildUnaryOp(getCurScope(), AsmLoc, UO_AddrOf, OpExpr).get(); 659 660 ConstraintRefs[i] = StringRef(Constraints[i]); 661 Exprs[i] = OpExpr; 662 } 663 664 // FIXME: We should be passing source locations for better diagnostics. 665 return Actions.ActOnMSAsmStmt(AsmLoc, LBraceLocs[0], AsmToks, AsmStringIR, 666 NumOutputs, NumInputs, ConstraintRefs, 667 ClobberRefs, Exprs, EndLoc); 668 } 669 670 /// ParseAsmStatement - Parse a GNU extended asm statement. 671 /// asm-statement: 672 /// gnu-asm-statement 673 /// ms-asm-statement 674 /// 675 /// [GNU] gnu-asm-statement: 676 /// 'asm' type-qualifier[opt] '(' asm-argument ')' ';' 677 /// 678 /// [GNU] asm-argument: 679 /// asm-string-literal 680 /// asm-string-literal ':' asm-operands[opt] 681 /// asm-string-literal ':' asm-operands[opt] ':' asm-operands[opt] 682 /// asm-string-literal ':' asm-operands[opt] ':' asm-operands[opt] 683 /// ':' asm-clobbers 684 /// 685 /// [GNU] asm-clobbers: 686 /// asm-string-literal 687 /// asm-clobbers ',' asm-string-literal 688 /// 689 StmtResult Parser::ParseAsmStatement(bool &msAsm) { 690 assert(Tok.is(tok::kw_asm) && "Not an asm stmt"); 691 SourceLocation AsmLoc = ConsumeToken(); 692 693 if (getLangOpts().AsmBlocks && !isGCCAsmStatement(Tok)) { 694 msAsm = true; 695 return ParseMicrosoftAsmStatement(AsmLoc); 696 } 697 698 DeclSpec DS(AttrFactory); 699 SourceLocation Loc = Tok.getLocation(); 700 ParseTypeQualifierListOpt(DS, AR_VendorAttributesParsed); 701 702 // GNU asms accept, but warn, about type-qualifiers other than volatile. 703 if (DS.getTypeQualifiers() & DeclSpec::TQ_const) 704 Diag(Loc, diag::warn_asm_qualifier_ignored) << "const"; 705 if (DS.getTypeQualifiers() & DeclSpec::TQ_restrict) 706 Diag(Loc, diag::warn_asm_qualifier_ignored) << "restrict"; 707 // FIXME: Once GCC supports _Atomic, check whether it permits it here. 708 if (DS.getTypeQualifiers() & DeclSpec::TQ_atomic) 709 Diag(Loc, diag::warn_asm_qualifier_ignored) << "_Atomic"; 710 711 // Remember if this was a volatile asm. 712 bool isVolatile = DS.getTypeQualifiers() & DeclSpec::TQ_volatile; 713 // Remember if this was a goto asm. 714 bool isGotoAsm = false; 715 716 if (Tok.is(tok::kw_goto)) { 717 isGotoAsm = true; 718 ConsumeToken(); 719 } 720 721 if (Tok.isNot(tok::l_paren)) { 722 Diag(Tok, diag::err_expected_lparen_after) << "asm"; 723 SkipUntil(tok::r_paren, StopAtSemi); 724 return StmtError(); 725 } 726 BalancedDelimiterTracker T(*this, tok::l_paren); 727 T.consumeOpen(); 728 729 ExprResult AsmString(ParseAsmStringLiteral()); 730 731 // Check if GNU-style InlineAsm is disabled. 732 // Error on anything other than empty string. 733 if (!(getLangOpts().GNUAsm || AsmString.isInvalid())) { 734 const auto *SL = cast<StringLiteral>(AsmString.get()); 735 if (!SL->getString().trim().empty()) 736 Diag(Loc, diag::err_gnu_inline_asm_disabled); 737 } 738 739 if (AsmString.isInvalid()) { 740 // Consume up to and including the closing paren. 741 T.skipToEnd(); 742 return StmtError(); 743 } 744 745 SmallVector<IdentifierInfo *, 4> Names; 746 ExprVector Constraints; 747 ExprVector Exprs; 748 ExprVector Clobbers; 749 750 if (Tok.is(tok::r_paren)) { 751 // We have a simple asm expression like 'asm("foo")'. 752 T.consumeClose(); 753 return Actions.ActOnGCCAsmStmt(AsmLoc, /*isSimple*/ true, isVolatile, 754 /*NumOutputs*/ 0, /*NumInputs*/ 0, nullptr, 755 Constraints, Exprs, AsmString.get(), 756 Clobbers, /*NumLabels*/ 0, 757 T.getCloseLocation()); 758 } 759 760 // Parse Outputs, if present. 761 bool AteExtraColon = false; 762 if (Tok.is(tok::colon) || Tok.is(tok::coloncolon)) { 763 // In C++ mode, parse "::" like ": :". 764 AteExtraColon = Tok.is(tok::coloncolon); 765 ConsumeToken(); 766 767 if (!AteExtraColon && isGotoAsm && Tok.isNot(tok::colon)) { 768 Diag(Tok, diag::err_asm_goto_cannot_have_output); 769 SkipUntil(tok::r_paren, StopAtSemi); 770 return StmtError(); 771 } 772 773 if (!AteExtraColon && ParseAsmOperandsOpt(Names, Constraints, Exprs)) 774 return StmtError(); 775 } 776 777 unsigned NumOutputs = Names.size(); 778 779 // Parse Inputs, if present. 780 if (AteExtraColon || Tok.is(tok::colon) || Tok.is(tok::coloncolon)) { 781 // In C++ mode, parse "::" like ": :". 782 if (AteExtraColon) 783 AteExtraColon = false; 784 else { 785 AteExtraColon = Tok.is(tok::coloncolon); 786 ConsumeToken(); 787 } 788 789 if (!AteExtraColon && ParseAsmOperandsOpt(Names, Constraints, Exprs)) 790 return StmtError(); 791 } 792 793 assert(Names.size() == Constraints.size() && 794 Constraints.size() == Exprs.size() && "Input operand size mismatch!"); 795 796 unsigned NumInputs = Names.size() - NumOutputs; 797 798 // Parse the clobbers, if present. 799 if (AteExtraColon || Tok.is(tok::colon) || Tok.is(tok::coloncolon)) { 800 if (AteExtraColon) 801 AteExtraColon = false; 802 else { 803 AteExtraColon = Tok.is(tok::coloncolon); 804 ConsumeToken(); 805 } 806 // Parse the asm-string list for clobbers if present. 807 if (!AteExtraColon && isTokenStringLiteral()) { 808 while (1) { 809 ExprResult Clobber(ParseAsmStringLiteral()); 810 811 if (Clobber.isInvalid()) 812 break; 813 814 Clobbers.push_back(Clobber.get()); 815 816 if (!TryConsumeToken(tok::comma)) 817 break; 818 } 819 } 820 } 821 if (!isGotoAsm && (Tok.isNot(tok::r_paren) || AteExtraColon)) { 822 Diag(Tok, diag::err_expected) << tok::r_paren; 823 SkipUntil(tok::r_paren, StopAtSemi); 824 return StmtError(); 825 } 826 827 // Parse the goto label, if present. 828 unsigned NumLabels = 0; 829 if (AteExtraColon || Tok.is(tok::colon)) { 830 if (!AteExtraColon) 831 ConsumeToken(); 832 833 while (true) { 834 if (Tok.isNot(tok::identifier)) { 835 Diag(Tok, diag::err_expected) << tok::identifier; 836 SkipUntil(tok::r_paren, StopAtSemi); 837 return StmtError(); 838 } 839 LabelDecl *LD = Actions.LookupOrCreateLabel(Tok.getIdentifierInfo(), 840 Tok.getLocation()); 841 Names.push_back(Tok.getIdentifierInfo()); 842 if (!LD) { 843 SkipUntil(tok::r_paren, StopAtSemi); 844 return StmtError(); 845 } 846 ExprResult Res = 847 Actions.ActOnAddrLabel(Tok.getLocation(), Tok.getLocation(), LD); 848 Exprs.push_back(Res.get()); 849 NumLabels++; 850 ConsumeToken(); 851 if (!TryConsumeToken(tok::comma)) 852 break; 853 } 854 } else if (isGotoAsm) { 855 Diag(Tok, diag::err_expected) << tok::colon; 856 SkipUntil(tok::r_paren, StopAtSemi); 857 return StmtError(); 858 } 859 T.consumeClose(); 860 return Actions.ActOnGCCAsmStmt( 861 AsmLoc, false, isVolatile, NumOutputs, NumInputs, Names.data(), 862 Constraints, Exprs, AsmString.get(), Clobbers, NumLabels, 863 T.getCloseLocation()); 864 } 865 866 /// ParseAsmOperands - Parse the asm-operands production as used by 867 /// asm-statement, assuming the leading ':' token was eaten. 868 /// 869 /// [GNU] asm-operands: 870 /// asm-operand 871 /// asm-operands ',' asm-operand 872 /// 873 /// [GNU] asm-operand: 874 /// asm-string-literal '(' expression ')' 875 /// '[' identifier ']' asm-string-literal '(' expression ')' 876 /// 877 // 878 // FIXME: Avoid unnecessary std::string trashing. 879 bool Parser::ParseAsmOperandsOpt(SmallVectorImpl<IdentifierInfo *> &Names, 880 SmallVectorImpl<Expr *> &Constraints, 881 SmallVectorImpl<Expr *> &Exprs) { 882 // 'asm-operands' isn't present? 883 if (!isTokenStringLiteral() && Tok.isNot(tok::l_square)) 884 return false; 885 886 while (1) { 887 // Read the [id] if present. 888 if (Tok.is(tok::l_square)) { 889 BalancedDelimiterTracker T(*this, tok::l_square); 890 T.consumeOpen(); 891 892 if (Tok.isNot(tok::identifier)) { 893 Diag(Tok, diag::err_expected) << tok::identifier; 894 SkipUntil(tok::r_paren, StopAtSemi); 895 return true; 896 } 897 898 IdentifierInfo *II = Tok.getIdentifierInfo(); 899 ConsumeToken(); 900 901 Names.push_back(II); 902 T.consumeClose(); 903 } else 904 Names.push_back(nullptr); 905 906 ExprResult Constraint(ParseAsmStringLiteral()); 907 if (Constraint.isInvalid()) { 908 SkipUntil(tok::r_paren, StopAtSemi); 909 return true; 910 } 911 Constraints.push_back(Constraint.get()); 912 913 if (Tok.isNot(tok::l_paren)) { 914 Diag(Tok, diag::err_expected_lparen_after) << "asm operand"; 915 SkipUntil(tok::r_paren, StopAtSemi); 916 return true; 917 } 918 919 // Read the parenthesized expression. 920 BalancedDelimiterTracker T(*this, tok::l_paren); 921 T.consumeOpen(); 922 ExprResult Res = Actions.CorrectDelayedTyposInExpr(ParseExpression()); 923 T.consumeClose(); 924 if (Res.isInvalid()) { 925 SkipUntil(tok::r_paren, StopAtSemi); 926 return true; 927 } 928 Exprs.push_back(Res.get()); 929 // Eat the comma and continue parsing if it exists. 930 if (!TryConsumeToken(tok::comma)) 931 return false; 932 } 933 } 934