diff --git a/compiler/GHC/Builtin/Names/TH.hs b/compiler/GHC/Builtin/Names/TH.hs
index b01a4705ec029d740b48a6f720257e21ea9651f0..0ea14689274bade5b91e30779e9aea08afee2834 100644
--- a/compiler/GHC/Builtin/Names/TH.hs
+++ b/compiler/GHC/Builtin/Names/TH.hs
@@ -76,7 +76,8 @@ templateHaskellNames = [
     classDName, instanceWithOverlapDName,
     standaloneDerivWithStrategyDName, sigDName, kiSigDName, forImpDName,
     pragInlDName, pragOpaqueDName, pragSpecDName, pragSpecInlDName, pragSpecInstDName,
-    pragRuleDName, pragCompleteDName, pragAnnDName, defaultSigDName, defaultDName,
+    pragRuleDName, pragCompleteDName, pragAnnDName, pragSCCFunDName, pragSCCFunNamedDName,
+    defaultSigDName, defaultDName,
     dataFamilyDName, openTypeFamilyDName, closedTypeFamilyDName,
     dataInstDName, newtypeInstDName, tySynInstDName,
     infixLDName, infixRDName, infixNDName,
@@ -374,7 +375,8 @@ recSName    = libFun (fsLit "recS")    recSIdKey
 funDName, valDName, dataDName, newtypeDName, typeDataDName, tySynDName, classDName,
     instanceWithOverlapDName, sigDName, kiSigDName, forImpDName, pragInlDName,
     pragSpecDName, pragSpecInlDName, pragSpecInstDName, pragRuleDName,
-    pragAnnDName, standaloneDerivWithStrategyDName, defaultSigDName, defaultDName,
+    pragAnnDName, pragSCCFunDName, pragSCCFunNamedDName,
+    standaloneDerivWithStrategyDName, defaultSigDName, defaultDName,
     dataInstDName, newtypeInstDName, tySynInstDName, dataFamilyDName,
     openTypeFamilyDName, closedTypeFamilyDName, infixLDName, infixRDName,
     infixNDName, roleAnnotDName, patSynDName, patSynSigDName,
@@ -401,6 +403,8 @@ pragSpecInstDName                = libFun (fsLit "pragSpecInstD")
 pragRuleDName                    = libFun (fsLit "pragRuleD")                    pragRuleDIdKey
 pragCompleteDName                = libFun (fsLit "pragCompleteD")                pragCompleteDIdKey
 pragAnnDName                     = libFun (fsLit "pragAnnD")                     pragAnnDIdKey
+pragSCCFunDName                  = libFun (fsLit "pragSCCFunD")                  pragSCCFunDKey
+pragSCCFunNamedDName             = libFun (fsLit "pragSCCFunNamedD")             pragSCCFunNamedDKey
 dataInstDName                    = libFun (fsLit "dataInstD")                    dataInstDIdKey
 newtypeInstDName                 = libFun (fsLit "newtypeInstD")                 newtypeInstDIdKey
 tySynInstDName                   = libFun (fsLit "tySynInstD")                   tySynInstDIdKey
@@ -921,7 +925,8 @@ funDIdKey, valDIdKey, dataDIdKey, newtypeDIdKey, tySynDIdKey, classDIdKey,
     newtypeInstDIdKey, tySynInstDIdKey, standaloneDerivWithStrategyDIdKey,
     infixLDIdKey, infixRDIdKey, infixNDIdKey, roleAnnotDIdKey, patSynDIdKey,
     patSynSigDIdKey, pragCompleteDIdKey, implicitParamBindDIdKey,
-    kiSigDIdKey, defaultDIdKey, pragOpaqueDIdKey, typeDataDIdKey :: Unique
+    kiSigDIdKey, defaultDIdKey, pragOpaqueDIdKey, typeDataDIdKey,
+    pragSCCFunDKey, pragSCCFunNamedDKey :: Unique
 funDIdKey                         = mkPreludeMiscIdUnique 320
 valDIdKey                         = mkPreludeMiscIdUnique 321
 dataDIdKey                        = mkPreludeMiscIdUnique 322
@@ -958,6 +963,8 @@ kiSigDIdKey                       = mkPreludeMiscIdUnique 352
 defaultDIdKey                     = mkPreludeMiscIdUnique 353
 pragOpaqueDIdKey                  = mkPreludeMiscIdUnique 354
 typeDataDIdKey                    = mkPreludeMiscIdUnique 355
+pragSCCFunDKey                    = mkPreludeMiscIdUnique 356
+pragSCCFunNamedDKey               = mkPreludeMiscIdUnique 357
 
 -- type Cxt = ...
 cxtIdKey :: Unique
diff --git a/compiler/GHC/HsToCore/Quote.hs b/compiler/GHC/HsToCore/Quote.hs
index 0d5a5950aca29abae8e3bb5b2a034eeddc62a615..04ebe88af69359312a9769f4e0f67f84ea8a05e4 100644
--- a/compiler/GHC/HsToCore/Quote.hs
+++ b/compiler/GHC/HsToCore/Quote.hs
@@ -997,7 +997,7 @@ rep_sig (L loc (SpecSig _ nm tys ispec))
   = concatMapM (\t -> rep_specialise nm t ispec (locA loc)) tys
 rep_sig (L loc (SpecInstSig _ ty))  = rep_specialiseInst ty (locA loc)
 rep_sig (L _   (MinimalSig {}))       = notHandled ThMinimalPragmas
-rep_sig (L _   (SCCFunSig {}))        = notHandled ThSCCPragmas
+rep_sig (L loc (SCCFunSig _ nm str)) = rep_sccFun nm str (locA loc)
 rep_sig (L loc (CompleteMatchSig _ cls mty))
   = rep_complete_sig cls mty (locA loc)
 rep_sig d@(L _ (XSig {}))             = pprPanic "rep_sig IdSig" (ppr d)
@@ -1121,6 +1121,21 @@ rep_specialiseInst ty loc
        ; pragma <- repPragSpecInst ty1
        ; return [(loc, pragma)] }
 
