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