diff --git a/compiler/GHC/Cmm/ThreadSanitizer.hs b/compiler/GHC/Cmm/ThreadSanitizer.hs
index 22ffd353a8d65e3603a17baf61719da0f5ba73e1..ad33bb7f6cdff1754e6ae63233384e0961b05341 100644
--- a/compiler/GHC/Cmm/ThreadSanitizer.hs
+++ b/compiler/GHC/Cmm/ThreadSanitizer.hs
@@ -145,7 +145,7 @@ mkUnsafeCall env ftgt formals args =
     -- arguments as Cmm-Lint checks this. To accomplish this we instead bind
     -- the arguments to local registers.
     arg_regs :: [CmmReg]
-    arg_regs = zipWith arg_reg (uniqsFromSupply arg_us) args
+    arg_regs = zipWith arg_reg (uniqListFromSupply arg_us) args
       where
         arg_reg :: Unique -> CmmExpr -> CmmReg
         arg_reg u expr = CmmLocal $ LocalReg u (cmmExprType (platform env) expr)
@@ -169,7 +169,7 @@ saveRestoreCallerRegs us platform =
 
     nodes :: [(CmmNode O O, CmmNode O O)]
     nodes =
-        zipWith mk_reg regs_to_save (uniqsFromSupply us)
+        zipWith mk_reg regs_to_save (uniqListFromSupply us)
       where
         mk_reg :: GlobalReg -> Unique -> (CmmNode O O, CmmNode O O)
         mk_reg reg u =
