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