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, 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(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, 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 std::pair<FileID, unsigned> ExpAsmLoc =
384 SrcMgr.getDecomposedExpansionLoc(Loc: EndLoc);
385 FID = ExpAsmLoc.first;
386 LineNo = SrcMgr.getLineNumber(FID, FilePos: ExpAsmLoc.second);
387 LBraceLocs.push_back(Elt: SourceLocation());
388 }
389
390 SourceLocation TokLoc = Tok.getLocation();
391 do {
392 // If we hit EOF, we're done, period.
393 if (isEofOrEom())
394 break;
395
396 if (!InAsmComment && Tok.is(K: tok::l_brace)) {
397 // Consume the opening brace.
398 SkippedStartOfLine = Tok.isAtStartOfLine();
399 AsmToks.push_back(Elt: Tok);
400 EndLoc = ConsumeBrace();
401 BraceNesting++;
402 LBraceLocs.push_back(Elt: EndLoc);
403 TokLoc = Tok.getLocation();
404 ++NumTokensRead;
405 continue;
406 } else if (!InAsmComment && Tok.is(K: tok::semi)) {
407 // A semicolon in an asm is the start of a comment.
408 InAsmComment = true;
409 if (!SingleLineMode) {
410 // Compute which line the comment is on.
411 std::pair<FileID, unsigned> ExpSemiLoc =
412 SrcMgr.getDecomposedExpansionLoc(Loc: TokLoc);
413 FID = ExpSemiLoc.first;
414 LineNo = SrcMgr.getLineNumber(FID, FilePos: ExpSemiLoc.second);
415 }
416 } else if (SingleLineMode || InAsmComment) {
417 // If end-of-line is significant, check whether this token is on a
418 // new line.
419 std::pair<FileID, unsigned> ExpLoc =
420 SrcMgr.getDecomposedExpansionLoc(Loc: TokLoc);
421 if (ExpLoc.first != FID ||
422 SrcMgr.getLineNumber(FID: ExpLoc.first, FilePos: ExpLoc.second) != LineNo) {
423 // If this is a single-line __asm, we're done, except if the next
424 // line is MS-style asm too, in which case we finish a comment
425 // if needed and then keep processing the next line as a single
426 // line __asm.
427 bool isAsm = Tok.is(K: tok::kw_asm);
428 if (SingleLineMode && (!isAsm || isGCCAsmStatement(TokAfterAsm: NextToken())))
429 break;
430 // We're no longer in a comment.
431 InAsmComment = false;
432 if (isAsm) {
433 // If this is a new __asm {} block we want to process it separately
434 // from the single-line __asm statements
435 if (PP.LookAhead(N: 0).is(K: tok::l_brace))
436 break;
437 LineNo = SrcMgr.getLineNumber(FID: ExpLoc.first, FilePos: ExpLoc.second);
438 SkippedStartOfLine = Tok.isAtStartOfLine();
439 } else if (Tok.is(K: tok::semi)) {
440 // A multi-line asm-statement, where next line is a comment
441 InAsmComment = true;
442 FID = ExpLoc.first;
443 LineNo = SrcMgr.getLineNumber(FID, FilePos: ExpLoc.second);
444 }
445 } else if (!InAsmComment && Tok.is(K: tok::r_brace)) {
446 // In MSVC mode, braces only participate in brace matching and
447 // separating the asm statements. This is an intentional
448 // departure from the Apple gcc behavior.
449 if (!BraceNesting)
450 break;
451 }
452 }
453 if (!InAsmComment && BraceNesting && Tok.is(K: tok::r_brace) &&
454 BraceCount == (savedBraceCount + BraceNesting)) {
455 // Consume the closing brace.
456 SkippedStartOfLine = Tok.isAtStartOfLine();
457 // Don't want to add the closing brace of the whole asm block
458 if (SingleLineMode || BraceNesting > 1) {
459 Tok.clearFlag(Flag: Token::LeadingSpace);
460 AsmToks.push_back(Elt: Tok);
461 }
462 EndLoc = ConsumeBrace();
463 BraceNesting--;
464 // Finish if all of the opened braces in the inline asm section were
465 // consumed.
466 if (BraceNesting == 0 && !SingleLineMode)
467 break;
468 else {
469 LBraceLocs.pop_back();
470 TokLoc = Tok.getLocation();
471 ++NumTokensRead;
472 continue;
473 }
474 }
475
476 // Consume the next token; make sure we don't modify the brace count etc.
477 // if we are in a comment.
478 EndLoc = TokLoc;
479 if (InAsmComment)
480 PP.Lex(Result&: Tok);
481 else {
482 // Set the token as the start of line if we skipped the original start
483 // of line token in case it was a nested brace.
484 if (SkippedStartOfLine)
485 Tok.setFlag(Token::StartOfLine);
486 AsmToks.push_back(Elt: Tok);
487 ConsumeAnyToken();
488 }
489 TokLoc = Tok.getLocation();
490 ++NumTokensRead;
491 SkippedStartOfLine = false;
492 } while (true);
493
494 if (BraceNesting && BraceCount != savedBraceCount) {
495 // __asm without closing brace (this can happen at EOF).
496 for (unsigned i = 0; i < BraceNesting; ++i) {
497 Diag(Tok, diag::err_expected) << tok::r_brace;
498 Diag(LBraceLocs.back(), diag::note_matching) << tok::l_brace;
499 LBraceLocs.pop_back();
500 }
501 return StmtError();
502 } else if (NumTokensRead == 0) {
503 // Empty __asm.
504 Diag(Tok, diag::err_expected) << tok::l_brace;
505 return StmtError();
506 }
507
508 // Okay, prepare to use MC to parse the assembly.
509 SmallVector<StringRef, 4> ConstraintRefs;
510 SmallVector<Expr *, 4> Exprs;
511 SmallVector<StringRef, 4> ClobberRefs;
512
513 // We need an actual supported target.
514 const llvm::Triple &TheTriple = Actions.Context.getTargetInfo().getTriple();
515 const std::string &TT = TheTriple.getTriple();
516 const llvm::Target *TheTarget = nullptr;
517 if (!TheTriple.isX86()) {
518 Diag(AsmLoc, diag::err_msasm_unsupported_arch) << TheTriple.getArchName();
519 } else {
520 std::string Error;
521 TheTarget = llvm::TargetRegistry::lookupTarget(TripleStr: TT, Error);
522 if (!TheTarget)
523 Diag(AsmLoc, diag::err_msasm_unable_to_create_target) << Error;
524 }
525
526 assert(!LBraceLocs.empty() && "Should have at least one location here");
527
528 SmallString<512> AsmString;
529 auto EmptyStmt = [&] {
530 return Actions.ActOnMSAsmStmt(AsmLoc, LBraceLoc: LBraceLocs[0], AsmToks, AsmString,
531 /*NumOutputs*/ 0, /*NumInputs*/ 0,
532 Constraints: ConstraintRefs, Clobbers: ClobberRefs, Exprs, EndLoc);
533 };
534 // If we don't support assembly, or the assembly is empty, we don't
535 // need to instantiate the AsmParser, etc.
536 if (!TheTarget || AsmToks.empty()) {
537 return EmptyStmt();
538 }
539
540 // Expand the tokens into a string buffer.
541 SmallVector<unsigned, 8> TokOffsets;
542 if (buildMSAsmString(PP, AsmLoc, AsmToks, TokOffsets, Asm&: AsmString))
543 return StmtError();
544
545 const TargetOptions &TO = Actions.Context.getTargetInfo().getTargetOpts();
546 std::string FeaturesStr =
547 llvm::join(Begin: TO.Features.begin(), End: TO.Features.end(), Separator: ",");
548
549 std::unique_ptr<llvm::MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TT));
550 if (!MRI) {
551 Diag(AsmLoc, diag::err_msasm_unable_to_create_target)
552 << "target MC unavailable";
553 return EmptyStmt();
554 }
555 // FIXME: init MCOptions from sanitizer flags here.
556 llvm::MCTargetOptions MCOptions;
557 std::unique_ptr<llvm::MCAsmInfo> MAI(
558 TheTarget->createMCAsmInfo(MRI: *MRI, TheTriple: TT, Options: MCOptions));
559 // Get the instruction descriptor.
560 std::unique_ptr<llvm::MCInstrInfo> MII(TheTarget->createMCInstrInfo());
561 std::unique_ptr<llvm::MCSubtargetInfo> STI(
562 TheTarget->createMCSubtargetInfo(TheTriple: TT, CPU: TO.CPU, Features: FeaturesStr));
563 // Target MCTargetDesc may not be linked in clang-based tools.
564
565 if (!MAI || !MII || !STI) {
566 Diag(AsmLoc, diag::err_msasm_unable_to_create_target)
567 << "target MC unavailable";
568 return EmptyStmt();
569 }
570
571 llvm::SourceMgr TempSrcMgr;
572 llvm::MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &TempSrcMgr);
573 std::unique_ptr<llvm::MCObjectFileInfo> MOFI(
574 TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false));
575 Ctx.setObjectFileInfo(MOFI.get());
576
577 std::unique_ptr<llvm::MemoryBuffer> Buffer =
578 llvm::MemoryBuffer::getMemBuffer(InputData: AsmString, BufferName: "<MS inline asm>");
579
580 // Tell SrcMgr about this buffer, which is what the parser will pick up.
581 TempSrcMgr.AddNewSourceBuffer(F: std::move(Buffer), IncludeLoc: llvm::SMLoc());
582
583 std::unique_ptr<llvm::MCStreamer> Str(createNullStreamer(Ctx));
584 std::unique_ptr<llvm::MCAsmParser> Parser(
585 createMCAsmParser(TempSrcMgr, Ctx, *Str, *MAI));
586
587 std::unique_ptr<llvm::MCTargetAsmParser> TargetParser(
588 TheTarget->createMCAsmParser(STI: *STI, Parser&: *Parser, MII: *MII, Options: MCOptions));
589 // Target AsmParser may not be linked in clang-based tools.
590 if (!TargetParser) {
591 Diag(AsmLoc, diag::err_msasm_unable_to_create_target)
592 << "target ASM parser unavailable";
593 return EmptyStmt();
594 }
595
596 std::unique_ptr<llvm::MCInstPrinter> IP(
597 TheTarget->createMCInstPrinter(T: llvm::Triple(TT), SyntaxVariant: 1, MAI: *MAI, MII: *MII, MRI: *MRI));
598
599 // Change to the Intel dialect.
600 Parser->setAssemblerDialect(1);
601 Parser->setTargetParser(*TargetParser);
602 Parser->setParsingMSInlineAsm(true);
603 TargetParser->setParsingMSInlineAsm(true);
604
605 ClangAsmParserCallback Callback(*this, AsmLoc, AsmString, AsmToks,
606 TokOffsets);
607 TargetParser->setSemaCallback(&Callback);
608 TempSrcMgr.setDiagHandler(DH: ClangAsmParserCallback::DiagHandlerCallback,
609 Ctx: &Callback);
610
611 unsigned NumOutputs;
612 unsigned NumInputs;
613 std::string AsmStringIR;
614 SmallVector<std::pair<void *, bool>, 4> OpExprs;
615 SmallVector<std::string, 4> Constraints;
616 SmallVector<std::string, 4> Clobbers;
617 if (Parser->parseMSInlineAsm(AsmString&: AsmStringIR, NumOutputs, NumInputs, OpDecls&: OpExprs,
618 Constraints, Clobbers, MII: MII.get(), IP: IP.get(),
619 SI&: Callback))
620 return StmtError();
621
622 // Filter out "fpsw" and "mxcsr". They aren't valid GCC asm clobber
623 // constraints. Clang always adds fpsr to the clobber list anyway.
624 llvm::erase_if(C&: Clobbers, P: [](const std::string &C) {
625 return C == "fpsr" || C == "mxcsr";
626 });
627
628 // Build the vector of clobber StringRefs.
629 llvm::append_range(C&: ClobberRefs, R&: Clobbers);
630
631 // Recast the void pointers and build the vector of constraint StringRefs.
632 unsigned NumExprs = NumOutputs + NumInputs;
633 ConstraintRefs.resize(N: NumExprs);
634 Exprs.resize(N: NumExprs);
635 for (unsigned i = 0, e = NumExprs; i != e; ++i) {
636 Expr *OpExpr = static_cast<Expr *>(OpExprs[i].first);
637 if (!OpExpr)
638 return StmtError();
639
640 // Need address of variable.
641 if (OpExprs[i].second)
642 OpExpr =
643 Actions.BuildUnaryOp(S: getCurScope(), OpLoc: AsmLoc, Opc: UO_AddrOf, Input: OpExpr).get();
644
645 ConstraintRefs[i] = StringRef(Constraints[i]);
646 Exprs[i] = OpExpr;
647 }
648
649 // FIXME: We should be passing source locations for better diagnostics.
650 return Actions.ActOnMSAsmStmt(AsmLoc, LBraceLoc: LBraceLocs[0], AsmToks, AsmString: AsmStringIR,
651 NumOutputs, NumInputs, Constraints: ConstraintRefs,
652 Clobbers: ClobberRefs, Exprs, EndLoc);
653}
654
655bool Parser::parseGNUAsmQualifierListOpt(GNUAsmQualifiers &AQ) {
656 while (true) {
657 const GNUAsmQualifiers::AQ A = getGNUAsmQualifier(Tok);
658 if (A == GNUAsmQualifiers::AQ_unspecified) {
659 if (Tok.isNot(K: tok::l_paren)) {
660 Diag(Tok.getLocation(), diag::err_asm_qualifier_ignored);
661 SkipUntil(T: tok::r_paren, Flags: StopAtSemi);
662 return true;
663 }
664 return false;
665 }
666 if (AQ.setAsmQualifier(A))
667 Diag(Tok.getLocation(), diag::err_asm_duplicate_qual)
668 << GNUAsmQualifiers::getQualifierName(A);
669 ConsumeToken();
670 }
671 return false;
672}
673
674StmtResult Parser::ParseAsmStatement(bool &msAsm) {
675 assert(Tok.is(tok::kw_asm) && "Not an asm stmt");
676 SourceLocation AsmLoc = ConsumeToken();
677
678 if (getLangOpts().AsmBlocks && !isGCCAsmStatement(TokAfterAsm: Tok)) {
679 msAsm = true;
680 return ParseMicrosoftAsmStatement(AsmLoc);
681 }
682
683 SourceLocation Loc = Tok.getLocation();
684 GNUAsmQualifiers GAQ;
685 if (parseGNUAsmQualifierListOpt(AQ&: GAQ))
686 return StmtError();
687
688 if (GAQ.isGoto() && getLangOpts().SpeculativeLoadHardening)
689 Diag(Loc, diag::warn_slh_does_not_support_asm_goto);
690
691 BalancedDelimiterTracker T(*this, tok::l_paren);
692 T.consumeOpen();
693
694 ExprResult AsmString(ParseAsmStringLiteral(/*ForAsmLabel*/ false));
695
696 // Check if GNU-style InlineAsm is disabled.
697 // Error on anything other than empty string.
698 if (!(getLangOpts().GNUAsm || AsmString.isInvalid())) {
699 const auto *SL = cast<StringLiteral>(Val: AsmString.get());
700 if (!SL->getString().trim().empty())
701 Diag(Loc, diag::err_gnu_inline_asm_disabled);
702 }
703
704 if (AsmString.isInvalid()) {
705 // Consume up to and including the closing paren.
706 T.skipToEnd();
707 return StmtError();
708 }
709
710 SmallVector<IdentifierInfo *, 4> Names;
711 ExprVector Constraints;
712 ExprVector Exprs;
713 ExprVector Clobbers;
714
715 if (Tok.is(K: tok::r_paren)) {
716 // We have a simple asm expression like 'asm("foo")'.
717 T.consumeClose();
718 return Actions.ActOnGCCAsmStmt(
719 AsmLoc, /*isSimple*/ IsSimple: true, IsVolatile: GAQ.isVolatile(),
720 /*NumOutputs*/ 0, /*NumInputs*/ 0, Names: nullptr, Constraints, Exprs,
721 AsmString: AsmString.get(), Clobbers, /*NumLabels*/ 0, RParenLoc: T.getCloseLocation());
722 }
723
724 // Parse Outputs, if present.
725 bool AteExtraColon = false;
726 if (Tok.is(K: tok::colon) || Tok.is(K: tok::coloncolon)) {
727 // In C++ mode, parse "::" like ": :".
728 AteExtraColon = Tok.is(K: tok::coloncolon);
729 ConsumeToken();
730
731 if (!AteExtraColon && ParseAsmOperandsOpt(Names, Constraints, Exprs))
732 return StmtError();
733 }
734
735 unsigned NumOutputs = Names.size();
736
737 // Parse Inputs, if present.
738 if (AteExtraColon || Tok.is(K: tok::colon) || Tok.is(K: tok::coloncolon)) {
739 // In C++ mode, parse "::" like ": :".
740 if (AteExtraColon)
741 AteExtraColon = false;
742 else {
743 AteExtraColon = Tok.is(K: tok::coloncolon);
744 ConsumeToken();
745 }
746
747 if (!AteExtraColon && ParseAsmOperandsOpt(Names, Constraints, Exprs))
748 return StmtError();
749 }
750
751 assert(Names.size() == Constraints.size() &&
752 Constraints.size() == Exprs.size() && "Input operand size mismatch!");
753
754 unsigned NumInputs = Names.size() - NumOutputs;
755
756 // Parse the clobbers, if present.
757 if (AteExtraColon || Tok.is(K: tok::colon) || Tok.is(K: tok::coloncolon)) {
758 if (AteExtraColon)
759 AteExtraColon = false;
760 else {
761 AteExtraColon = Tok.is(K: tok::coloncolon);
762 ConsumeToken();
763 }
764 // Parse the asm-string list for clobbers if present.
765 if (!AteExtraColon && (isTokenStringLiteral() || Tok.is(K: tok::l_paren))) {
766 while (true) {
767 ExprResult Clobber(ParseAsmStringLiteral(/*ForAsmLabel*/ false));
768
769 if (Clobber.isInvalid())
770 break;
771
772 Clobbers.push_back(Elt: Clobber.get());
773
774 if (!TryConsumeToken(Expected: tok::comma))
775 break;
776 }
777 }
778 }
779 if (!GAQ.isGoto() && (Tok.isNot(K: tok::r_paren) || AteExtraColon)) {
780 Diag(Tok, diag::err_expected) << tok::r_paren;
781 SkipUntil(T: tok::r_paren, Flags: StopAtSemi);
782 return StmtError();
783 }
784
785 // Parse the goto label, if present.
786 unsigned NumLabels = 0;
787 if (AteExtraColon || Tok.is(K: tok::colon)) {
788 if (!AteExtraColon)
789 ConsumeToken();
790
791 while (true) {
792 if (Tok.isNot(K: tok::identifier)) {
793 Diag(Tok, diag::err_expected) << tok::identifier;
794 SkipUntil(T: tok::r_paren, Flags: StopAtSemi);
795 return StmtError();
796 }
797 LabelDecl *LD = Actions.LookupOrCreateLabel(II: Tok.getIdentifierInfo(),
798 IdentLoc: Tok.getLocation());
799 Names.push_back(Elt: Tok.getIdentifierInfo());
800 if (!LD) {
801 SkipUntil(T: tok::r_paren, Flags: StopAtSemi);
802 return StmtError();
803 }
804 ExprResult Res =
805 Actions.ActOnAddrLabel(OpLoc: Tok.getLocation(), LabLoc: Tok.getLocation(), TheDecl: LD);
806 Exprs.push_back(Elt: Res.get());
807 NumLabels++;
808 ConsumeToken();
809 if (!TryConsumeToken(Expected: tok::comma))
810 break;
811 }
812 } else if (GAQ.isGoto()) {
813 Diag(Tok, diag::err_expected) << tok::colon;
814 SkipUntil(T: tok::r_paren, Flags: StopAtSemi);
815 return StmtError();
816 }
817 T.consumeClose();
818 return Actions.ActOnGCCAsmStmt(AsmLoc, IsSimple: false, IsVolatile: GAQ.isVolatile(), NumOutputs,
819 NumInputs, Names: Names.data(), Constraints, Exprs,
820 AsmString: AsmString.get(), Clobbers, NumLabels,
821 RParenLoc: T.getCloseLocation());
822}
823
824bool Parser::ParseAsmOperandsOpt(SmallVectorImpl<IdentifierInfo *> &Names,
825 SmallVectorImpl<Expr *> &Constraints,
826 SmallVectorImpl<Expr *> &Exprs) {
827 // 'asm-operands' isn't present
828 if (Tok.isOneOf(K1: tok::colon, Ks: tok::coloncolon, Ks: tok::r_paren))
829 return false;
830
831 while (true) {
832 // Read the [id] if present.
833 if (Tok.is(K: tok::l_square)) {
834 BalancedDelimiterTracker T(*this, tok::l_square);
835 T.consumeOpen();
836
837 if (Tok.isNot(K: tok::identifier)) {
838 Diag(Tok, diag::err_expected) << tok::identifier;
839 SkipUntil(T: tok::r_paren, Flags: StopAtSemi);
840 return true;
841 }
842
843 IdentifierInfo *II = Tok.getIdentifierInfo();
844 ConsumeToken();
845
846 Names.push_back(Elt: II);
847 T.consumeClose();
848 } else
849 Names.push_back(Elt: nullptr);
850
851 ExprResult Constraint(ParseAsmStringLiteral(/*ForAsmLabel*/ false));
852 if (Constraint.isInvalid()) {
853 SkipUntil(T: tok::r_paren, Flags: StopAtSemi);
854 return true;
855 }
856 Constraints.push_back(Elt: Constraint.get());
857
858 if (Tok.isNot(K: tok::l_paren)) {
859 Diag(Tok, diag::err_expected_lparen_after) << "asm operand";
860 SkipUntil(T: tok::r_paren, Flags: StopAtSemi);
861 return true;
862 }
863
864 // Read the parenthesized expression.
865 BalancedDelimiterTracker T(*this, tok::l_paren);
866 T.consumeOpen();
867 ExprResult Res = Actions.CorrectDelayedTyposInExpr(ER: ParseExpression());
868 T.consumeClose();
869 if (Res.isInvalid()) {
870 SkipUntil(T: tok::r_paren, Flags: StopAtSemi);
871 return true;
872 }
873 Exprs.push_back(Elt: Res.get());
874 // Eat the comma and continue parsing if it exists.
875 if (!TryConsumeToken(Expected: tok::comma))
876 return false;
877 }
878}
879
880const char *Parser::GNUAsmQualifiers::getQualifierName(AQ Qualifier) {
881 switch (Qualifier) {
882 case AQ_volatile: return "volatile";
883 case AQ_inline: return "inline";
884 case AQ_goto: return "goto";
885 case AQ_unspecified: return "unspecified";
886 }
887 llvm_unreachable("Unknown GNUAsmQualifier");
888}
889
890Parser::GNUAsmQualifiers::AQ
891Parser::getGNUAsmQualifier(const Token &Tok) const {
892 switch (Tok.getKind()) {
893 case tok::kw_volatile: return GNUAsmQualifiers::AQ_volatile;
894 case tok::kw_inline: return GNUAsmQualifiers::AQ_inline;
895 case tok::kw_goto: return GNUAsmQualifiers::AQ_goto;
896 default: return GNUAsmQualifiers::AQ_unspecified;
897 }
898}
899bool Parser::GNUAsmQualifiers::setAsmQualifier(AQ Qualifier) {
900 bool IsDuplicate = Qualifiers & Qualifier;
901 Qualifiers |= Qualifier;
902 return IsDuplicate;
903}
904

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of clang/lib/Parse/ParseStmtAsm.cpp