xref: /freebsd/contrib/llvm-project/clang/lib/Parse/ParseStmtAsm.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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"
32 using namespace clang;
33 
34 namespace {
35 class 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 
46 public:
ClangAsmParserCallback(Parser & P,SourceLocation Loc,StringRef AsmString,ArrayRef<Token> Toks,ArrayRef<unsigned> Offsets)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 
LookupInlineAsmField(StringRef Base,StringRef Member,unsigned & Offset)62   bool LookupInlineAsmField(StringRef Base, StringRef Member,
63                             unsigned &Offset) override {
64     return TheParser.getActions().LookupInlineAsmField(Base, Member, Offset,
65                                                        AsmLoc);
66   }
67 
DiagHandlerCallback(const llvm::SMDiagnostic & D,void * Context)68   static void DiagHandlerCallback(const llvm::SMDiagnostic &D, void *Context) {
69     ((ClangAsmParserCallback *)Context)->handleDiagnostic(D);
70   }
71 
72 private:
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 
LookupInlineAsmIdentifier(StringRef & LineBuf,llvm::InlineAsmIdentifierInfo & Info,bool IsUnevaluatedContext)84 void ClangAsmParserCallback::LookupInlineAsmIdentifier(
85     StringRef &LineBuf, llvm::InlineAsmIdentifierInfo &Info,
86     bool IsUnevaluatedContext) {
87   // Collect the desired tokens.
88   SmallVector<Token, 16> LineToks;
89   const Token *FirstOrigToken = nullptr;
90   findTokensForString(LineBuf, LineToks, FirstOrigToken);
91 
92   unsigned NumConsumedToks;
93   ExprResult Result = TheParser.ParseMSAsmIdentifier(LineToks, NumConsumedToks,
94                                                      IsUnevaluatedContext);
95 
96   // If we consumed the entire line, tell MC that.
97   // Also do this if we consumed nothing as a way of reporting failure.
98   if (NumConsumedToks == 0 || NumConsumedToks == LineToks.size()) {
99     // By not modifying LineBuf, we're implicitly consuming it all.
100 
101     // Otherwise, consume up to the original tokens.
102   } else {
103     assert(FirstOrigToken && "not using original tokens?");
104 
105     // Since we're using original tokens, apply that offset.
106     assert(FirstOrigToken[NumConsumedToks].getLocation() ==
107            LineToks[NumConsumedToks].getLocation());
108     unsigned FirstIndex = FirstOrigToken - AsmToks.begin();
109     unsigned LastIndex = FirstIndex + NumConsumedToks - 1;
110 
111     // The total length we've consumed is the relative 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(0, TotalOffset);
117   }
118 
119   // Initialize Info with the lookup result.
120   if (!Result.isUsable())
121     return;
122   TheParser.getActions().FillInlineAsmIdentifierInfo(Result.get(), Info);
123 }
124 
LookupInlineAsmLabel(StringRef Identifier,llvm::SourceMgr & LSM,llvm::SMLoc Location,bool Create)125 StringRef ClangAsmParserCallback::LookupInlineAsmLabel(StringRef Identifier,
126                                                        llvm::SourceMgr &LSM,
127                                                        llvm::SMLoc Location,
128                                                        bool Create) {
129   SourceLocation Loc = translateLocation(LSM, Location);
130   LabelDecl *Label =
131       TheParser.getActions().GetOrCreateMSAsmLabel(Identifier, Loc, Create);
132   return Label->getMSAsmLabel();
133 }
134 
findTokensForString(StringRef Str,SmallVectorImpl<Token> & TempToks,const Token * & FirstOrigToken) const135 void 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(AsmTokOffsets, 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(AsmToks[i]);
161   }
162 }
163 
164 SourceLocation
translateLocation(const llvm::SourceMgr & LSM,llvm::SMLoc SMLoc)165 ClangAsmParserCallback::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(LSM.FindBufferContainingLoc(SMLoc));
172   unsigned Offset = SMLoc.getPointer() - LBuf->getBufferStart();
173 
174   // Figure out which token that offset points into.
175   const unsigned *TokOffsetPtr = llvm::lower_bound(AsmTokOffsets, Offset);
176   unsigned TokIndex = TokOffsetPtr - AsmTokOffsets.begin();
177   unsigned TokOffset = *TokOffsetPtr;
178 
179   // If we come up with an answer which seems sane, use it; otherwise,
180   // just point at the __asm keyword.
181   // FIXME: Assert the answer is sane once we handle .macro correctly.
182   SourceLocation Loc = AsmLoc;
183   if (TokIndex < AsmToks.size()) {
184     const Token &Tok = AsmToks[TokIndex];
185     Loc = Tok.getLocation();
186     Loc = Loc.getLocWithOffset(Offset - TokOffset);
187   }
188   return Loc;
189 }
190 
handleDiagnostic(const llvm::SMDiagnostic & D)191 void ClangAsmParserCallback::handleDiagnostic(const llvm::SMDiagnostic &D) {
192   const llvm::SourceMgr &LSM = *D.getSourceMgr();
193   SourceLocation Loc = translateLocation(LSM, D.getLoc());
194   TheParser.Diag(Loc, diag::err_inline_ms_asm_parsing) << D.getMessage();
195 }
196 
ParseMSAsmIdentifier(llvm::SmallVectorImpl<Token> & LineToks,unsigned & NumLineToksConsumed,bool IsUnevaluatedContext)197 ExprResult 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(EndOfStreamTok);
208 
209   // Also copy the current token over.
210   LineToks.push_back(Tok);
211 
212   PP.EnterTokenStream(LineToks, /*DisableMacroExpansions*/ true,
213                       /*IsReinject*/ true);
214 
215   // Clear the current token and advance to the first token in LineToks.
216   ConsumeAnyToken();
217 
218   // Parse an optional scope-specifier if we're in C++.
219   CXXScopeSpec SS;
220   if (getLangOpts().CPlusPlus)
221     ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
222                                    /*ObjectHasErrors=*/false,
223                                    /*EnteringContext=*/false);
224 
225   // Require an identifier here.
226   SourceLocation TemplateKWLoc;
227   UnqualifiedId Id;
228   bool Invalid = true;
229   ExprResult Result;
230   if (Tok.is(tok::kw_this)) {
231     Result = ParseCXXThis();
232     Invalid = false;
233   } else {
234     Invalid =
235         ParseUnqualifiedId(SS, /*ObjectType=*/nullptr,
236                            /*ObjectHadErrors=*/false,
237                            /*EnteringContext=*/false,
238                            /*AllowDestructorName=*/false,
239                            /*AllowConstructorName=*/false,
240                            /*AllowDeductionGuide=*/false, &TemplateKWLoc, Id);
241     // Perform the lookup.
242     Result = Actions.LookupInlineAsmIdentifier(SS, TemplateKWLoc, Id,
243                                                IsUnevaluatedContext);
244   }
245   // While the next two tokens are 'period' 'identifier', repeatedly parse it as
246   // a field access. We have to avoid consuming assembler directives that look
247   // like '.' 'else'.
248   while (Result.isUsable() && Tok.is(tok::period)) {
249     Token IdTok = PP.LookAhead(0);
250     if (IdTok.isNot(tok::identifier))
251       break;
252     ConsumeToken(); // Consume the period.
253     IdentifierInfo *Id = Tok.getIdentifierInfo();
254     ConsumeToken(); // Consume the identifier.
255     Result = Actions.LookupInlineAsmVarDeclField(Result.get(), Id->getName(),
256                                                  Tok.getLocation());
257   }
258 
259   // Figure out how many tokens we are into LineToks.
260   unsigned LineIndex = 0;
261   if (Tok.is(EndOfStream)) {
262     LineIndex = LineToks.size() - 2;
263   } else {
264     while (LineToks[LineIndex].getLocation() != Tok.getLocation()) {
265       LineIndex++;
266       assert(LineIndex < LineToks.size() - 2); // we added two extra 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(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.
buildMSAsmString(Preprocessor & PP,SourceLocation AsmLoc,ArrayRef<Token> AsmToks,SmallVectorImpl<unsigned> & TokOffsets,SmallString<512> & Asm)296 static bool buildMSAsmString(Preprocessor &PP, SourceLocation AsmLoc,
297                              ArrayRef<Token> AsmToks,
298                              SmallVectorImpl<unsigned> &TokOffsets,
299                              SmallString<512> &Asm) {
300   assert(!AsmToks.empty() && "Didn't expect an empty AsmToks!");
301 
302   // Is this the start of a new assembly statement?
303   bool isNewStatement = true;
304 
305   for (unsigned i = 0, e = AsmToks.size(); i < e; ++i) {
306     const Token &Tok = AsmToks[i];
307 
308     // Start each new statement with a newline and a tab.
309     if (!isNewStatement && (Tok.is(tok::kw_asm) || Tok.isAtStartOfLine())) {
310       Asm += "\n\t";
311       isNewStatement = true;
312     }
313 
314     // Preserve the existence of leading whitespace except at 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(Asm.size());
321 
322     // Don't actually write '__asm' into the assembly stream.
323     if (Tok.is(tok::kw_asm)) {
324       // Complain about __asm at the end of the stream.
325       if (i + 1 == e) {
326         PP.Diag(AsmLoc, diag::err_asm_empty);
327         return true;
328       }
329 
330       continue;
331     }
332 
333     // Append the spelling of the token.
334     SmallString<32> SpellingBuffer;
335     bool SpellingInvalid = false;
336     Asm += PP.getSpelling(Tok, SpellingBuffer, &SpellingInvalid);
337     assert(!SpellingInvalid && "spelling was invalid after correct parse?");
338 
339     // We are no longer at the start of a statement.
340     isNewStatement = false;
341   }
342 
343   // Ensure that the buffer is null-terminated.
344   Asm.push_back('\0');
345   Asm.pop_back();
346 
347   assert(TokOffsets.size() == AsmToks.size());
348   return false;
349 }
350 
isGCCAsmStatement(const Token & TokAfterAsm) const351 bool Parser::isGCCAsmStatement(const Token &TokAfterAsm) const {
352   return TokAfterAsm.is(tok::l_paren) || isGNUAsmQualifier(TokAfterAsm);
353 }
354 
isGNUAsmQualifier(const Token & TokAfterAsm) const355 bool Parser::isGNUAsmQualifier(const Token &TokAfterAsm) const {
356   return getGNUAsmQualifier(TokAfterAsm) != GNUAsmQualifiers::AQ_unspecified;
357 }
358 
ParseMicrosoftAsmStatement(SourceLocation AsmLoc)359 StmtResult Parser::ParseMicrosoftAsmStatement(SourceLocation AsmLoc) {
360   SourceManager &SrcMgr = PP.getSourceManager();
361   SourceLocation EndLoc = AsmLoc;
362   SmallVector<Token, 4> AsmToks;
363 
364   bool SingleLineMode = true;
365   unsigned BraceNesting = 0;
366   unsigned short savedBraceCount = BraceCount;
367   bool InAsmComment = false;
368   FileID FID;
369   unsigned LineNo = 0;
370   unsigned NumTokensRead = 0;
371   SmallVector<SourceLocation, 4> LBraceLocs;
372   bool SkippedStartOfLine = false;
373 
374   if (Tok.is(tok::l_brace)) {
375     // Braced inline asm: consume the opening brace.
376     SingleLineMode = false;
377     BraceNesting = 1;
378     EndLoc = ConsumeBrace();
379     LBraceLocs.push_back(EndLoc);
380     ++NumTokensRead;
381   } else {
382     // Single-line inline asm; compute which line it is on.
383     FileIDAndOffset ExpAsmLoc = SrcMgr.getDecomposedExpansionLoc(EndLoc);
384     FID = ExpAsmLoc.first;
385     LineNo = SrcMgr.getLineNumber(FID, ExpAsmLoc.second);
386     LBraceLocs.push_back(SourceLocation());
387   }
388 
389   SourceLocation TokLoc = Tok.getLocation();
390   do {
391     // If we hit EOF, we're done, period.
392     if (isEofOrEom())
393       break;
394 
395     if (!InAsmComment && Tok.is(tok::l_brace)) {
396       // Consume the opening brace.
397       SkippedStartOfLine = Tok.isAtStartOfLine();
398       AsmToks.push_back(Tok);
399       EndLoc = ConsumeBrace();
400       BraceNesting++;
401       LBraceLocs.push_back(EndLoc);
402       TokLoc = Tok.getLocation();
403       ++NumTokensRead;
404       continue;
405     } else if (!InAsmComment && Tok.is(tok::semi)) {
406       // A semicolon in an asm is the start of a comment.
407       InAsmComment = true;
408       if (!SingleLineMode) {
409         // Compute which line the comment is on.
410         FileIDAndOffset ExpSemiLoc = SrcMgr.getDecomposedExpansionLoc(TokLoc);
411         FID = ExpSemiLoc.first;
412         LineNo = SrcMgr.getLineNumber(FID, ExpSemiLoc.second);
413       }
414     } else if (SingleLineMode || InAsmComment) {
415       // If end-of-line is significant, check whether this token is on a
416       // new line.
417       FileIDAndOffset ExpLoc = SrcMgr.getDecomposedExpansionLoc(TokLoc);
418       if (ExpLoc.first != FID ||
419           SrcMgr.getLineNumber(ExpLoc.first, ExpLoc.second) != LineNo) {
420         // If this is a single-line __asm, we're done, except if the 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(tok::kw_asm);
425         if (SingleLineMode && (!isAsm || isGCCAsmStatement(NextToken())))
426           break;
427         // We're no longer in a comment.
428         InAsmComment = false;
429         if (isAsm) {
430           // If this is a new __asm {} block we want to process it separately
431           // from the single-line __asm statements
432           if (PP.LookAhead(0).is(tok::l_brace))
433             break;
434           LineNo = SrcMgr.getLineNumber(ExpLoc.first, ExpLoc.second);
435           SkippedStartOfLine = Tok.isAtStartOfLine();
436         } else if (Tok.is(tok::semi)) {
437           // A multi-line asm-statement, where next line is a comment
438           InAsmComment = true;
439           FID = ExpLoc.first;
440           LineNo = SrcMgr.getLineNumber(FID, ExpLoc.second);
441         }
442       } else if (!InAsmComment && Tok.is(tok::r_brace)) {
443         // In MSVC mode, braces only participate in brace matching 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(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(Token::LeadingSpace);
457         AsmToks.push_back(Tok);
458       }
459       EndLoc = ConsumeBrace();
460       BraceNesting--;
461       // Finish if all of the opened braces in the inline asm section 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(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(Tok);
484       ConsumeAnyToken();
485     }
486     TokLoc = Tok.getLocation();
487     ++NumTokensRead;
488     SkippedStartOfLine = false;
489   } while (true);
490 
491   if (BraceNesting && BraceCount != savedBraceCount) {
492     // __asm without closing brace (this can happen at EOF).
493     for (unsigned i = 0; i < BraceNesting; ++i) {
494       Diag(Tok, diag::err_expected) << tok::r_brace;
495       Diag(LBraceLocs.back(), diag::note_matching) << tok::l_brace;
496       LBraceLocs.pop_back();
497     }
498     return StmtError();
499   } else if (NumTokensRead == 0) {
500     // Empty __asm.
501     Diag(Tok, diag::err_expected) << tok::l_brace;
502     return StmtError();
503   }
504 
505   // Okay, prepare to use MC to parse the assembly.
506   SmallVector<StringRef, 4> ConstraintRefs;
507   SmallVector<Expr *, 4> Exprs;
508   SmallVector<StringRef, 4> ClobberRefs;
509 
510   // We need an actual supported target.
511   const llvm::Triple &TheTriple = Actions.Context.getTargetInfo().getTriple();
512   const std::string &TT = TheTriple.getTriple();
513   const llvm::Target *TheTarget = nullptr;
514   if (!TheTriple.isX86()) {
515     Diag(AsmLoc, diag::err_msasm_unsupported_arch) << TheTriple.getArchName();
516   } else {
517     std::string Error;
518     TheTarget = llvm::TargetRegistry::lookupTarget(TT, Error);
519     if (!TheTarget)
520       Diag(AsmLoc, 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, LBraceLocs[0], AsmToks, AsmString,
528                                   /*NumOutputs*/ 0, /*NumInputs*/ 0,
529                                   ConstraintRefs, 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, AsmString))
540     return StmtError();
541 
542   const TargetOptions &TO = Actions.Context.getTargetInfo().getTargetOpts();
543   std::string FeaturesStr =
544       llvm::join(TO.Features.begin(), TO.Features.end(), ",");
545 
546   std::unique_ptr<llvm::MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TT));
547   if (!MRI) {
548     Diag(AsmLoc, diag::err_msasm_unable_to_create_target)
549         << "target MC unavailable";
550     return EmptyStmt();
551   }
552   // FIXME: init MCOptions from sanitizer flags here.
553   llvm::MCTargetOptions MCOptions;
554   std::unique_ptr<llvm::MCAsmInfo> MAI(
555       TheTarget->createMCAsmInfo(*MRI, TT, 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(TT, TO.CPU, FeaturesStr));
560   // Target MCTargetDesc may not be linked in clang-based tools.
561 
562   if (!MAI || !MII || !STI) {
563     Diag(AsmLoc, diag::err_msasm_unable_to_create_target)
564         << "target MC unavailable";
565     return EmptyStmt();
566   }
567 
568   llvm::SourceMgr TempSrcMgr;
569   llvm::MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &TempSrcMgr);
570   std::unique_ptr<llvm::MCObjectFileInfo> MOFI(
571       TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false));
572   Ctx.setObjectFileInfo(MOFI.get());
573 
574   std::unique_ptr<llvm::MemoryBuffer> Buffer =
575       llvm::MemoryBuffer::getMemBuffer(AsmString, "<MS inline asm>");
576 
577   // Tell SrcMgr about this buffer, which is what the parser will pick up.
578   TempSrcMgr.AddNewSourceBuffer(std::move(Buffer), llvm::SMLoc());
579 
580   std::unique_ptr<llvm::MCStreamer> Str(createNullStreamer(Ctx));
581   std::unique_ptr<llvm::MCAsmParser> Parser(
582       createMCAsmParser(TempSrcMgr, Ctx, *Str, *MAI));
583 
584   std::unique_ptr<llvm::MCTargetAsmParser> TargetParser(
585       TheTarget->createMCAsmParser(*STI, *Parser, *MII, MCOptions));
586   // Target AsmParser may not be linked in clang-based tools.
587   if (!TargetParser) {
588     Diag(AsmLoc, diag::err_msasm_unable_to_create_target)
589         << "target ASM parser unavailable";
590     return EmptyStmt();
591   }
592 
593   std::unique_ptr<llvm::MCInstPrinter> IP(
594       TheTarget->createMCInstPrinter(llvm::Triple(TT), 1, *MAI, *MII, *MRI));
595 
596   // Change to the Intel dialect.
597   Parser->setAssemblerDialect(1);
598   Parser->setTargetParser(*TargetParser);
599   Parser->setParsingMSInlineAsm(true);
600   TargetParser->setParsingMSInlineAsm(true);
601 
602   ClangAsmParserCallback Callback(*this, AsmLoc, AsmString, AsmToks,
603                                   TokOffsets);
604   TargetParser->setSemaCallback(&Callback);
605   TempSrcMgr.setDiagHandler(ClangAsmParserCallback::DiagHandlerCallback,
606                             &Callback);
607 
608   unsigned NumOutputs;
609   unsigned NumInputs;
610   std::string AsmStringIR;
611   SmallVector<std::pair<void *, bool>, 4> OpExprs;
612   SmallVector<std::string, 4> Constraints;
613   SmallVector<std::string, 4> Clobbers;
614   if (Parser->parseMSInlineAsm(AsmStringIR, NumOutputs, NumInputs, OpExprs,
615                                Constraints, Clobbers, MII.get(), IP.get(),
616                                Callback))
617     return StmtError();
618 
619   // Filter out "fpsw" and "mxcsr". They aren't valid GCC asm clobber
620   // constraints. Clang always adds fpsr to the clobber list anyway.
621   llvm::erase_if(Clobbers, [](const std::string &C) {
622     return C == "fpsr" || C == "mxcsr";
623   });
624 
625   // Build the vector of clobber StringRefs.
626   llvm::append_range(ClobberRefs, Clobbers);
627 
628   // Recast the void pointers and build the vector of constraint StringRefs.
629   unsigned NumExprs = NumOutputs + NumInputs;
630   ConstraintRefs.resize(NumExprs);
631   Exprs.resize(NumExprs);
632   for (unsigned i = 0, e = NumExprs; i != e; ++i) {
633     Expr *OpExpr = static_cast<Expr *>(OpExprs[i].first);
634     if (!OpExpr)
635       return StmtError();
636 
637     // Need address of variable.
638     if (OpExprs[i].second)
639       OpExpr =
640           Actions.BuildUnaryOp(getCurScope(), AsmLoc, UO_AddrOf, OpExpr).get();
641 
642     ConstraintRefs[i] = StringRef(Constraints[i]);
643     Exprs[i] = OpExpr;
644   }
645 
646   // FIXME: We should be passing source locations for better diagnostics.
647   return Actions.ActOnMSAsmStmt(AsmLoc, LBraceLocs[0], AsmToks, AsmStringIR,
648                                 NumOutputs, NumInputs, ConstraintRefs,
649                                 ClobberRefs, Exprs, EndLoc);
650 }
651 
parseGNUAsmQualifierListOpt(GNUAsmQualifiers & AQ)652 bool Parser::parseGNUAsmQualifierListOpt(GNUAsmQualifiers &AQ) {
653   while (true) {
654     const GNUAsmQualifiers::AQ A = getGNUAsmQualifier(Tok);
655     if (A == GNUAsmQualifiers::AQ_unspecified) {
656       if (Tok.isNot(tok::l_paren)) {
657         Diag(Tok.getLocation(), diag::err_asm_qualifier_ignored);
658         SkipUntil(tok::r_paren, StopAtSemi);
659         return true;
660       }
661       return false;
662     }
663     if (AQ.setAsmQualifier(A))
664       Diag(Tok.getLocation(), diag::err_asm_duplicate_qual)
665           << GNUAsmQualifiers::getQualifierName(A);
666     ConsumeToken();
667   }
668   return false;
669 }
670 
ParseAsmStatement(bool & msAsm)671 StmtResult Parser::ParseAsmStatement(bool &msAsm) {
672   assert(Tok.is(tok::kw_asm) && "Not an asm stmt");
673   SourceLocation AsmLoc = ConsumeToken();
674 
675   if (getLangOpts().AsmBlocks && !isGCCAsmStatement(Tok)) {
676     msAsm = true;
677     return ParseMicrosoftAsmStatement(AsmLoc);
678   }
679 
680   SourceLocation Loc = Tok.getLocation();
681   GNUAsmQualifiers GAQ;
682   if (parseGNUAsmQualifierListOpt(GAQ))
683     return StmtError();
684 
685   if (GAQ.isGoto() && getLangOpts().SpeculativeLoadHardening)
686     Diag(Loc, diag::warn_slh_does_not_support_asm_goto);
687 
688   BalancedDelimiterTracker T(*this, tok::l_paren);
689   T.consumeOpen();
690 
691   ExprResult AsmString(ParseAsmStringLiteral(/*ForAsmLabel*/ false));
692 
693   // Check if GNU-style InlineAsm is disabled.
694   // Error on anything other than empty string.
695   if (!(getLangOpts().GNUAsm || AsmString.isInvalid())) {
696     const auto *SL = cast<StringLiteral>(AsmString.get());
697     if (!SL->getString().trim().empty())
698       Diag(Loc, diag::err_gnu_inline_asm_disabled);
699   }
700 
701   if (AsmString.isInvalid()) {
702     // Consume up to and including the closing paren.
703     T.skipToEnd();
704     return StmtError();
705   }
706 
707   SmallVector<IdentifierInfo *, 4> Names;
708   ExprVector Constraints;
709   ExprVector Exprs;
710   ExprVector Clobbers;
711 
712   if (Tok.is(tok::r_paren)) {
713     // We have a simple asm expression like 'asm("foo")'.
714     T.consumeClose();
715     return Actions.ActOnGCCAsmStmt(
716         AsmLoc, /*isSimple*/ true, GAQ.isVolatile(),
717         /*NumOutputs*/ 0, /*NumInputs*/ 0, nullptr, Constraints, Exprs,
718         AsmString.get(), Clobbers, /*NumLabels*/ 0, T.getCloseLocation());
719   }
720 
721   // Parse Outputs, if present.
722   bool AteExtraColon = false;
723   if (Tok.is(tok::colon) || Tok.is(tok::coloncolon)) {
724     // In C++ mode, parse "::" like ": :".
725     AteExtraColon = Tok.is(tok::coloncolon);
726     ConsumeToken();
727 
728     if (!AteExtraColon && ParseAsmOperandsOpt(Names, Constraints, Exprs))
729       return StmtError();
730   }
731 
732   unsigned NumOutputs = Names.size();
733 
734   // Parse Inputs, if present.
735   if (AteExtraColon || Tok.is(tok::colon) || Tok.is(tok::coloncolon)) {
736     // In C++ mode, parse "::" like ": :".
737     if (AteExtraColon)
738       AteExtraColon = false;
739     else {
740       AteExtraColon = Tok.is(tok::coloncolon);
741       ConsumeToken();
742     }
743 
744     if (!AteExtraColon && ParseAsmOperandsOpt(Names, Constraints, Exprs))
745       return StmtError();
746   }
747 
748   assert(Names.size() == Constraints.size() &&
749          Constraints.size() == Exprs.size() && "Input operand size mismatch!");
750 
751   unsigned NumInputs = Names.size() - NumOutputs;
752 
753   // Parse the clobbers, if present.
754   if (AteExtraColon || Tok.is(tok::colon) || Tok.is(tok::coloncolon)) {
755     if (AteExtraColon)
756       AteExtraColon = false;
757     else {
758       AteExtraColon = Tok.is(tok::coloncolon);
759       ConsumeToken();
760     }
761     // Parse the asm-string list for clobbers if present.
762     if (!AteExtraColon && (isTokenStringLiteral() || Tok.is(tok::l_paren))) {
763       while (true) {
764         ExprResult Clobber(ParseAsmStringLiteral(/*ForAsmLabel*/ false));
765 
766         if (Clobber.isInvalid())
767           break;
768 
769         Clobbers.push_back(Clobber.get());
770 
771         if (!TryConsumeToken(tok::comma))
772           break;
773       }
774     }
775   }
776   if (!GAQ.isGoto() && (Tok.isNot(tok::r_paren) || AteExtraColon)) {
777     Diag(Tok, diag::err_expected) << tok::r_paren;
778     SkipUntil(tok::r_paren, StopAtSemi);
779     return StmtError();
780   }
781 
782   // Parse the goto label, if present.
783   unsigned NumLabels = 0;
784   if (AteExtraColon || Tok.is(tok::colon)) {
785     if (!AteExtraColon)
786       ConsumeToken();
787 
788     while (true) {
789       if (Tok.isNot(tok::identifier)) {
790         Diag(Tok, diag::err_expected) << tok::identifier;
791         SkipUntil(tok::r_paren, StopAtSemi);
792         return StmtError();
793       }
794       LabelDecl *LD = Actions.LookupOrCreateLabel(Tok.getIdentifierInfo(),
795                                                   Tok.getLocation());
796       Names.push_back(Tok.getIdentifierInfo());
797       if (!LD) {
798         SkipUntil(tok::r_paren, StopAtSemi);
799         return StmtError();
800       }
801       ExprResult Res =
802           Actions.ActOnAddrLabel(Tok.getLocation(), Tok.getLocation(), LD);
803       Exprs.push_back(Res.get());
804       NumLabels++;
805       ConsumeToken();
806       if (!TryConsumeToken(tok::comma))
807         break;
808     }
809   } else if (GAQ.isGoto()) {
810     Diag(Tok, diag::err_expected) << tok::colon;
811     SkipUntil(tok::r_paren, StopAtSemi);
812     return StmtError();
813   }
814   T.consumeClose();
815   return Actions.ActOnGCCAsmStmt(AsmLoc, false, GAQ.isVolatile(), NumOutputs,
816                                  NumInputs, Names.data(), Constraints, Exprs,
817                                  AsmString.get(), Clobbers, NumLabels,
818                                  T.getCloseLocation());
819 }
820 
ParseAsmOperandsOpt(SmallVectorImpl<IdentifierInfo * > & Names,SmallVectorImpl<Expr * > & Constraints,SmallVectorImpl<Expr * > & Exprs)821 bool Parser::ParseAsmOperandsOpt(SmallVectorImpl<IdentifierInfo *> &Names,
822                                  SmallVectorImpl<Expr *> &Constraints,
823                                  SmallVectorImpl<Expr *> &Exprs) {
824   // 'asm-operands' isn't present
825   if (Tok.isOneOf(tok::colon, tok::coloncolon, tok::r_paren))
826     return false;
827 
828   while (true) {
829     // Read the [id] if present.
830     if (Tok.is(tok::l_square)) {
831       BalancedDelimiterTracker T(*this, tok::l_square);
832       T.consumeOpen();
833 
834       if (Tok.isNot(tok::identifier)) {
835         Diag(Tok, diag::err_expected) << tok::identifier;
836         SkipUntil(tok::r_paren, StopAtSemi);
837         return true;
838       }
839 
840       IdentifierInfo *II = Tok.getIdentifierInfo();
841       ConsumeToken();
842 
843       Names.push_back(II);
844       T.consumeClose();
845     } else
846       Names.push_back(nullptr);
847 
848     ExprResult Constraint(ParseAsmStringLiteral(/*ForAsmLabel*/ false));
849     if (Constraint.isInvalid()) {
850       SkipUntil(tok::r_paren, StopAtSemi);
851       return true;
852     }
853     Constraints.push_back(Constraint.get());
854 
855     if (Tok.isNot(tok::l_paren)) {
856       Diag(Tok, diag::err_expected_lparen_after) << "asm operand";
857       SkipUntil(tok::r_paren, StopAtSemi);
858       return true;
859     }
860 
861     // Read the parenthesized expression.
862     BalancedDelimiterTracker T(*this, tok::l_paren);
863     T.consumeOpen();
864     ExprResult Res = ParseExpression();
865     T.consumeClose();
866     if (Res.isInvalid()) {
867       SkipUntil(tok::r_paren, StopAtSemi);
868       return true;
869     }
870     Exprs.push_back(Res.get());
871     // Eat the comma and continue parsing if it exists.
872     if (!TryConsumeToken(tok::comma))
873       return false;
874   }
875 }
876 
getQualifierName(AQ Qualifier)877 const 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 
887 Parser::GNUAsmQualifiers::AQ
getGNUAsmQualifier(const Token & Tok) const888 Parser::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 }
setAsmQualifier(AQ Qualifier)896 bool Parser::GNUAsmQualifiers::setAsmQualifier(AQ Qualifier) {
897   bool IsDuplicate = Qualifiers & Qualifier;
898   Qualifiers |= Qualifier;
899   return IsDuplicate;
900 }
901