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