Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -4524,7 +4524,9 @@
   StmtResult ActOnDefaultStmt(SourceLocation DefaultLoc,
                                       SourceLocation ColonLoc,
                                       Stmt *SubStmt, Scope *CurScope);
-  StmtResult ActOnLabelStmt(SourceLocation IdentLoc, LabelDecl *TheDecl,
+  StmtResult ActOnLabelStmt(SourceLocation IdentLoc,
+                            const ParsedAttributesView *Attrs,
+                            SourceRange AttrsRange, LabelDecl *TheDecl,
                             SourceLocation ColonLoc, Stmt *SubStmt);
 
   StmtResult ActOnAttributedStmt(SourceLocation AttrLoc,
Index: clang/lib/Parse/ParseStmt.cpp
===================================================================
--- clang/lib/Parse/ParseStmt.cpp
+++ clang/lib/Parse/ParseStmt.cpp
@@ -666,10 +666,19 @@
   LabelDecl *LD = Actions.LookupOrCreateLabel(IdentTok.getIdentifierInfo(),
                                               IdentTok.getLocation());
   Actions.ProcessDeclAttributeList(Actions.CurScope, LD, attrs);
+
+  ParsedAttributes StmtAttrs(AttrFactory);
+  for (size_t I = 0; I < attrs.size();) {
+    if (attrs[I].isStmtAttr())
+      StmtAttrs.takeOneFrom(attrs, &attrs[I]);
+    else
+      ++I;
+  }
+  SourceRange AttributeRange = attrs.Range;
   attrs.clear();
 
-  return Actions.ActOnLabelStmt(IdentTok.getLocation(), LD, ColonLoc,
-                                SubStmt.get());
+  return Actions.ActOnLabelStmt(IdentTok.getLocation(), &StmtAttrs,
+                                AttributeRange, LD, ColonLoc, SubStmt.get());
 }
 
 /// ParseCaseStatement
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -7363,8 +7363,11 @@
       assert(AL.isTypeAttr() && "Non-type attribute not handled");
       break;
     }
-    S.Diag(AL.getLoc(), diag::err_stmt_attribute_invalid_on_decl)
-        << AL << D->getLocation();
+
+    // The statement attributes attached to a LabelDecl are handled separately.
+    if (!isa<LabelDecl>(D))
+      S.Diag(AL.getLoc(), diag::err_stmt_attribute_invalid_on_decl)
+          << AL << D->getLocation();
     break;
   case ParsedAttr::AT_Interrupt:
     handleInterruptAttr(S, D, AL);
Index: clang/lib/Sema/SemaStmt.cpp
===================================================================
--- clang/lib/Sema/SemaStmt.cpp
+++ clang/lib/Sema/SemaStmt.cpp
@@ -531,9 +531,10 @@
   return DS;
 }
 
-StmtResult
-Sema::ActOnLabelStmt(SourceLocation IdentLoc, LabelDecl *TheDecl,
-                     SourceLocation ColonLoc, Stmt *SubStmt) {
+StmtResult Sema::ActOnLabelStmt(SourceLocation IdentLoc,
+                                const ParsedAttributesView *Attrs,
+                                SourceRange AttrsRange, LabelDecl *TheDecl,
+                                SourceLocation ColonLoc, Stmt *SubStmt) {
   // If the label was multiply defined, reject it now.
   if (TheDecl->getStmt()) {
     Diag(IdentLoc, diag::err_redefinition_of_label) << TheDecl->getDeclName();
@@ -552,6 +553,9 @@
       TheDecl->setLocation(IdentLoc);
     }
   }
+  if (Attrs && !Attrs->empty())
+    return ProcessStmtAttributes(LS, *Attrs, AttrsRange);
+
   return LS;
 }
 
Index: clang/lib/Sema/TreeTransform.h
===================================================================
--- clang/lib/Sema/TreeTransform.h
+++ clang/lib/Sema/TreeTransform.h
@@ -1302,7 +1302,10 @@
   /// Subclasses may override this routine to provide different behavior.
   StmtResult RebuildLabelStmt(SourceLocation IdentLoc, LabelDecl *L,
                               SourceLocation ColonLoc, Stmt *SubStmt) {
-    return SemaRef.ActOnLabelStmt(IdentLoc, L, ColonLoc, SubStmt);
+    // The attributes are already processed in template declaration and are
+    // already properly rebuild. Only the LabelStmt itself needs to be rebuild.
+    return SemaRef.ActOnLabelStmt(IdentLoc, nullptr, SourceLocation(), L,
+                                  ColonLoc, SubStmt);
   }
 
   /// Build a new attributed statement.
