Index: include/clang/AST/Stmt.h =================================================================== --- include/clang/AST/Stmt.h +++ include/clang/AST/Stmt.h @@ -1720,10 +1720,14 @@ StringRef AsmStr; unsigned NumAsmToks; + unsigned NumBranchTargets; + unsigned NumLabelsDefined; Token *AsmToks; StringRef *Constraints; StringRef *Clobbers; + StringRef *BranchTargets; + StringRef *LabelsDefined; friend class ASTStmtReader; @@ -1733,11 +1737,14 @@ ArrayRef asmtoks, unsigned numoutputs, unsigned numinputs, ArrayRef constraints, ArrayRef exprs, StringRef asmstr, - ArrayRef clobbers, SourceLocation endloc); + ArrayRef clobbers, + ArrayRef branchTargets, + ArrayRef labelsDefined, SourceLocation endloc); /// \brief Build an empty MS-style inline-assembly statement. explicit MSAsmStmt(EmptyShell Empty) : AsmStmt(MSAsmStmtClass, Empty), - NumAsmToks(0), AsmToks(nullptr), Constraints(nullptr), Clobbers(nullptr) { } + NumAsmToks(0), NumBranchTargets(0), NumLabelsDefined(0), + AsmToks(nullptr), Constraints(nullptr), Clobbers(nullptr) { } SourceLocation getLBraceLoc() const { return LBraceLoc; } void setLBraceLoc(SourceLocation L) { LBraceLoc = L; } @@ -1790,6 +1797,12 @@ ArrayRef getClobbers() const { return llvm::makeArrayRef(Clobbers, NumClobbers); } + ArrayRef getBranchTargets() const { + return llvm::makeArrayRef(BranchTargets, NumBranchTargets); + } + ArrayRef getLabelsDefined() const { + return llvm::makeArrayRef(LabelsDefined, NumLabelsDefined); + } ArrayRef getAllExprs() const { return llvm::makeArrayRef(reinterpret_cast(Exprs), NumInputs + NumOutputs); @@ -1800,7 +1813,9 @@ private: void initialize(const ASTContext &C, StringRef AsmString, ArrayRef AsmToks, ArrayRef Constraints, - ArrayRef Exprs, ArrayRef Clobbers); + ArrayRef Exprs, ArrayRef Clobbers, + ArrayRef BranchTargets, + ArrayRef LabelsDefined); public: SourceLocation getLocStart() const LLVM_READONLY { return AsmLoc; } Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -4137,6 +4137,10 @@ "cannot jump from this goto statement to label %0 inside an inline assembly block">; def note_goto_ms_asm_label : Note< "inline assembly label %0 declared here">; +def err_cross_block_asm_jump : Error< + "cannot jump from one inline assembly block to label %0 defined inside another one">; +def note_cross_block_asm_jump : Note < + "label %0 defined in an inline assembly block defined here">; def warn_unused_label : Warning<"unused label %0">, InGroup, DefaultIgnore; Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -3157,6 +3157,8 @@ unsigned NumOutputs, unsigned NumInputs, ArrayRef Constraints, ArrayRef Clobbers, + ArrayRef BranchTargets, + ArrayRef LabelsDefined, ArrayRef Exprs, SourceLocation EndLoc); LabelDecl *GetOrCreateMSAsmLabel(StringRef ExternalLabelName, Index: lib/AST/Stmt.cpp =================================================================== --- lib/AST/Stmt.cpp +++ lib/AST/Stmt.cpp @@ -689,12 +689,16 @@ unsigned numinputs, ArrayRef constraints, ArrayRef exprs, StringRef asmstr, ArrayRef clobbers, - SourceLocation endloc) + ArrayRef branchTargets, + ArrayRef labelsDefined, SourceLocation endloc) : AsmStmt(MSAsmStmtClass, asmloc, issimple, isvolatile, numoutputs, numinputs, clobbers.size()), LBraceLoc(lbraceloc), - EndLoc(endloc), NumAsmToks(asmtoks.size()) { + EndLoc(endloc), NumAsmToks(asmtoks.size()), + NumBranchTargets(branchTargets.size()), + NumLabelsDefined(labelsDefined.size()) { - initialize(C, asmstr, asmtoks, constraints, exprs, clobbers); + initialize(C, asmstr, asmtoks, constraints, exprs, clobbers, + branchTargets, labelsDefined); } static StringRef copyIntoContext(const ASTContext &C, StringRef str) { @@ -708,9 +712,13 @@ ArrayRef asmtoks, ArrayRef constraints, ArrayRef exprs, - ArrayRef clobbers) { + ArrayRef clobbers, + ArrayRef branchTargets, + ArrayRef labelsDefined) { assert(NumAsmToks == asmtoks.size()); assert(NumClobbers == clobbers.size()); + assert(NumBranchTargets == branchTargets.size()); + assert(NumLabelsDefined == labelsDefined.size()); unsigned NumExprs = exprs.size(); assert(NumExprs == NumOutputs + NumInputs); @@ -736,6 +744,18 @@ // FIXME: Avoid the allocation/copy if at all possible. Clobbers[i] = copyIntoContext(C, clobbers[i]); } + + BranchTargets = new (C) StringRef[NumBranchTargets]; + for (unsigned i = 0, e = NumBranchTargets; i != e; ++i) { + // FIXME: Avoid the allocation/copy if at all possible. + BranchTargets[i] = copyIntoContext(C, branchTargets[i]); + } + + LabelsDefined = new (C) StringRef[NumLabelsDefined]; + for (unsigned i = 0, e = NumLabelsDefined; i != e; ++i) { + // FIXME: Avoid the allocation/copy if at all possible. + LabelsDefined[i] = copyIntoContext(C, labelsDefined[i]); + } } ObjCForCollectionStmt::ObjCForCollectionStmt(Stmt *Elem, Expr *Collect, Index: lib/Parse/ParseStmtAsm.cpp =================================================================== --- lib/Parse/ParseStmtAsm.cpp +++ lib/Parse/ParseStmtAsm.cpp @@ -469,6 +469,8 @@ SmallVector ConstraintRefs; SmallVector Exprs; SmallVector ClobberRefs; + SmallVector BranchTargetRefs; + SmallVector LabelsDefinedRefs; // We need an actual supported target. const llvm::Triple &TheTriple = Actions.Context.getTargetInfo().getTriple(); @@ -493,7 +495,8 @@ if (!TheTarget || AsmToks.empty()) { return Actions.ActOnMSAsmStmt(AsmLoc, LBraceLocs[0], AsmToks, StringRef(), /*NumOutputs*/ 0, /*NumInputs*/ 0, - ConstraintRefs, ClobberRefs, Exprs, EndLoc); + ConstraintRefs, ClobberRefs, BranchTargetRefs, + LabelsDefinedRefs, Exprs, EndLoc); } // Expand the tokens into a string buffer. @@ -548,9 +551,12 @@ SmallVector, 4> OpExprs; SmallVector Constraints; SmallVector Clobbers; + SmallVector BranchTargets; + SmallVector LabelsDefined; if (Parser->parseMSInlineAsm(AsmLoc.getPtrEncoding(), AsmStringIR, NumOutputs, NumInputs, OpExprs, Constraints, Clobbers, - MII.get(), IP.get(), Callback)) + BranchTargets, LabelsDefined, MII.get(), IP.get(), + Callback)) return StmtError(); // Filter out "fpsw". Clang doesn't accept it, and it always lists flags and @@ -560,6 +566,8 @@ // Build the vector of clobber StringRefs. ClobberRefs.insert(ClobberRefs.end(), Clobbers.begin(), Clobbers.end()); + BranchTargetRefs.insert(BranchTargetRefs.end(), BranchTargets.begin(), BranchTargets.end()); + LabelsDefinedRefs.insert(LabelsDefinedRefs.end(), LabelsDefined.begin(), LabelsDefined.end()); // Recast the void pointers and build the vector of constraint StringRefs. unsigned NumExprs = NumOutputs + NumInputs; @@ -582,7 +590,8 @@ // FIXME: We should be passing source locations for better diagnostics. return Actions.ActOnMSAsmStmt(AsmLoc, LBraceLocs[0], AsmToks, AsmStringIR, NumOutputs, NumInputs, ConstraintRefs, - ClobberRefs, Exprs, EndLoc); + ClobberRefs, BranchTargetRefs, + LabelsDefinedRefs, Exprs, EndLoc); } /// ParseAsmStatement - Parse a GNU extended asm statement. Index: lib/Sema/JumpDiagnostics.cpp =================================================================== --- lib/Sema/JumpDiagnostics.cpp +++ lib/Sema/JumpDiagnostics.cpp @@ -18,6 +18,7 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" +#include "clang/Lex/Preprocessor.h" #include "llvm/ADT/BitVector.h" using namespace clang; @@ -85,6 +86,8 @@ unsigned JumpDiag, unsigned JumpDiagWarning, unsigned JumpDiagCXX98Compat); void CheckGotoStmt(GotoStmt *GS); + void CheckMSAsmStmt(MSAsmStmt *AsmStmt, + const std::map &LabelProvider); unsigned GetDeepestCommonScope(unsigned A, unsigned B); }; @@ -315,6 +318,11 @@ Jumps.push_back(S); break; + case Stmt::MSAsmStmtClass: + // Verify possible jumps from within MS inline asm blocks. + Jumps.push_back(S); + break; + case Stmt::CXXTryStmtClass: { CXXTryStmt *TS = cast(S); unsigned newParentScope; @@ -485,6 +493,15 @@ /// VerifyJumps - Verify each element of the Jumps array to see if they are /// valid, emitting diagnostics if not. void JumpScopeChecker::VerifyJumps() { + std::map LabelProvider; + for (Stmt *J : Jumps) { + if (isa(J)) { + for (const auto &L : cast(J)->getLabelsDefined()) { + LabelProvider.insert(std::make_pair(L, cast(J))); + } + } + } + while (!Jumps.empty()) { Stmt *Jump = Jumps.pop_back_val(); @@ -511,6 +528,11 @@ continue; } + if (MSAsmStmt *AsmStmt = dyn_cast(Jump)) { + CheckMSAsmStmt(AsmStmt, LabelProvider); + continue; + } + SwitchStmt *SS = cast(Jump); for (SwitchCase *SC = SS->getSwitchCaseList(); SC; SC = SC->getNextSwitchCase()) { @@ -803,6 +825,20 @@ } } +void JumpScopeChecker::CheckMSAsmStmt(MSAsmStmt *AsmStmt, + const std::map &LabelProvider) { + for (const auto &BT : AsmStmt->getBranchTargets()) { + const auto &LabelProviderStmt = LabelProvider.find(BT); + if (LabelProviderStmt != LabelProvider.end() && + LabelProviderStmt->second != AsmStmt) { + S.Diag(AsmStmt->getLocStart(), diag::err_cross_block_asm_jump) + << S.getPreprocessor().getIdentifierInfo(BT); + S.Diag(LabelProviderStmt->second->getLocStart(), diag::note_cross_block_asm_jump) + << S.getPreprocessor().getIdentifierInfo(BT); + } + } +} + void Sema::DiagnoseInvalidJumps(Stmt *Body) { (void)JumpScopeChecker(Body, *this); } Index: lib/Sema/SemaStmtAsm.cpp =================================================================== --- lib/Sema/SemaStmtAsm.cpp +++ lib/Sema/SemaStmtAsm.cpp @@ -518,6 +518,8 @@ unsigned NumOutputs, unsigned NumInputs, ArrayRef Constraints, ArrayRef Clobbers, + ArrayRef BranchTargets, + ArrayRef LabelsDefined, ArrayRef Exprs, SourceLocation EndLoc) { bool IsSimple = (NumOutputs != 0 || NumInputs != 0); @@ -526,7 +528,7 @@ new (Context) MSAsmStmt(Context, AsmLoc, LBraceLoc, IsSimple, /*IsVolatile*/ true, AsmToks, NumOutputs, NumInputs, Constraints, Exprs, AsmString, - Clobbers, EndLoc); + Clobbers, BranchTargets, LabelsDefined, EndLoc); return NS; } @@ -545,6 +547,9 @@ // name. OS << "__MSASMLABEL_." << MSAsmLabelNameCounter++ << "__" << ExternalLabelName; Label->setMSAsmLabel(OS.str()); + // We need to check possible cross inline MS asm block jumps when we encounter an + // MS asm label being defined. + getCurFunction()->setHasIndirectGoto(); } if (AlwaysCreate) { // The label might have been created implicitly from a previously encountered Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -1231,11 +1231,14 @@ unsigned NumOutputs, unsigned NumInputs, ArrayRef Constraints, ArrayRef Clobbers, + ArrayRef BranchTargets, + ArrayRef LabelsDefined, ArrayRef Exprs, SourceLocation EndLoc) { return getSema().ActOnMSAsmStmt(AsmLoc, LBraceLoc, AsmToks, AsmString, NumOutputs, NumInputs, - Constraints, Clobbers, Exprs, EndLoc); + Constraints, Clobbers, BranchTargets, + LabelsDefined, Exprs, EndLoc); } /// \brief Build a new Objective-C \@try statement. @@ -5947,6 +5950,8 @@ AsmToks, S->getAsmString(), S->getNumOutputs(), S->getNumInputs(), S->getAllConstraints(), S->getClobbers(), + S->getBranchTargets(), + S->getLabelsDefined(), TransformedExprs, S->getEndLoc()); } Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -368,6 +368,8 @@ SmallVector Exprs; SmallVector ConstraintsData; SmallVector Constraints; + SmallVector BranchTargets; + SmallVector LabelsDefined; Exprs.reserve(NumOperands); ConstraintsData.reserve(NumOperands); Constraints.reserve(NumOperands); @@ -378,7 +380,8 @@ } S->initialize(Reader.getContext(), AsmStr, AsmToks, - Constraints, Exprs, Clobbers); + Constraints, Exprs, Clobbers, BranchTargets, + LabelsDefined); } void ASTStmtReader::VisitCapturedStmt(CapturedStmt *S) { Index: test/Parser/ms-inline-asm.c =================================================================== --- test/Parser/ms-inline-asm.c +++ test/Parser/ms-inline-asm.c @@ -51,6 +51,37 @@ void t12() { __asm jmp label // expected-error {{use of undeclared label 'label'}} } +void t13() { + __asm { // expected-error {{cannot jump from one inline assembly block to label 'lbl1' defined inside another one}} + jmp lbl1; + } + __asm { // expected-note {{label 'lbl1' defined in an inline assembly block defined here}} + lbl1: + nop + } +} +void t14() { + __asm { // expected-error {{cannot jump from one inline assembly block to label 'lbl2' defined inside another one}} + jmp lbl2; + } + for (;;) { + __asm { // expected-note {{label 'lbl2' defined in an inline assembly block defined here}} + lbl2: + nop + } + } +} +void t15() { + for (;;) { + __asm { // expected-error {{cannot jump from one inline assembly block to label 'lbl2' defined inside another one}} + jmp lbl2; + } + } + __asm { // expected-note {{label 'lbl2' defined in an inline assembly block defined here}} + lbl2: + nop + } +} int t_fail() { // expected-note {{to match this}} __asm __asm { // expected-error 2 {{expected}} expected-note {{to match this}}