+rep_sccFun :: LocatedN Name
+        -> Maybe (XRec GhcRn StringLiteral)
+        -> SrcSpan
+        -> MetaM [(SrcSpan, Core (M TH.Dec))]
+rep_sccFun nm Nothing loc = do
+  nm1 <- lookupLOcc nm
+  scc <- repPragSCCFun nm1
+  return [(loc, scc)]
+
+rep_sccFun nm (Just (L _ str)) loc = do
+  nm1 <- lookupLOcc nm
+  str1 <- coreStringLit (sl_fs str)
+  scc <- repPragSCCFunNamed nm1 str1
+  return [(loc, scc)]
+
 repInline :: InlineSpec -> MetaM (Core TH.Inline)
 repInline (NoInline          _ )   = dataCon noInlineDataConName
 -- There is a mismatch between the TH and GHC representation because
@@ -2687,6 +2702,12 @@ repPragRule (MkC nm) (MkC ty_bndrs) (MkC tm_bndrs) (MkC lhs) (MkC rhs) (MkC phas
 repPragAnn :: Core TH.AnnTarget -> Core (M TH.Exp) -> MetaM (Core (M TH.Dec))
 repPragAnn (MkC targ) (MkC e) = rep2 pragAnnDName [targ, e]
 
+repPragSCCFun :: Core TH.Name -> MetaM (Core (M TH.Dec))
+repPragSCCFun (MkC nm) = rep2 pragSCCFunDName [nm]
+
+repPragSCCFunNamed :: Core TH.Name -> Core String -> MetaM (Core (M TH.Dec))
+repPragSCCFunNamed (MkC nm) (MkC str) = rep2 pragSCCFunNamedDName [nm, str]
+
 repTySynInst :: Core (M TH.TySynEqn) -> MetaM (Core (M TH.Dec))
 repTySynInst (MkC eqn)
     = rep2 tySynInstDName [eqn]
diff --git a/compiler/GHC/ThToHs.hs b/compiler/GHC/ThToHs.hs
index 2fe751300b008b2be5b907c3e8fd355ea4840dbb..10f1c6c846de9a47c58bd2f790dcbb55d3710649 100644
--- a/compiler/GHC/ThToHs.hs
+++ b/compiler/GHC/ThToHs.hs
@@ -954,6 +954,12 @@ cvtPragmaD (CompleteP cls mty)
        ; mty'  <- traverse tconNameN mty
        ; returnJustLA $ Hs.SigD noExtField
                    $ CompleteMatchSig (noAnn, NoSourceText) cls' mty' }
