Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -24,6 +24,7 @@ #include "clang/Basic/OperatorKinds.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallString.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" @@ -308,6 +309,8 @@ class LabelDecl : public NamedDecl { void anchor() override; LabelStmt *TheStmt; + SmallString<16> MSAsmName; + bool MSAsmNameResolved; /// LocStart - For normal labels, this is the same as the main declaration /// label, i.e., the location of the identifier; for GNU local labels, /// this is the location of the __label__ keyword. @@ -315,7 +318,10 @@ LabelDecl(DeclContext *DC, SourceLocation IdentL, IdentifierInfo *II, LabelStmt *S, SourceLocation StartL) - : NamedDecl(Label, DC, IdentL, II), TheStmt(S), LocStart(StartL) {} + : NamedDecl(Label, DC, IdentL, II), + TheStmt(S), + MSAsmNameResolved(false), + LocStart(StartL) {} public: static LabelDecl *Create(ASTContext &C, DeclContext *DC, @@ -335,6 +341,12 @@ return SourceRange(LocStart, getLocation()); } + bool isMSAsmLabel() const { return MSAsmName.size() != 0; } + bool isResolvedMSAsmLabel() const { return isMSAsmLabel() && MSAsmNameResolved; } + void setMSAsmLabel(StringRef Name) { MSAsmName = Name; } + StringRef getMSAsmLabel() const { return MSAsmName; } + void setMSAsmLabelResolved() { MSAsmNameResolved = true; } + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == Label; } Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -4116,6 +4116,10 @@ def err_redefinition_of_label : Error<"redefinition of label %0">; def err_undeclared_label_use : Error<"use of undeclared label %0">; +def err_goto_ms_asm_label : Error< + "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 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 @@ -3156,6 +3156,9 @@ ArrayRef Clobbers, ArrayRef Exprs, SourceLocation EndLoc); + LabelDecl *GetOrCreateMSAsmLabel(StringRef ExternalLabelName, + SourceLocation Location, + bool AlwaysCreate); VarDecl *BuildObjCExceptionDecl(TypeSourceInfo *TInfo, QualType ExceptionType, SourceLocation StartLoc, Index: lib/Parse/ParseStmtAsm.cpp =================================================================== --- lib/Parse/ParseStmtAsm.cpp +++ lib/Parse/ParseStmtAsm.cpp @@ -93,6 +93,18 @@ return Info.OpDecl; } + StringRef LookupInlineAsmLabel(StringRef Identifier, llvm::SourceMgr &LSM, + llvm::SMLoc Location, + bool Create) override { + const llvm::MemoryBuffer *LBuf = + LSM.getMemoryBuffer(LSM.FindBufferContainingLoc(Location)); + unsigned Offset = Location.getPointer() - LBuf->getBufferStart(); + SourceLocation Loc = translateLocation(Offset); + LabelDecl *Label = + TheParser.getActions().GetOrCreateMSAsmLabel(Identifier, Loc, Create); + return Label->getMSAsmLabel(); + } + bool LookupInlineAsmField(StringRef Base, StringRef Member, unsigned &Offset) override { return TheParser.getActions().LookupInlineAsmField(Base, Member, Offset, @@ -133,15 +145,7 @@ } } - void handleDiagnostic(const llvm::SMDiagnostic &D) { - // Compute an offset into the inline asm buffer. - // FIXME: This isn't right if .macro is involved (but hopefully, no - // real-world code does that). - const llvm::SourceMgr &LSM = *D.getSourceMgr(); - const llvm::MemoryBuffer *LBuf = - LSM.getMemoryBuffer(LSM.FindBufferContainingLoc(D.getLoc())); - unsigned Offset = D.getLoc().getPointer() - LBuf->getBufferStart(); - + SourceLocation translateLocation(unsigned Offset) { // Figure out which token that offset points into. const unsigned *TokOffsetPtr = std::lower_bound(AsmTokOffsets.begin(), AsmTokOffsets.end(), Offset); @@ -157,6 +161,19 @@ Loc = Tok.getLocation(); Loc = Loc.getLocWithOffset(Offset - TokOffset); } + return Loc; + } + + void handleDiagnostic(const llvm::SMDiagnostic &D) { + // Compute an offset into the inline asm buffer. + // FIXME: This isn't right if .macro is involved (but hopefully, no + // real-world code does that). + const llvm::SourceMgr &LSM = *D.getSourceMgr(); + const llvm::MemoryBuffer *LBuf = + LSM.getMemoryBuffer(LSM.FindBufferContainingLoc(D.getLoc())); + unsigned Offset = D.getLoc().getPointer() - LBuf->getBufferStart(); + + SourceLocation Loc = translateLocation(Offset); TheParser.Diag(Loc, diag::err_inline_ms_asm_parsing) << D.getMessage(); } }; Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -1517,8 +1517,14 @@ static void CheckPoppedLabel(LabelDecl *L, Sema &S) { // Verify that we have no forward references left. If so, there was a goto // or address of a label taken, but no definition of it. Label fwd - // definitions are indicated with a null substmt. - if (L->getStmt() == nullptr) + // definitions are indicated with a null substmt which is also not a resolved + // MS inline assembly label name. + bool Diagnose = false; + if (L->isMSAsmLabel()) + Diagnose = !L->isResolvedMSAsmLabel(); + else + Diagnose = L->getStmt() == nullptr; + if (Diagnose) S.Diag(L->getLocation(), diag::err_undeclared_label_use) <getDeclName(); } Index: lib/Sema/SemaStmt.cpp =================================================================== --- lib/Sema/SemaStmt.cpp +++ lib/Sema/SemaStmt.cpp @@ -2353,6 +2353,15 @@ StmtResult Sema::ActOnGotoStmt(SourceLocation GotoLoc, SourceLocation LabelLoc, LabelDecl *TheDecl) { + if (TheDecl->isMSAsmLabel()) { + // TODO Move this diagnostic elsewhere so that we get to examine the goto + // statements after we've seen all of the body of the current scope. + StmtResult error(StmtError(Diag(GotoLoc, diag::err_goto_ms_asm_label) + << TheDecl->getIdentifier())); + Diag(TheDecl->getLocation(), diag::note_goto_ms_asm_label) + << TheDecl->getIdentifier(); + return error; + } getCurFunction()->setHasBranchIntoScope(); TheDecl->markUsed(Context); return new (Context) GotoStmt(TheDecl, GotoLoc, LabelLoc); Index: lib/Sema/SemaStmtAsm.cpp =================================================================== --- lib/Sema/SemaStmtAsm.cpp +++ lib/Sema/SemaStmtAsm.cpp @@ -15,6 +15,7 @@ #include "clang/AST/RecordLayout.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/TargetInfo.h" +#include "clang/Lex/Preprocessor.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/Scope.h" @@ -22,6 +23,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/BitVector.h" #include "llvm/MC/MCParser/MCAsmParser.h" +#include "llvm/Support/Format.h" using namespace clang; using namespace sema; @@ -513,3 +515,27 @@ Clobbers, EndLoc); return NS; } + +LabelDecl *Sema::GetOrCreateMSAsmLabel(StringRef ExternalLabelName, + SourceLocation Location, + bool AlwaysCreate) { + static uint32_t counter = 0; + + LabelDecl* Label = LookupOrCreateLabel(PP.getIdentifierInfo(ExternalLabelName), + Location); + + if (!Label->isMSAsmLabel()) { + // Otherwise, insert it, but only resolve it if we have seen the label itself. + std::string InternalName; + llvm::raw_string_ostream OS(InternalName); + // Create an internal name for the label. The name should not be a valid mangled + // name, and should be unique. We use a dot to make the name an invalid mangled + // name. + OS << llvm::format("__MSASMLABEL_.%" PRIu32 "__", counter++); + Label->setMSAsmLabel(OS.str()); + } + if (AlwaysCreate) + Label->setMSAsmLabelResolved(); + + return Label; +} Index: test/CodeGen/mozilla-ms-inline-asm.c =================================================================== --- test/CodeGen/mozilla-ms-inline-asm.c +++ test/CodeGen/mozilla-ms-inline-asm.c @@ -3,6 +3,8 @@ // Some test cases for MS inline asm support from Mozilla code base. +void invoke_copy_to_stack() {} + void invoke(void* that, unsigned methodIndex, unsigned paramCount, void* params) { @@ -18,24 +20,25 @@ // CHECK: call void asm sideeffect inteldialect // CHECK: mov edx,dword ptr $1 // CHECK: test edx,edx -// CHECK: jz noparams +// CHECK: jz {{[^_]*}}__MSASMLABEL_.0__ +// ^ Can't use {{.*}} here because the matching is greedy. // CHECK: mov eax,edx // CHECK: shl eax,$$3 // CHECK: sub esp,eax // CHECK: mov ecx,esp // CHECK: push dword ptr $0 -// CHECK: call invoke_copy_to_stack -// CHECK: noparams: -// CHECK: mov ecx,dword ptr $2 +// CHECK: call dword ptr $2 +// CHECK: {{.*}}__MSASMLABEL_.0__: +// CHECK: mov ecx,dword ptr $3 // CHECK: push ecx // CHECK: mov edx,[ecx] -// CHECK: mov eax,dword ptr $3 +// CHECK: mov eax,dword ptr $4 // CHECK: call dword ptr[edx+eax*$$4] // CHECK: mov esp,ebp // CHECK: pop ebp // CHECK: ret -// CHECK: "=*m,*m,*m,*m,~{eax},~{ebp},~{ecx},~{edx},~{flags},~{esp},~{dirflag},~{fpsr},~{flags}" -// CHECK: (i8** %8, i32* %7, i8** %5, i32* %6) +// CHECK: "=*m,*m,*m,*m,*m,~{eax},~{ebp},~{ecx},~{edx},~{flags},~{esp},~{dirflag},~{fpsr},~{flags}" +// CHECK: (i8** %8, i32* %7, void (...)* bitcast (void ()* @invoke_copy_to_stack to void (...)*), i8** %5, i32* %6) // CHECK: ret void __asm { mov edx,paramCount Index: test/CodeGen/ms-inline-asm.c =================================================================== --- test/CodeGen/ms-inline-asm.c +++ test/CodeGen/ms-inline-asm.c @@ -240,7 +240,7 @@ the_label: } // CHECK: t23 -// CHECK: call void asm sideeffect inteldialect "the_label:", "~{dirflag},~{fpsr},~{flags}"() +// CHECK: call void asm sideeffect inteldialect "{{.*}}__MSASMLABEL_.0__:", "~{dirflag},~{fpsr},~{flags}"() } void t24_helper(void) {} @@ -517,3 +517,30 @@ } // CHECK-LABEL: define void @xgetbv() // CHECK: call void asm sideeffect inteldialect "xgetbv", "~{eax},~{edx},~{dirflag},~{fpsr},~{flags}"() + +void label1() { + __asm { + label: + jmp label + } + // CHECK-LABEL: define void @label1 + // CHECK: call void asm sideeffect inteldialect "{{.*}}__MSASMLABEL_.1__:\0A\09jmp {{.*}}__MSASMLABEL_.1__", "~{dirflag},~{fpsr},~{flags}"() +} + +void label2() { + __asm { + jmp label + label: + } + // CHECK-LABEL: define void @label2 + // CHECK: call void asm sideeffect inteldialect "jmp {{.*}}__MSASMLABEL_.2__\0A\09{{.*}}__MSASMLABEL_.2__:", "~{dirflag},~{fpsr},~{flags}"() +} + +void label3() { + __asm { + label: + mov eax, label + } + // CHECK-LABEL: define void @label3 + // CHECK: call void asm sideeffect inteldialect "{{.*}}__MSASMLABEL_.3__:\0A\09mov eax, {{.*}}__MSASMLABEL_.3__", "~{eax},~{dirflag},~{fpsr},~{flags}"() +} Index: test/Parser/ms-inline-asm.c =================================================================== --- test/Parser/ms-inline-asm.c +++ test/Parser/ms-inline-asm.c @@ -48,6 +48,9 @@ void t11() { do { __asm mov eax, 0 __asm { __asm mov edx, 1 } } while(0); } +void t12() { + __asm jmp label // expected-error {{use of undeclared label 'label'}} +} int t_fail() { // expected-note {{to match this}} __asm __asm { // expected-error 2 {{expected}} expected-note {{to match this}} Index: test/Sema/ms-inline-asm.c =================================================================== --- test/Sema/ms-inline-asm.c +++ test/Sema/ms-inline-asm.c @@ -21,7 +21,7 @@ } f(); __asm { - mov eax, LENGTH bar // expected-error {{unable to lookup expression}} + mov eax, LENGTH bar // expected-error {{unable to lookup expression}} expected-error {{use of undeclared label 'bar'}} } f(); __asm { @@ -80,9 +80,10 @@ } A; void t3() { - __asm { mov eax, [eax] UndeclaredId } // expected-error {{unknown token in expression}} + __asm { mov eax, [eax] UndeclaredId } // expected-error {{unknown token in expression}} expected-error {{use of undeclared label 'UndeclaredId'}} // FIXME: Only emit one diagnostic here. + // expected-error@+3 {{use of undeclared label 'A'}} // expected-error@+2 {{unexpected type name 'A': expected expression}} // expected-error@+1 {{unknown token in expression}} __asm { mov eax, [eax] A } @@ -105,7 +106,7 @@ } __declspec(naked) int t5(int x) { // expected-note {{attribute is here}} - asm { movl eax, x } // expected-error {{parameter references not allowed in naked functions}} + asm { movl eax, x } // expected-error {{parameter references not allowed in naked functions}} expected-error {{use of undeclared label 'x'}} asm { retl } } @@ -114,3 +115,34 @@ asm { mov eax, y } // No error. asm { ret } } + +void t7() { + __asm { + foo: // expected-note {{inline assembly label 'foo' declared here}} + mov eax, 0 + } + goto foo; // expected-error {{cannot jump from this goto statement to label 'foo' inside an inline assembly block}} +} + +void t8() { + __asm foo: // expected-note {{inline assembly label 'foo' declared here}} + __asm mov eax, 0 + goto foo; // expected-error {{cannot jump from this goto statement to label 'foo' inside an inline assembly block}} +} + +/* +// TODO: Fix the following diagnostics +void t9() { + goto foo; // expected-xxxerror {{cannot jump from this goto statement to label 'foo' inside an inline assembly block}} + __asm { + foo: // expected-xxxnote {{inline assembly label 'foo' declared here}} + mov eax, 0 + } +} +*/ + +void t10() { +// TODO: The diagnostic below has the wrong location, because we don't have a better location in CheckPoppedLabel +foo: // expected-error {{use of undeclared label 'foo'}} + __asm mov eax, foo +}