Index: clang/test/AST/ast-dump-attr.cpp
===================================================================
--- clang/test/AST/ast-dump-attr.cpp
+++ clang/test/AST/ast-dump-attr.cpp
@@ -113,6 +113,35 @@
 N: __attribute(()) ;
 // CHECK: LabelStmt {{.*}} 'N'
 // CHECK-NEXT: NullStmt
+
+[[likely]] O:;
+// CHECK: AttributedStmt
+// CHECK-NEXT: LikelyAttr
+// CHECK-NEXT: LabelStmt {{.*}} 'O'
+
+[[likely]] P: __attribute__((unused)) int k;
+// CHECK: AttributedStmt
+// CHECK-NEXT: LikelyAttr
+// CHECK-NEXT: LabelStmt {{.*}} 'P'
+// CHECK: VarDecl{{.*}} k 'int'
+// CHECK-NEXT: UnusedAttr{{.*}}
+}
+
+template<class T>
+T TestLabelTemplate() {
+[[likely]] A: return T();
+// CHECK: FunctionTemplateDecl {{.*}} TestLabelTemplate
+// CHECK: AttributedStmt
+// CHECK-NEXT: LikelyAttr
+// CHECK-NEXT: LabelStmt {{.*}} 'A'
+}
+
+void TestLabelTemplateInstantiate() {
+TestLabelTemplate<int>();
+// CHECK: FunctionDecl {{.*}} TestLabelTemplate 'int ()'
+// CHECK: AttributedStmt
+// CHECK-NEXT: LikelyAttr
+// CHECK-NEXT: LabelStmt {{.*}} 'A'
 }
 
 namespace Test {
Index: clang/test/Sema/attr-likelihood.c
===================================================================
--- clang/test/Sema/attr-likelihood.c
+++ clang/test/Sema/attr-likelihood.c
@@ -43,8 +43,7 @@
   if (x)
     goto lbl;
 
-  // FIXME: allow the attribute on the label
-  [[clang::unlikely]] lbl : // expected-error {{'unlikely' attribute cannot be applied to a declaration}}
+  [[clang::unlikely]] lbl :
   [[clang::likely]] x = x + 1;
 
   [[clang::likely]]++ x;
Index: clang/test/Sema/attr-nomerge.cpp
===================================================================
--- clang/test/Sema/attr-nomerge.cpp
+++ clang/test/Sema/attr-nomerge.cpp
@@ -8,7 +8,7 @@
   int x;
   [[clang::nomerge]] x = 10; // expected-warning {{nomerge attribute is ignored because there exists no call expression inside the statement}}
 
-  [[clang::nomerge]] label: bar(); // expected-error {{'nomerge' attribute cannot be applied to a declaration}}
+  [[clang::nomerge]] label: bar();
 
 }
 
Index: clang/test/SemaCXX/attr-likelihood.cpp
===================================================================
--- clang/test/SemaCXX/attr-likelihood.cpp
+++ clang/test/SemaCXX/attr-likelihood.cpp
@@ -115,8 +115,7 @@
   if (x)
     goto lbl;
 
-  // FIXME: allow the attribute on the label
-  [[unlikely]] lbl : // expected-error {{'unlikely' attribute cannot be applied to a declaration}}
+  [[unlikely]] lbl :
                      [[likely]] x = x + 1;
 
   [[likely]]++ x;
@@ -148,4 +147,14 @@
   // expected-warning@+1 {{attribute 'likely' has no effect when annotating an 'if constexpr' statement}}
   } else [[likely]];
 }
+
+void p()
+{
+// Make sure the attributes aren't processed as statement attributes.
+[[likely]] __label__ label; // expected-error {{expected expression}}
+__label__ [[likely]] label; // expected-error {{expected expression}}
+__label__ label [[likely]]; // expected-error {{expected expression}}
+
+[[likely]] label: __attribute__((unused)) int i;
+}
 #endif