+cvtPragmaD (SCCP nm str) = do
+  nm' <- vcNameN nm
+  str' <- traverse (\s ->
+    returnLA $ StringLiteral NoSourceText (mkFastString s) Nothing) str
+  returnJustLA $ Hs.SigD noExtField
+    $ SCCFunSig (noAnn, SourceText $ fsLit "{-# SCC") nm' str'
 
 dfltActivation :: TH.Inline -> Activation
 dfltActivation TH.NoInline = NeverActive
diff --git a/libraries/template-haskell/Language/Haskell/TH/Lib/Internal.hs b/libraries/template-haskell/Language/Haskell/TH/Lib/Internal.hs
index 4da31ac144dd896d24b79d579b1275bea7d5374c..66192bdceda1eed91bfc5bf7afc370b8ddd677d8 100644
--- a/libraries/template-haskell/Language/Haskell/TH/Lib/Internal.hs
+++ b/libraries/template-haskell/Language/Haskell/TH/Lib/Internal.hs
@@ -553,6 +553,12 @@ pragLineD line file = pure $ PragmaD $ LineP line file
 pragCompleteD :: Quote m => [Name] -> Maybe Name -> m Dec
 pragCompleteD cls mty = pure $ PragmaD $ CompleteP cls mty
 
+pragSCCFunD :: Quote m => Name -> m Dec
+pragSCCFunD nm = pure $ PragmaD $ SCCP nm Nothing
+
+pragSCCFunNamedD :: Quote m => Name -> String -> m Dec
+pragSCCFunNamedD nm str = pure $ PragmaD $ SCCP nm (Just str)
+
 dataInstD :: Quote m => m Cxt -> (Maybe [m (TyVarBndr ())]) -> m Type -> Maybe (m Kind) -> [m Con]
           -> [m DerivClause] -> m Dec
 dataInstD ctxt mb_bndrs ty ksig cons derivs =