diff --git a/compiler/GHC/CmmToAsm/AArch64/CodeGen.hs b/compiler/GHC/CmmToAsm/AArch64/CodeGen.hs
index e3ca72f0fcdcfa21504abf993ddbf0d02c1b448c..6d642c560a5cc266dfe4ab62332d0193804922a3 100644
--- a/compiler/GHC/CmmToAsm/AArch64/CodeGen.hs
+++ b/compiler/GHC/CmmToAsm/AArch64/CodeGen.hs
@@ -1,5 +1,6 @@
 {-# language GADTs, LambdaCase #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE LambdaCase #-}
 module GHC.CmmToAsm.AArch64.CodeGen (
       cmmTopCodeGen
     , generateJumpTableForInstr
@@ -50,7 +51,9 @@ import GHC.Types.Unique.DSM
 import GHC.Data.OrdList
 import GHC.Utils.Outputable
 
-import Control.Monad    ( mapAndUnzipM )
+import Control.Monad    ( join, mapAndUnzipM )
+import Data.List.NonEmpty ( NonEmpty (..), nonEmpty )
+import qualified Data.List.NonEmpty as NE
 import GHC.Float
 
 import GHC.Types.Basic
@@ -1587,7 +1590,7 @@ genCondJump bid expr = do
       _ -> pprPanic "AArch64.genCondJump: " (text $ show expr)
 
 -- A conditional jump with at least +/-128M jump range
-genCondFarJump :: MonadGetUnique m => Cond -> Target -> m InstrBlock
+genCondFarJump :: MonadGetUnique m => Cond -> Target -> m (NonEmpty Instr)
 genCondFarJump cond far_target = do
   skip_lbl_id <- newBlockId
   jmp_lbl_id <- newBlockId
@@ -1597,11 +1600,13 @@ genCondFarJump cond far_target = do
   -- need to consider float orderings.
   -- So we take the hit of the additional jump in the false
   -- case for now.
-  return $ toOL [ BCOND cond (TBlock jmp_lbl_id)
-                , B (TBlock skip_lbl_id)
-                , NEWBLOCK jmp_lbl_id
-                , B far_target
-                , NEWBLOCK skip_lbl_id]
+  pure
+    ( BCOND cond (TBlock jmp_lbl_id) :|
+      B (TBlock skip_lbl_id) :
+      NEWBLOCK jmp_lbl_id :
+      B far_target :
+      NEWBLOCK skip_lbl_id :
+      [] )
 
 genCondBranch :: BlockId      -- the true branch target
     -> BlockId      -- the false branch target
@@ -2457,48 +2462,49 @@ makeFarBranches {- only used when debugging -} _platform statics basic_blocks =
 
     -- Replace out of range conditional jumps with unconditional jumps.
     replace_blk :: LabelMap Int -> Int -> GenBasicBlock Instr -> UniqDSM (Int, [GenBasicBlock Instr])
-    replace_blk !m !pos (BasicBlock lbl instrs) = do
-      -- Account for a potential info table before the label.
-      let !block_pos = pos + infoTblSize_maybe lbl
-      (!pos', instrs') <- mapAccumLM (replace_jump m) block_pos instrs
-      let instrs'' = concat instrs'
-      -- We might have introduced new labels, so split the instructions into basic blocks again if neccesary.
-      let (top, split_blocks, no_data) = foldr mkBlocks ([],[],[]) instrs''
-      -- There should be no data in the instruction stream at this point
-      massert (null no_data)
-
-      let final_blocks = BasicBlock lbl top : split_blocks
-      pure (pos', final_blocks)
-
-    replace_jump :: LabelMap Int -> Int -> Instr -> UniqDSM (Int, [Instr])
+    replace_blk !m !pos (BasicBlock lbl instrs) = case nonEmpty instrs of
+      Nothing -> pure (0, [])
+      Just instrs -> do
+        -- Account for a potential info table before the label.
+        let !block_pos = pos + infoTblSize_maybe lbl
+        (!pos', instrs') <- mapAccumLM (replace_jump m) block_pos instrs
+        let instrs'' = join instrs'
+        -- We might have introduced new labels, so split the instructions into basic blocks again if neccesary.
+        let (top, split_blocks, no_data) = foldr mkBlocks ([],[],[]) instrs''
+        -- There should be no data in the instruction stream at this point
+        massert (null no_data)
+
+        let final_blocks = BasicBlock lbl top : split_blocks
+        pure (pos', final_blocks)
+
+    replace_jump :: LabelMap Int -> Int -> Instr -> UniqDSM (Int, NonEmpty Instr)
     replace_jump !m !pos instr = do
       case instr of
         ANN ann instr -> do
-          replace_jump m pos instr >>= \case
-            (idx,instr':instrs') ->
-              pure (idx, ANN ann instr':instrs')
-            (idx,[]) -> pprPanic "replace_jump" (text "empty return list for " <+> ppr idx)
+          replace_jump m pos instr >>= \
+            (idx,instr':|instrs') ->
+              pure (idx, ANN ann instr':|instrs')
         BCOND cond t
           -> case target_in_range m t pos of
-              InRange -> pure (pos+long_bc_jump_size,[instr])
+              InRange -> pure (pos+long_bc_jump_size, NE.singleton instr)
               NotInRange far_target -> do
                 jmp_code <- genCondFarJump cond far_target
-                pure (pos+long_bc_jump_size, fromOL jmp_code)
+                pure (pos+long_bc_jump_size, jmp_code)
         CBZ op t -> long_zero_jump op t EQ
         CBNZ op t -> long_zero_jump op t NE
         instr
-          | isMetaInstr instr -> pure (pos,[instr])
-          | otherwise -> pure (pos+1, [instr])
+          | isMetaInstr instr -> pure (pos, NE.singleton instr)
+          | otherwise -> pure (pos+1, NE.singleton instr)
 
       where
         -- cmp_op: EQ = CBZ, NEQ = CBNZ
         long_zero_jump op t cmp_op =
           case target_in_range m t pos of
-              InRange -> pure (pos+long_bz_jump_size,[instr])
+              InRange -> pure (pos+long_bz_jump_size, NE.singleton instr)
               NotInRange far_target -> do
                 jmp_code <- genCondFarJump cmp_op far_target
                 -- TODO: Fix zero reg so we can use it here
-                pure (pos + long_bz_jump_size, CMP op (OpImm (ImmInt 0)) : fromOL jmp_code)
+                pure (pos + long_bz_jump_size, CMP op (OpImm (ImmInt 0)) NE.<| jmp_code)
 
 
     target_in_range :: LabelMap Int -> Target -> Int -> BlockInRange
diff --git a/compiler/GHC/Core/Opt/SetLevels.hs b/compiler/GHC/Core/Opt/SetLevels.hs
index 8948cc209579fefcf9fc602727ad98f80f87b9ea..6e3cb64d2e595e4b7b590510cf14001def2bb2ed 100644
--- a/compiler/GHC/Core/Opt/SetLevels.hs
+++ b/compiler/GHC/Core/Opt/SetLevels.hs
@@ -1,7 +1,5 @@
 {-# LANGUAGE PatternSynonyms #-}
 
-{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
-
 {-
 (c) The GRASP/AQUA Project, Glasgow University, 1992-1998
 
@@ -120,12 +118,16 @@ import GHC.Builtin.Types
 import GHC.Builtin.Names      ( runRWKey )
 
 import GHC.Data.FastString
+import GHC.Data.Pair ( Pair (..) )
 
 import GHC.Utils.FV
 import GHC.Utils.Misc
 import GHC.Utils.Outputable
 import GHC.Utils.Panic
 
+import Data.Foldable ( toList )
+import Data.Functor.Identity ( Identity (..) )
+import Data.List.NonEmpty ( NonEmpty (..) )
 import Data.Maybe
 
 {-
@@ -451,14 +453,14 @@ lvlCase env scrut_fvs scrut' case_bndr ty alts
   , ManyTy <- idMult case_bndr     -- See Note [Floating linear case]
   =     -- Always float the case if possible
         -- Unlike lets we don't insist that it escapes a value lambda
-    do { (env1, (case_bndr' : bs')) <- cloneCaseBndrs env dest_lvl (case_bndr : bs)
+    do { (env1, case_bndr' :| bs') <- cloneCaseBndrs env dest_lvl (case_bndr :| bs)
        ; let rhs_env = extendCaseBndrEnv env1 case_bndr scrut'
        ; body' <- lvlMFE rhs_env True body
        ; let alt' = Alt con (map (stayPut dest_lvl) bs') body'
        ; return (Case scrut' (TB case_bndr' (FloatMe dest_lvl)) ty' [alt']) }
 
   | otherwise     -- Stays put
-  = do { let (alts_env1, [case_bndr']) = substAndLvlBndrs NonRecursive env incd_lvl [case_bndr]
+  = do { let (alts_env1, Identity case_bndr') = substAndLvlBndrs NonRecursive env incd_lvl (Identity case_bndr)
              alts_env = extendCaseBndrEnv alts_env1 case_bndr scrut'
        ; alts' <- mapM (lvl_alt alts_env) alts
        ; return (Case scrut' case_bndr' ty' alts') }
@@ -649,7 +651,7 @@ lvlMFE env strict_ctxt ann_expr
                          -- See Note [Test cheapness with exprOkForSpeculation]
   , BI_Box { bi_data_con = box_dc, bi_inst_con = boxing_expr
            , bi_boxed_type = box_ty } <- boxingDataCon expr_ty
-  , let [bx_bndr, ubx_bndr] = mkTemplateLocals [box_ty, expr_ty]
+  , let Pair bx_bndr ubx_bndr = mkTemplateLocals (Pair box_ty expr_ty)
   = do { expr1 <- lvlExpr rhs_env ann_expr
        ; let l1r       = incMinorLvlFrom rhs_env
              float_rhs = mkLams abs_vars_w_lvls $
@@ -1227,7 +1229,7 @@ lvlBind env (AnnNonRec bndr rhs)
   = -- No float
     do { rhs' <- lvlRhs env NonRecursive is_bot_lam mb_join_arity rhs
        ; let  bind_lvl        = incMinorLvl (le_ctxt_lvl env)
-              (env', [bndr']) = substAndLvlBndrs NonRecursive env bind_lvl [bndr]
+              (env', Identity bndr') = substAndLvlBndrs NonRecursive env bind_lvl (Identity bndr)
        ; return (NonRec bndr' rhs', env') }
 
   -- Otherwise we are going to float
@@ -1235,7 +1237,7 @@ lvlBind env (AnnNonRec bndr rhs)
   = do {  -- No type abstraction; clone existing binder
          rhs' <- lvlFloatRhs [] dest_lvl env NonRecursive
                              is_bot_lam NotJoinPoint rhs
-       ; (env', [bndr']) <- cloneLetVars NonRecursive env dest_lvl [bndr]
+       ; (env', Identity bndr') <- cloneLetVars NonRecursive env dest_lvl (Identity bndr)
        ; let bndr2 = annotateBotStr bndr' 0 mb_bot_str
        ; return (NonRec (TB bndr2 (FloatMe dest_lvl)) rhs', env') }
 
@@ -1243,7 +1245,7 @@ lvlBind env (AnnNonRec bndr rhs)
   = do {  -- Yes, type abstraction; create a new binder, extend substitution, etc
          rhs' <- lvlFloatRhs abs_vars dest_lvl env NonRecursive
                              is_bot_lam NotJoinPoint rhs
-       ; (env', [bndr']) <- newPolyBndrs dest_lvl env abs_vars [bndr]
+       ; (env', Identity bndr') <- newPolyBndrs dest_lvl env abs_vars (Identity bndr)
        ; let bndr2 = annotateBotStr bndr' n_extra mb_bot_str
        ; return (NonRec (TB bndr2 (FloatMe dest_lvl)) rhs', env') }
 
@@ -1301,13 +1303,13 @@ lvlBind env (AnnRec pairs)
     let (rhs_env, abs_vars_w_lvls) = lvlLamBndrs env dest_lvl abs_vars
         rhs_lvl = le_ctxt_lvl rhs_env
 
-    (rhs_env', [new_bndr]) <- cloneLetVars Recursive rhs_env rhs_lvl [bndr]
+    (rhs_env', Identity new_bndr) <- cloneLetVars Recursive rhs_env rhs_lvl (Identity bndr)
     let
         (lam_bndrs, rhs_body)   = collectAnnBndrs rhs
         (body_env1, lam_bndrs1) = substBndrsSL NonRecursive rhs_env' lam_bndrs
         (body_env2, lam_bndrs2) = lvlLamBndrs body_env1 rhs_lvl lam_bndrs1
     new_rhs_body <- lvlRhs body_env2 Recursive is_bot NotJoinPoint rhs_body
-    (poly_env, [poly_bndr]) <- newPolyBndrs dest_lvl env abs_vars [bndr]
+    (poly_env, Identity poly_bndr) <- newPolyBndrs dest_lvl env abs_vars (Identity bndr)
     return (Rec [(TB poly_bndr (FloatMe dest_lvl)
                  , mkLams abs_vars_w_lvls $
                    mkLams lam_bndrs2 $
@@ -1479,24 +1481,26 @@ Use lvlExpr otherwise.  A little subtle, and I got it wrong at least twice
 ************************************************************************
 -}
 
-substAndLvlBndrs :: RecFlag -> LevelEnv -> Level -> [InVar] -> (LevelEnv, [LevelledBndr])
+substAndLvlBndrs :: Traversable f => RecFlag -> LevelEnv -> Level -> f InVar -> (LevelEnv, f LevelledBndr)
 substAndLvlBndrs is_rec env lvl bndrs
   = lvlBndrs subst_env lvl subst_bndrs
   where
     (subst_env, subst_bndrs) = substBndrsSL is_rec env bndrs
+{-# INLINE substAndLvlBndrs #-}
 
-substBndrsSL :: RecFlag -> LevelEnv -> [InVar] -> (LevelEnv, [OutVar])
+substBndrsSL :: Traversable f => RecFlag -> LevelEnv -> f InVar -> (LevelEnv, f OutVar)
 -- So named only to avoid the name clash with GHC.Core.Subst.substBndrs
 substBndrsSL is_rec env@(LE { le_subst = subst, le_env = id_env }) bndrs
   = ( env { le_subst    = subst'
-          , le_env      = foldl' add_id  id_env (bndrs `zip` bndrs') }
+          , le_env      = foldl' add_id  id_env (toList bndrs `zip` toList bndrs') }
     , bndrs')
   where
     (subst', bndrs') = case is_rec of
                          NonRecursive -> substBndrs    subst bndrs
                          Recursive    -> substRecBndrs subst bndrs
+{-# INLINE substBndrsSL #-}
 
-lvlLamBndrs :: LevelEnv -> Level -> [OutVar] -> (LevelEnv, [LevelledBndr])
+lvlLamBndrs :: Traversable f => LevelEnv -> Level -> f OutVar -> (LevelEnv, f LevelledBndr)
 -- Compute the levels for the binders of a lambda group
 lvlLamBndrs env lvl bndrs
   = lvlBndrs env new_lvl bndrs
@@ -1510,17 +1514,18 @@ lvlLamBndrs env lvl bndrs
        -- true of a type variable -- there is no point in floating
        -- out of a big lambda.
        -- See Note [Computing one-shot info] in GHC.Types.Demand
+{-# INLINE lvlLamBndrs #-}
 
-lvlJoinBndrs :: LevelEnv -> Level -> RecFlag -> [OutVar]
-             -> (LevelEnv, [LevelledBndr])
-lvlJoinBndrs env lvl rec bndrs
-  = lvlBndrs env new_lvl bndrs
+lvlJoinBndrs :: Traversable f => LevelEnv -> Level -> RecFlag -> f OutVar
+             -> (LevelEnv, f LevelledBndr)
+lvlJoinBndrs env lvl rec = lvlBndrs env new_lvl
   where
     new_lvl | isRec rec = incMajorLvl lvl
             | otherwise = incMinorLvl lvl
       -- Non-recursive join points are one-shot; recursive ones are not
+{-# INLINE lvlJoinBndrs #-}
 
-lvlBndrs :: LevelEnv -> Level -> [CoreBndr] -> (LevelEnv, [LevelledBndr])
+lvlBndrs :: Traversable f => LevelEnv -> Level -> f CoreBndr -> (LevelEnv, f LevelledBndr)
 -- The binders returned are exactly the same as the ones passed,
 -- apart from applying the substitution, but they are now paired
 -- with a (StayPut level)
@@ -1533,7 +1538,8 @@ lvlBndrs :: LevelEnv -> Level -> [CoreBndr] -> (LevelEnv, [LevelledBndr])
 lvlBndrs env@(LE { le_lvl_env = lvl_env }) new_lvl bndrs
   = ( env { le_ctxt_lvl = new_lvl
           , le_lvl_env  = addLvls new_lvl lvl_env bndrs }
-    , map (stayPut new_lvl) bndrs)
+    , fmap (stayPut new_lvl) bndrs)
+{-# INLINE lvlBndrs #-}
 
 stayPut :: Level -> OutVar -> LevelledBndr
 stayPut new_lvl bndr = TB bndr (StayPut new_lvl)
@@ -1693,8 +1699,8 @@ initialEnv float_lams binds
 addLvl :: Level -> VarEnv Level -> OutVar -> VarEnv Level
 addLvl dest_lvl env v' = extendVarEnv env v' dest_lvl
 
-addLvls :: Level -> VarEnv Level -> [OutVar] -> VarEnv Level
-addLvls dest_lvl env vs = foldl' (addLvl dest_lvl) env vs
+addLvls :: Foldable f => Level -> VarEnv Level -> f OutVar -> VarEnv Level
+addLvls = foldl' . addLvl
 
 floatLams :: LevelEnv -> Maybe Int
 floatLams le = floatOutLambdas (le_switches le)
@@ -1792,17 +1798,15 @@ type LvlM result = UniqSM result
 initLvl :: UniqSupply -> UniqSM a -> a
 initLvl = initUs_
 
-newPolyBndrs :: Level -> LevelEnv -> [OutVar] -> [InId]
-             -> LvlM (LevelEnv, [OutId])
+newPolyBndrs :: (MonadUnique m, Traversable t) => Level -> LevelEnv -> [OutVar] -> t InId -> m (LevelEnv, t OutId)
 -- The envt is extended to bind the new bndrs to dest_lvl, but
 -- the le_ctxt_lvl is unaffected
 newPolyBndrs dest_lvl
              env@(LE { le_lvl_env = lvl_env, le_subst = subst, le_env = id_env })
              abs_vars bndrs
  = assert (all (not . isCoVar) bndrs) $   -- What would we add to the CoSubst in this case. No easy answer.
-   do { uniqs <- getUniquesM
-      ; let new_bndrs = zipWith mk_poly_bndr bndrs uniqs
-            bndr_prs  = bndrs `zip` new_bndrs
+   do { bndr_prs <- withUniquesM (\ uniq bndr -> (bndr, mk_poly_bndr bndr uniq)) bndrs
+      ; let new_bndrs = fmap snd bndr_prs
             env' = env { le_lvl_env = addLvls dest_lvl lvl_env new_bndrs
                        , le_subst   = foldl' add_subst subst   bndr_prs
                        , le_env     = foldl' add_id    id_env  bndr_prs }
@@ -1828,6 +1832,10 @@ newPolyBndrs dest_lvl
       = new_bndr `asJoinId` join_arity + length abs_vars
       | otherwise
       = new_bndr
+{-# SPECIALIZE newPolyBndrs :: (MonadUnique m) => Level -> LevelEnv -> [OutVar] -> [InId] -> m (LevelEnv, [OutId]) #-}
+{-# SPECIALIZE newPolyBndrs :: (MonadUnique m) => Level -> LevelEnv -> [OutVar] -> Identity InId -> m (LevelEnv, Identity OutId) #-}
+{-# SPECIALIZE newPolyBndrs :: (MonadUnique m) => Level -> LevelEnv -> [OutVar] -> NonEmpty InId -> m (LevelEnv, NonEmpty OutId) #-}
+{-# SPECIALIZE newPolyBndrs :: (MonadUnique m) => Level -> LevelEnv -> [OutVar] -> Pair InId -> m (LevelEnv, Pair OutId) #-}
 
 newLvlVar :: LevelledExpr        -- The RHS of the new binding
           -> JoinPointHood       -- Its join arity, if it is a join point
@@ -1851,7 +1859,7 @@ newLvlVar lvld_rhs join_arity_maybe is_mk_static
       = mkSysLocal (mkFastString "lvl") uniq ManyTy rhs_ty
 
 -- | Clone the binders bound by a single-alternative case.
-cloneCaseBndrs :: LevelEnv -> Level -> [Var] -> LvlM (LevelEnv, [Var])
+cloneCaseBndrs :: Traversable t => LevelEnv -> Level -> t Var -> LvlM (LevelEnv, t Var)
 cloneCaseBndrs env@(LE { le_subst = subst, le_lvl_env = lvl_env, le_env = id_env })
                new_lvl vs
   = do { (subst', vs') <- cloneBndrsM subst vs
@@ -1860,12 +1868,11 @@ cloneCaseBndrs env@(LE { le_subst = subst, le_lvl_env = lvl_env, le_env = id_env
              -- See Note [Setting levels when floating single-alternative cases].
        ; let env' = env { le_lvl_env   = addLvls new_lvl lvl_env vs'
                         , le_subst     = subst'
-                        , le_env       = foldl' add_id id_env (vs `zip` vs') }
-
+                        , le_env       = foldl' add_id id_env (toList vs `zip` toList vs') }
        ; return (env', vs') }
 
-cloneLetVars :: RecFlag -> LevelEnv -> Level -> [InVar]
-             -> LvlM (LevelEnv, [OutVar])
+cloneLetVars
+ :: Traversable t => RecFlag -> LevelEnv -> Level -> t InVar -> LvlM (LevelEnv, t OutVar)
 -- See Note [Need for cloning during float-out]
 -- Works for Ids bound by let(rec)
 -- The dest_lvl is attributed to the binders in the new env,
@@ -1873,12 +1880,12 @@ cloneLetVars :: RecFlag -> LevelEnv -> Level -> [InVar]
 cloneLetVars is_rec
           env@(LE { le_subst = subst, le_lvl_env = lvl_env, le_env = id_env })
           dest_lvl vs
-  = do { let vs1  = map zap vs
+  = do { let vs1  = fmap zap vs
        ; (subst', vs2) <- case is_rec of
                             NonRecursive -> cloneBndrsM      subst vs1
                             Recursive    -> cloneRecIdBndrsM subst vs1
 
-       ; let prs  = vs `zip` vs2
+       ; let prs  = toList vs `zip` toList vs2
              env' = env { le_lvl_env = addLvls dest_lvl lvl_env vs2
                         , le_subst   = subst'
                         , le_env     = foldl' add_id id_env prs }
@@ -1894,6 +1901,10 @@ cloneLetVars is_rec
     -- See Note [Zapping JoinId when floating]
     zap_join | isTopLvl dest_lvl = zapJoinId
              | otherwise         = id
+{-# SPECIALIZE cloneLetVars :: RecFlag -> LevelEnv -> Level -> [InVar] -> LvlM (LevelEnv, [OutVar]) #-}
+{-# SPECIALIZE cloneLetVars :: RecFlag -> LevelEnv -> Level -> Identity InVar -> LvlM (LevelEnv, Identity OutVar) #-}
+{-# SPECIALIZE cloneLetVars :: RecFlag -> LevelEnv -> Level -> NonEmpty InVar -> LvlM (LevelEnv, NonEmpty OutVar) #-}
+{-# SPECIALIZE cloneLetVars :: RecFlag -> LevelEnv -> Level -> Pair InVar -> LvlM (LevelEnv, Pair OutVar) #-}
 
 add_id :: IdEnv ([Var], LevelledExpr) -> (Var, Var) -> IdEnv ([Var], LevelledExpr)
 add_id id_env (v, v1)
diff --git a/compiler/GHC/Core/Opt/Simplify/Utils.hs b/compiler/GHC/Core/Opt/Simplify/Utils.hs
index e07dd57a30bb8a39f22de19d8a0f57da03a3f409..85ff74b8bd4364b1341ad40bc903aee56cc6405e 100644
--- a/compiler/GHC/Core/Opt/Simplify/Utils.hs
+++ b/compiler/GHC/Core/Opt/Simplify/Utils.hs
@@ -2414,7 +2414,7 @@ prepareAlts :: OutExpr -> InId -> [InAlt] -> SimplM ([AltCon], [InAlt])
 -- Note that case_bndr is an InId; see Note [Shadowing in prepareAlts]
 prepareAlts scrut case_bndr alts
   | Just (tc, tys) <- splitTyConApp_maybe (idType case_bndr)
-  = do { us <- getUniquesM
+  = do { us <- getUniqueListM
        ; let (idcs1, alts1) = filterAlts tc tys imposs_cons alts
              (yes2,  alts2) = refineDefaultAlt us (idMult case_bndr) tc tys idcs1 alts1
                -- The multiplicity on case_bndr's is the multiplicity of the
@@ -2765,7 +2765,7 @@ mkCase2 mode scrut bndr alts_ty alts
       | not (isNullaryRepDataCon dc)
       = -- For non-nullary data cons we must invent some fake binders
         -- See Note [caseRules for dataToTag] in GHC.Core.Opt.ConstantFold
-        do { us <- getUniquesM
+        do { us <- getUniqueListM
            ; let (ex_tvs, arg_ids) = dataConRepInstPat us (idMult new_bndr) dc
                                         (tyConAppArgs (idType new_bndr))
            ; return (ex_tvs ++ arg_ids) }
diff --git a/compiler/GHC/Core/Opt/WorkWrap/Utils.hs b/compiler/GHC/Core/Opt/WorkWrap/Utils.hs
index 94dd2a1fe4a0972f3647029a06bf84b8e9e2d020..16f038649b707237603ad00cf2d7ea33ff79b618 100644
--- a/compiler/GHC/Core/Opt/WorkWrap/Utils.hs
+++ b/compiler/GHC/Core/Opt/WorkWrap/Utils.hs
@@ -971,7 +971,7 @@ unbox_one_arg :: WwOpts
 unbox_one_arg opts arg_var
               DataConPatContext { dcpc_dc = dc, dcpc_tc_args = tc_args
                                 , dcpc_co = co, dcpc_args = ds }
-  = do { pat_bndrs_uniqs <- getUniquesM
+  = do { pat_bndrs_uniqs <- getUniqueListM
        ; let ex_name_fss = map getOccFS $ dataConExTyCoVars dc
 
              -- Create new arguments we get when unboxing dc
@@ -1563,7 +1563,7 @@ unbox_one_result opts res_bndr
   --   ( case i of I# a ->          )    |
   --   ( case j of I# b ->          )    |     (      (<i>, <j>)      )
   --   ( <hole>                     )    |
-  pat_bndrs_uniqs <- getUniquesM
+  pat_bndrs_uniqs <- getUniqueListM
   let (_exs, arg_ids) =
         dataConRepFSInstPat (repeat ww_prefix) pat_bndrs_uniqs cprCaseBndrMult dc tc_args
   massert (null _exs) -- Should have been caught by canUnboxResult
diff --git a/compiler/GHC/Core/Subst.hs b/compiler/GHC/Core/Subst.hs
index 7df997412d38f08dcb5e978309df377248396d8b..e3f4df4af5d95715ef4d0a8e2345ddc27b4fffd1 100644
--- a/compiler/GHC/Core/Subst.hs
+++ b/compiler/GHC/Core/Subst.hs
@@ -415,20 +415,23 @@ cloneIdBndr subst us old_id
 -- | Applies 'cloneIdBndr' to a number of 'Id's, accumulating a final
 -- substitution from left to right
 -- Discards non-Stable unfoldings
-cloneIdBndrs :: Subst -> UniqSupply -> [Id] -> (Subst, [Id])
+cloneIdBndrs :: Traversable t => Subst -> UniqSupply -> t Id -> (Subst, t Id)
 cloneIdBndrs subst us ids
-  = mapAccumL (clone_id subst) subst (ids `zip` uniqsFromSupply us)
+  = mapAccumL (clone_id subst) subst (withUniques (flip (,)) us ids)
+{-# SPECIALIZE cloneIdBndrs :: Subst -> UniqSupply -> [Id] -> (Subst, [Id]) #-}
 
-cloneBndrs :: Subst -> UniqSupply -> [Var] -> (Subst, [Var])
+cloneBndrs :: Traversable t => Subst -> UniqSupply -> t Var -> (Subst, t Var)
 -- Works for all kinds of variables (typically case binders)
 -- not just Ids
 cloneBndrs subst us vs
-  = mapAccumL (\subst (v, u) -> cloneBndr subst u v) subst (vs `zip` uniqsFromSupply us)
+  = mapAccumL (\subst (v, u) -> cloneBndr subst u v) subst (withUniques (flip (,)) us vs)
+{-# SPECIALIZE cloneBndrs :: Subst -> UniqSupply -> [Var] -> (Subst, [Var]) #-}
 
-cloneBndrsM :: MonadUnique m => Subst -> [Var] -> m (Subst, [Var])
+cloneBndrsM :: (Traversable t, MonadUnique m) => Subst -> t Var -> m (Subst, t Var)
 -- Works for all kinds of variables (typically case binders)
 -- not just Ids
 cloneBndrsM subst vs = cloneBndrs subst `flip` vs <$> getUniqueSupplyM
+{-# INLINE cloneBndrsM #-}
 
 cloneBndr :: Subst -> Unique -> Var -> (Subst, Var)
 cloneBndr subst uniq v
@@ -436,14 +439,16 @@ cloneBndr subst uniq v
   | otherwise = clone_id subst subst (v,uniq)  -- Works for coercion variables too
 
 -- | Clone a mutually recursive group of 'Id's
-cloneRecIdBndrs :: Subst -> UniqSupply -> [Id] -> (Subst, [Id])
+cloneRecIdBndrs :: Traversable t => Subst -> UniqSupply -> t Id -> (Subst, t Id)
 cloneRecIdBndrs subst us ids =
-    let x@(subst', _) = mapAccumL (clone_id subst') subst (ids `zip` uniqsFromSupply us)
+    let x@(subst', _) = mapAccumL (clone_id subst') subst (withUniques (flip (,)) us ids)
     in x
+{-# SPECIALIZE cloneRecIdBndrs :: Subst -> UniqSupply -> [Id] -> (Subst, [Id]) #-}
 
 -- | Clone a mutually recursive group of 'Id's
-cloneRecIdBndrsM :: MonadUnique m => Subst -> [Id] -> m (Subst, [Id])
+cloneRecIdBndrsM :: (Traversable t, MonadUnique m) => Subst -> t Id -> m (Subst, t Id)
 cloneRecIdBndrsM subst ids = cloneRecIdBndrs subst `flip` ids <$> getUniqueSupplyM
+{-# INLINE cloneRecIdBndrsM #-}
 
 -- Just like substIdBndr, except that it always makes a new unique
 -- It is given the unique to use
diff --git a/compiler/GHC/Iface/Env.hs b/compiler/GHC/Iface/Env.hs
index 430fdcbdcc57013b3edfc485a862a7c6709d6a53..2fea7626d983ac8645314309ae957ed29fdd845b 100644
--- a/compiler/GHC/Iface/Env.hs
+++ b/compiler/GHC/Iface/Env.hs
@@ -262,11 +262,9 @@ newIfaceName occ
   = do  { uniq <- newUnique
         ; return $! mkInternalName uniq occ noSrcSpan }
 
-newIfaceNames :: [OccName] -> IfL [Name]
-newIfaceNames occs
-  = do  { uniqs <- getUniquesM
-        ; return [ mkInternalName uniq occ noSrcSpan
-                 | (occ,uniq) <- occs `zip` uniqs] }
+newIfaceNames :: Traversable t => t OccName -> IfL (t Name)
+newIfaceNames = withUniquesM (\ uniq occ -> mkInternalName uniq occ noSrcSpan)
+{-# INLINE newIfaceNames #-}
 
 trace_if :: Logger -> SDoc -> IO ()
 {-# INLINE trace_if #-} -- see Note [INLINE conditional tracing utilities]
diff --git a/compiler/GHC/IfaceToCore.hs b/compiler/GHC/IfaceToCore.hs
index 71eb13855887b2292721d1233a8b13a8cebbafe1..6df6812b75a44521f98c0bda997da9b2f3f1dbcd 100644
--- a/compiler/GHC/IfaceToCore.hs
+++ b/compiler/GHC/IfaceToCore.hs
@@ -1721,7 +1721,7 @@ tcIfaceAlt scrut mult (tycon, inst_tys) (IfaceAlt (IfaceDataAlt data_occ) arg_st
 tcIfaceDataAlt :: Mult -> DataCon -> [Type] -> [IfLclName] -> IfaceExpr
                -> IfL CoreAlt
 tcIfaceDataAlt mult con inst_tys arg_strs rhs
-  = do  { uniqs <- getUniquesM
+  = do  { uniqs <- getUniqueListM
         ; let (ex_tvs, arg_ids)
                       = dataConRepFSInstPat (map ifLclNameFS arg_strs) uniqs mult con inst_tys
 
diff --git a/compiler/GHC/Runtime/Eval.hs b/compiler/GHC/Runtime/Eval.hs
index 4303d445c6b85530175c2921949acd2bd37e163a..62723bf4d6b7b43f21b020802d890a757710074e 100644
--- a/compiler/GHC/Runtime/Eval.hs
+++ b/compiler/GHC/Runtime/Eval.hs
@@ -642,7 +642,7 @@ bindLocalsAtBreakpoint hsc_env apStack_fhv (Just ibi) = do
    newTyVars :: UniqSupply -> [TcTyVar] -> Subst
      -- Similarly, clone the type variables mentioned in the types
      -- we have here, *and* make them all RuntimeUnk tyvars
-   newTyVars us tvs = foldl' new_tv emptySubst (tvs `zip` uniqsFromSupply us)
+   newTyVars us tvs = foldl' new_tv emptySubst (tvs `zip` uniqListFromSupply us)
    new_tv subst (tv,uniq) = extendTCvSubstWithClone subst tv new_tv
     where
      new_tv = mkRuntimeUnkTyVar (setNameUnique (tyVarName tv) uniq)
diff --git a/compiler/GHC/Stg/Unarise.hs b/compiler/GHC/Stg/Unarise.hs
index d9444ee501f8bf7ba7033677ad6b6727c1f5d325..b6271ce7ca3c5263ca56792185be4162060f941d 100644
--- a/compiler/GHC/Stg/Unarise.hs
+++ b/compiler/GHC/Stg/Unarise.hs
@@ -731,7 +731,10 @@ unariseAlts rho (MultiValAlt _) bndr [GenStgAlt{ alt_con    = DEFAULT
 
 unariseAlts rho (MultiValAlt _) bndr alts
   | isUnboxedSumBndr bndr
-  = do (rho_sum_bndrs, scrt_bndrs@(tag_bndr : real_bndrs)) <- unariseConArgBinder rho bndr
+  = do (rho_sum_bndrs, scrt_bndrs) <- unariseConArgBinder rho bndr
+       let (tag_bndr, real_bndrs) = case scrt_bndrs of
+               [] -> panic "unariseAlts: empty scrt_bndrs"
+               x:xs -> (x, xs)
        alts' <- unariseSumAlts rho_sum_bndrs (map StgVarArg real_bndrs) alts
        let inner_case = StgCase (StgApp tag_bndr []) tag_bndr tagAltTy alts'
        return [GenStgAlt{ alt_con   = DataAlt (tupleDataCon Unboxed (length scrt_bndrs))
@@ -849,7 +852,7 @@ mapSumIdBinders alt_bndr args rhs rho0
       mkCastInput :: (Id,PrimRep,UniqSupply) -> ([(PrimOp,Type,Unique)],Id,Id)
       mkCastInput (id,rep,bndr_us) =
         let (ops,types) = unzip $ getCasts (typePrimRepU $ idType id) rep
-            cst_opts = zip3 ops types $ uniqsFromSupply bndr_us
+            cst_opts = zip3 ops types $ uniqListFromSupply bndr_us
             out_id = case cst_opts of
               [] -> id
               _ ->  let (_,ty,uq) = last cst_opts
@@ -960,7 +963,7 @@ mkUbxSum dc ty_args args0 us
         , (ops,types) <- unzip $ getCasts (stgArgRepU arg) $ slotPrimRep slot_ty
         , not . null $ ops
         = let (us1,us2) = splitUniqSupply us
-              cast_uqs = uniqsFromSupply us1
+              cast_uqs = uniqListFromSupply us1
               cast_opts = zip3 ops types cast_uqs
               (_op,out_ty,out_uq) = last cast_opts
               casts = castArgRename cast_opts arg :: StgExpr -> StgExpr
diff --git a/compiler/GHC/Tc/Gen/HsType.hs b/compiler/GHC/Tc/Gen/HsType.hs
index 98985cf8873e5ff053574cb301a2986719e17002..e8232a1553eb2c053657db4e779175a5288b2a0c 100644
--- a/compiler/GHC/Tc/Gen/HsType.hs
+++ b/compiler/GHC/Tc/Gen/HsType.hs
@@ -3941,7 +3941,7 @@ splitTyConKind skol_info in_scope avoid_occs kind
                         name   = mkInternalName uniq occ loc
                         tv     = mkTcTyVar name arg' details
                         subst' = extendSubstInScope subst tv
-                        uniq:uniqs' = uniqs
+                        Inf uniq uniqs' = uniqs
                         Inf occ occs' = occs
 
                     Just (Named (Bndr tv vis), kind')
diff --git a/compiler/GHC/Tc/Utils/Monad.hs b/compiler/GHC/Tc/Utils/Monad.hs
index 216d862d3ed8567643ecb69bf3cdd66ca54d2e90..a723d252f39f8ee648b01f680b93c90afdf37361 100644
--- a/compiler/GHC/Tc/Utils/Monad.hs
+++ b/compiler/GHC/Tc/Utils/Monad.hs
@@ -744,11 +744,9 @@ newSysLocalId fs w ty
   = do  { u <- newUnique
         ; return (mkSysLocal fs u w ty) }
 
-newSysLocalIds :: FastString -> [Scaled TcType] -> TcRnIf gbl lcl [TcId]
-newSysLocalIds fs tys
-  = do  { us <- getUniquesM
-        ; let mkId' n (Scaled w t) = mkSysLocal fs n w t
-        ; return (zipWith mkId' us tys) }
+newSysLocalIds :: (Traversable t) => FastString -> t (Scaled TcType) -> TcRnIf gbl lcl (t TcId)
+newSysLocalIds fs = withUniquesM (\ u (Scaled w t) -> mkSysLocal fs u w t)
+{-# INLINE newSysLocalIds #-}
 
 instance MonadUnique (IOEnv (Env gbl lcl)) where
         getUniqueM = newUnique
diff --git a/compiler/GHC/Types/Id.hs b/compiler/GHC/Types/Id.hs
index 8abbeebbc903c70a56ccc837a59fd1c1685733a8..2d1dfd6ac4f6823c40d409e1a2edbce01943720e 100644
--- a/compiler/GHC/Types/Id.hs
+++ b/compiler/GHC/Types/Id.hs
@@ -172,6 +172,8 @@ import GHC.Utils.Misc
 import GHC.Utils.Outputable
 import GHC.Utils.Panic
 
+import Control.Monad.Trans.State (evalState, state)
+
 -- infixl so you can say (id `set` a `set` b)
 infixl  1 `setIdUnfolding`,
           `setIdArity`,
@@ -398,12 +400,14 @@ mkScaledTemplateLocal i (Scaled w ty) = mkSysLocalOrCoVar (fsLit "v") (mkBuiltin
    -- and "~" and "~~" have coercion "superclasses".
 
 -- | Create a template local for a series of types
-mkTemplateLocals :: [Type] -> [Id]
+mkTemplateLocals :: Traversable f => f Type -> f Id
 mkTemplateLocals = mkTemplateLocalsNum 1
+{-# SPECIALIZE mkTemplateLocals :: [Type] -> [Id] #-}
 
 -- | Create a template local for a series of type, but start from a specified template local
-mkTemplateLocalsNum :: Int -> [Type] -> [Id]
-mkTemplateLocalsNum n tys = zipWith mkTemplateLocal [n..] tys
+mkTemplateLocalsNum :: Traversable f => Int -> f Type -> f Id
+mkTemplateLocalsNum n = flip evalState n . traverse (state . \ ty n -> (mkTemplateLocal n ty, succ n))
+{-# SPECIALIZE mkTemplateLocalsNum :: Int -> [Type] -> [Id] #-}
 
 {- Note [Exported LocalIds]
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/compiler/GHC/Types/Unique/Supply.hs b/compiler/GHC/Types/Unique/Supply.hs
index fa67ae61f7961e61ed88f831d0d17251fdc75498..6c85ee356785332e13ed799eb8fbdfa11febc3a8 100644
--- a/compiler/GHC/Types/Unique/Supply.hs
+++ b/compiler/GHC/Types/Unique/Supply.hs
@@ -14,12 +14,14 @@ module GHC.Types.Unique.Supply (
         UniqSupply, -- Abstractly
 
         -- ** Operations on supplies
-        uniqFromSupply, uniqsFromSupply, -- basic ops
+        uniqFromSupply, uniqsFromSupply, uniqListFromSupply, -- basic ops
         takeUniqFromSupply, uniqFromTag,
 
         mkSplitUniqSupply,
         splitUniqSupply, listSplitUniqSupply,
 
+        withUniques, withUniquesM,
+
         -- * Unique supply monad and its abstraction
         UniqSM, MonadUnique(..),
 
@@ -27,23 +29,26 @@ module GHC.Types.Unique.Supply (
         initUs, initUs_,
 
         -- * Set supply strategy
-        initUniqSupply
+        initUniqSupply,
   ) where
 
 import GHC.Prelude
 
+import GHC.Data.List.Infinite
 import GHC.Types.Unique
-import GHC.Utils.Panic.Plain
 
 import GHC.IO
 
 import GHC.Utils.Monad
-import Control.Monad
 import Data.Word
 import GHC.Exts( Ptr(..), noDuplicate#, oneShot )
 import Foreign.Storable
 import GHC.Utils.Monad.State.Strict as Strict
 
+#if defined(DEBUG)
+import GHC.Utils.Panic.Plain
+#endif
+
 #include "MachDeps.h"
 
 #if WORD_SIZE_IN_BITS != 64
@@ -292,7 +297,9 @@ listSplitUniqSupply :: UniqSupply -> [UniqSupply]
 -- ^ Create an infinite list of 'UniqSupply' from a single one
 uniqFromSupply  :: UniqSupply -> Unique
 -- ^ Obtain the 'Unique' from this particular 'UniqSupply'
-uniqsFromSupply :: UniqSupply -> [Unique] -- Infinite
+uniqsFromSupply :: UniqSupply -> Infinite Unique
+-- ^ Obtain an infinite list of 'Unique' that can be generated by constant splitting of the supply
+uniqListFromSupply :: UniqSupply -> [Unique]
 -- ^ Obtain an infinite list of 'Unique' that can be generated by constant splitting of the supply
 takeUniqFromSupply :: UniqSupply -> (Unique, UniqSupply)
 -- ^ Obtain the 'Unique' from this particular 'UniqSupply', and a new supply
@@ -301,11 +308,24 @@ splitUniqSupply (MkSplitUniqSupply _ s1 s2) = (s1, s2)
 listSplitUniqSupply  (MkSplitUniqSupply _ s1 s2) = s1 : listSplitUniqSupply s2
 
 uniqFromSupply  (MkSplitUniqSupply n _ _)  = mkUniqueGrimily n
-uniqsFromSupply (MkSplitUniqSupply n _ s2) = mkUniqueGrimily n : uniqsFromSupply s2
+uniqsFromSupply (MkSplitUniqSupply n _ s2) = mkUniqueGrimily n `Inf` uniqsFromSupply s2
+uniqListFromSupply (MkSplitUniqSupply n _ s2) = mkUniqueGrimily n : uniqListFromSupply s2
 takeUniqFromSupply (MkSplitUniqSupply n s1 _) = (mkUniqueGrimily n, s1)
 
 {-# INLINE splitUniqSupply #-}
 
+withUniques :: Traversable t => (Unique -> a -> b) -> UniqSupply -> t a -> t b
+withUniques f us = initUs_ us . traverse (\ a -> flip f a <$> getUniqueUs)
+{-# INLINE withUniques #-}
+
+withUniquesM :: (MonadUnique m, Traversable t) => (Unique -> a -> b) -> t a -> m (t b)
+withUniquesM f = \ as -> ($ as) <$> withUniquesM' f
+{-# INLINE withUniquesM #-}
+
+withUniquesM' :: (MonadUnique m, Traversable t) => (Unique -> a -> b) -> m (t a -> t b)
+withUniquesM' f = withUniques f <$> getUniqueSupplyM
+{-# INLINE withUniquesM' #-}
+
 {-
 ************************************************************************
 *                                                                      *
@@ -330,10 +350,6 @@ mkUniqSM :: (UniqSupply -> UniqResult a) -> UniqSM a
 mkUniqSM f = USM (oneShot f)
 {-# INLINE mkUniqSM #-}
 
--- TODO: try to get rid of this instance
-instance MonadFail UniqSM where
-    fail = panic
-
 -- | Run the 'UniqSM' action, returning the final 'UniqSupply'
 initUs :: UniqSupply -> UniqSM a -> (a, UniqSupply)
 initUs init_us m = case unUSM m init_us of { UniqResult r us -> (r, us) }
@@ -358,14 +374,17 @@ class Monad m => MonadUnique m where
     -- | Get a new unique identifier
     getUniqueM  :: m Unique
     -- | Get an infinite list of new unique identifiers
-    getUniquesM :: m [Unique]
+    getUniquesM :: m (Infinite Unique)
+    -- | Get an infinite list of new unique identifiers
+    getUniqueListM :: m [Unique]
 
     -- This default definition of getUniqueM, while correct, is not as
     -- efficient as it could be since it needlessly generates and throws away
     -- an extra Unique. For your instances consider providing an explicit
     -- definition for 'getUniqueM' which uses 'takeUniqFromSupply' directly.
-    getUniqueM  = liftM uniqFromSupply  getUniqueSupplyM
-    getUniquesM = liftM uniqsFromSupply getUniqueSupplyM
+    getUniqueM  = fmap uniqFromSupply  getUniqueSupplyM
+    getUniquesM = fmap uniqsFromSupply getUniqueSupplyM
+    getUniqueListM = fmap uniqListFromSupply getUniqueSupplyM
 
 instance MonadUnique UniqSM where
     getUniqueSupplyM = getUs
@@ -376,6 +395,6 @@ getUniqueUs :: UniqSM Unique
 getUniqueUs = mkUniqSM (\us0 -> case takeUniqFromSupply us0 of
                            (u,us1) -> UniqResult u us1)
 
-getUniquesUs :: UniqSM [Unique]
+getUniquesUs :: UniqSM (Infinite Unique)
 getUniquesUs = mkUniqSM (\us0 -> case splitUniqSupply us0 of
                             (us1,us2) -> UniqResult (uniqsFromSupply us1) us2)