diff --git a/libraries/template-haskell/Language/Haskell/TH/Ppr.hs b/libraries/template-haskell/Language/Haskell/TH/Ppr.hs
index 65be9831770dab78bbfba6750d7d1df54fee2177..9f138fdf916d02049583fe2a8b361508cab952c4 100644
--- a/libraries/template-haskell/Language/Haskell/TH/Ppr.hs
+++ b/libraries/template-haskell/Language/Haskell/TH/Ppr.hs
@@ -658,6 +658,8 @@ instance Ppr Pragma where
     ppr (CompleteP cls mty)
        = text "{-# COMPLETE" <+> (fsep $ punctuate comma $ map (pprName' Applied) cls)
                 <+> maybe empty (\ty -> dcolon <+> pprName' Applied ty) mty <+> text "#-}"
+    ppr (SCCP nm str)
+       = text "{-# SCC" <+> pprName' Applied nm <+> maybe empty text str <+> text "#-}"
 
 ------------------------------
 instance Ppr Inline where
diff --git a/libraries/template-haskell/Language/Haskell/TH/Syntax.hs b/libraries/template-haskell/Language/Haskell/TH/Syntax.hs
index 95f1b7fa171c38f78505e6a869c94fdc54ee024f..1e3859949fe2b46cd504f9ad2575d59c20b1d853 100644
--- a/libraries/template-haskell/Language/Haskell/TH/Syntax.hs
+++ b/libraries/template-haskell/Language/Haskell/TH/Syntax.hs
@@ -2629,6 +2629,8 @@ data Pragma = InlineP         Name Inline RuleMatch Phases
             | LineP           Int String
             | CompleteP       [Name] (Maybe Name)
                 -- ^ @{ {\-\# COMPLETE C_1, ..., C_i [ :: T ] \#-} }@
+            | SCCP            Name (Maybe String)
+                -- ^ @{ {\-\# SCC fun "optional_name" \#-} }@
         deriving( Show, Eq, Ord, Data, Generic )
 
 data Inline = NoInline
diff --git a/libraries/template-haskell/changelog.md b/libraries/template-haskell/changelog.md
index 0efa706750fa6e457bf4baf861e0123ab332047a..9c33cb7971fba2d5e970be1a67d20263cd4448b6 100644
--- a/libraries/template-haskell/changelog.md
+++ b/libraries/template-haskell/changelog.md
@@ -8,6 +8,8 @@
   * Extend `Pat` with `TypeP` and `Exp` with `TypeE`,
     introduce functions `typeP` and `typeE` (GHC Proposal #281).
 
+  * Extend `Pragma` with `SCCP`.
+
 ## 2.21.0.0
 
   * Record fields now belong to separate `NameSpace`s, keyed by the parent of
diff --git a/testsuite/tests/th/should_compile/T24081/Main.hs b/testsuite/tests/th/should_compile/T24081/Main.hs
new file mode 100644
index 0000000000000000000000000000000000000000..a92589ef2df445a7c0550120404c0d646c609d02
--- /dev/null
+++ b/testsuite/tests/th/should_compile/T24081/Main.hs
@@ -0,0 +1,11 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+import TH
+
+x
+y
+a = 1
+b = 1
+gen
+
+main = return ()
diff --git a/testsuite/tests/th/should_compile/T24081/Makefile b/testsuite/tests/th/should_compile/T24081/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..1c39d1c1fed04f14cab76d17579c1e97343590f0
--- /dev/null
+++ b/testsuite/tests/th/should_compile/T24081/Makefile
@@ -0,0 +1,3 @@
+TOP=../../../..
+include $(TOP)/mk/boilerplate.mk
+include $(TOP)/mk/test.mk
diff --git a/testsuite/tests/th/should_compile/T24081/T24081.stderr b/testsuite/tests/th/should_compile/T24081/T24081.stderr
new file mode 100644
index 0000000000000000000000000000000000000000..b39eec7244b9d50bb1d0ad1d5d58cfc03d962c66
--- /dev/null
+++ b/testsuite/tests/th/should_compile/T24081/T24081.stderr
@@ -0,0 +1,15 @@
+Main.hs:5:1: Splicing declarations
+    x
+  ======>
+    {-# SCC f #-}
+    f = 1
+Main.hs:6:1: Splicing declarations
+    y
+  ======>
+    {-# SCC g custom_name_g #-}
+    g = 1
+Main.hs:9:1-3: Splicing declarations
+    gen
+  ======>
+    {-# SCC a #-}
+    {-# SCC b custom_name_b #-}
diff --git a/testsuite/tests/th/should_compile/T24081/TH.hs b/testsuite/tests/th/should_compile/T24081/TH.hs
new file mode 100644
index 0000000000000000000000000000000000000000..95c8120854ced83e39edc1e2ca2aa9d8a541ea19
--- /dev/null
+++ b/testsuite/tests/th/should_compile/T24081/TH.hs
@@ -0,0 +1,18 @@
+{-# LANGUAGE TemplateHaskell #-}
+module TH where
+
+import Data.Maybe
+import Language.Haskell.TH
+
+x, y :: Q [Dec]
+x = [d|{-# SCC f #-}; f = 1|]
+y = [d|{-# SCC g "custom_name_g" #-}; g = 1|]
+
+gen :: Q [Dec]
+gen = do
+  a <- fromJust <$> lookupValueName "a"
+  b <- fromJust <$> lookupValueName "b"
+  pure
+    [ PragmaD $ SCCP a Nothing
+    , PragmaD $ SCCP b (Just "custom_name_b")
+    ]
diff --git a/testsuite/tests/th/should_compile/T24081/all.T b/testsuite/tests/th/should_compile/T24081/all.T
new file mode 100644
index 0000000000000000000000000000000000000000..87da13cd0f4925c247989e45192416283e578aaa
--- /dev/null
+++ b/testsuite/tests/th/should_compile/T24081/all.T
@@ -0,0 +1,2 @@
+test('T24081', [extra_files(['TH.hs', 'Main.hs']), req_th],
+	      multimod_compile, ['TH Main', '-v0 -ddump-splices -dsuppress-uniques'])