diff --git a/compiler/GHC/StgToCmm/Prim.hs b/compiler/GHC/StgToCmm/Prim.hs
index e309d061a894094f3aac44ca7ff029b5ff18cb7c..d6d75a3deba0189b44b9dce5a63b38109c6653e4 100644
--- a/compiler/GHC/StgToCmm/Prim.hs
+++ b/compiler/GHC/StgToCmm/Prim.hs
@@ -82,33 +82,19 @@ cgOpApp (StgFCallOp fcall ty) stg_args res_ty
   = cgForeignCall fcall ty stg_args res_ty
       -- Note [Foreign call results]
 
--- tagToEnum# is special: we need to pull the constructor
--- out of the table, and perform an appropriate return.
-
-cgOpApp (StgPrimOp TagToEnumOp) [arg] res_ty
-  = ASSERT(isEnumerationTyCon tycon)
-    do  { dflags <- getDynFlags
-        ; args' <- getNonVoidArgAmodes [arg]
-        ; let amode = case args' of [amode] -> amode
-                                    _ -> panic "TagToEnumOp had void arg"
-        ; emitReturn [tagToClosure dflags tycon amode] }
-   where
-          -- If you're reading this code in the attempt to figure
-          -- out why the compiler panic'ed here, it is probably because
-          -- you used tagToEnum# in a non-monomorphic setting, e.g.,
-          --         intToTg :: Enum a => Int -> a ; intToTg (I# x#) = tagToEnum# x#
-          -- That won't work.
-        tycon = tyConAppTyCon res_ty
-
 cgOpApp (StgPrimOp primop) args res_ty = do
     dflags <- getDynFlags
     cmm_args <- getNonVoidArgAmodes args
     case emitPrimOp dflags primop cmm_args of
-        Nothing -> do  -- out-of-line
+        PrimopCmmEmit_External -> do  -- out-of-line
           let fun = CmmLit (CmmLabel (mkRtsPrimOpLabel primop))
           emitCall (NativeNodeCall, NativeReturn) fun cmm_args
 
-        Just f  -- inline
+        PrimopCmmEmit_Raw f -> do
+          exprs <- f res_ty
+          emitReturn exprs
+
+        PrimopCmmEmit_IntoRegs f  -- inline
           | ReturnsPrim VoidRep <- result_info
           -> do f []
                 emitReturn []
@@ -158,8 +144,9 @@ cgPrimOp results op args = do
   dflags <- getDynFlags
   arg_exprs <- getNonVoidArgAmodes args
   case emitPrimOp dflags op arg_exprs of
-    Nothing -> panic "External prim op"
-    Just f -> f results
+    PrimopCmmEmit_External -> panic "External prim op"
+    PrimopCmmEmit_Raw _ -> panic "caller should handle TagToEnum themselves"
+    PrimopCmmEmit_IntoRegs f -> f results
 
 
 ------------------------------------------------------------------------
@@ -167,7 +154,10 @@ cgPrimOp results op args = do
 ------------------------------------------------------------------------
 
 shouldInlinePrimOp :: DynFlags -> PrimOp -> [CmmExpr] -> Bool
-shouldInlinePrimOp dflags op args = isJust $ emitPrimOp dflags op args
+shouldInlinePrimOp dflags op args = case emitPrimOp dflags op args of
+  PrimopCmmEmit_External -> False
+  PrimopCmmEmit_IntoRegs _ -> True
+  PrimopCmmEmit_Raw _ -> True
 
 -- TODO: Several primop implementations (e.g. 'doNewByteArrayOp') use
 -- ByteOff (or some other fixed width signed type) to represent
@@ -1435,7 +1425,18 @@ dispatchPrimop dflags = \case
     then Left MO_F64_Fabs
     else Right $ genericFabsOp W64
 
-  TagToEnumOp -> panic "emitPrimOp: handled above in cgOpApp"
+  -- tagToEnum# is special: we need to pull the constructor
+  -- out of the table, and perform an appropriate return.
+  TagToEnumOp -> \[amode] -> OpDest_Raw $ \res_ty -> do
+    -- If you're reading this code in the attempt to figure
+    -- out why the compiler panic'ed here, it is probably because
+    -- you used tagToEnum# in a non-monomorphic setting, e.g.,
+    --         intToTg :: Enum a => Int -> a ; intToTg (I# x#) = tagToEnum# x#
+    -- That won't work.
+    let tycon = tyConAppTyCon res_ty
+    MASSERT(isEnumerationTyCon tycon)
+    dflags <- getDynFlags
+    pure [tagToClosure dflags tycon amode]
 
 -- Out of line primops.
 -- TODO compiler need not know about these
@@ -1579,6 +1580,17 @@ data OpDest
   -- choice of variant never depends on them.
   | OpDest_AllDone ([LocalReg] -- where to put the results
                     -> FCode ())
+  -- | Even more manual than '@OpDest_AllDone@', this is just for the '@TagToEnum@' primop for now.
+  -- It would be nice to remove this special case but that is future work.
+  | OpDest_Raw (Type -- the return type, some primops are specialized to it
+                -> FCode [CmmExpr])
+
+data PrimopCmmEmit
+  = PrimopCmmEmit_External
+  | PrimopCmmEmit_IntoRegs ([LocalReg] -- where to put the results
+                           -> FCode ())
+  | PrimopCmmEmit_Raw (Type -- the return type, some primops are specialized to it
+                       -> FCode [CmmExpr]) -- just for TagToEnum for now
 
 -- | Wrapper around '@dispatchPrimop@' which implements the cases represented
 -- with '@OpDest@'.
@@ -1589,31 +1601,32 @@ data OpDest
 emitPrimOp :: DynFlags
            -> PrimOp            -- the op
            -> [CmmExpr]         -- arguments
-           -> Maybe ([LocalReg]        -- where to put the results
-                      -> FCode ())
+           -> PrimopCmmEmit
 
 -- The rest just translate straightforwardly
 emitPrimOp dflags op args = case dispatchPrimop dflags op args of
-  OpDest_Nop -> Just $ \[res] -> emitAssign (CmmLocal res) arg
+  OpDest_Nop -> PrimopCmmEmit_IntoRegs $ \[res] -> emitAssign (CmmLocal res) arg
     where [arg] = args
 
-  OpDest_Narrow (mop, rep) -> Just $ \[res] -> emitAssign (CmmLocal res) $
+  OpDest_Narrow (mop, rep) -> PrimopCmmEmit_IntoRegs $ \[res] -> emitAssign (CmmLocal res) $
     CmmMachOp (mop rep (wordWidth dflags)) [CmmMachOp (mop (wordWidth dflags) rep) [arg]]
     where [arg] = args
 
-  OpDest_Callish prim -> Just $ \[res] -> emitPrimCall [res] prim args
+  OpDest_Callish prim -> PrimopCmmEmit_IntoRegs $ \[res] -> emitPrimCall [res] prim args
 
-  OpDest_Translate mop -> Just $ \[res] -> do
+  OpDest_Translate mop -> PrimopCmmEmit_IntoRegs $ \[res] -> do
     let stmt = mkAssign (CmmLocal res) (CmmMachOp mop args)
     emit stmt
 
-  OpDest_CallishHandledLater callOrNot -> Just $ \res0 -> case callOrNot of
+  OpDest_CallishHandledLater callOrNot -> PrimopCmmEmit_IntoRegs $ \res0 -> case callOrNot of
           Left op   -> emit $ mkUnsafeCall (PrimTarget op) res0 args
           Right gen -> gen res0 args
 
-  OpDest_AllDone f -> Just $ f
+  OpDest_AllDone f -> PrimopCmmEmit_IntoRegs $ f
+
+  OpDest_External -> PrimopCmmEmit_External
 
-  OpDest_External -> Nothing
+  OpDest_Raw f -> PrimopCmmEmit_Raw f
 
 type GenericOp = [CmmFormal] -> [CmmActual] -> FCode ()
 
diff --git a/testsuite/tests/cmm/should_compile/T17442.hs b/testsuite/tests/cmm/should_compile/T17442.hs
new file mode 100644
index 0000000000000000000000000000000000000000..b9c96afea93872e97d96ad21c380d87ae348eb54
--- /dev/null
+++ b/testsuite/tests/cmm/should_compile/T17442.hs
@@ -0,0 +1,43 @@
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE TypeFamilies #-}
+module T17442 where
+
+import Control.Monad
+import GHC.Arr (Ix(..))
+import GHC.Base (getTag)
+import GHC.Exts
+
+data family D
+data instance D = MkD
+  deriving (Eq, Ord, Show)
+
+instance Ix D where
+  range (a, b) =
+    let a# = getTag a
+        b# = getTag b
+    in map (\(I# i#) -> tagToEnum# i# :: D)
+           (enumFromTo (I# a#) (I# b#))
+  unsafeIndex (a, _) c =
+    let a# = getTag a
+        c# = getTag c
+        d# = c# -# a#
+    in I# d#
+  inRange (a, b) c =
+    let a# = getTag a
+        b# = getTag b
+        c# = getTag c
+    in tagToEnum# (c# >=# a#) && tagToEnum# (c# <=# b#)
+
+shouldBe :: (Eq a, Show a) => a -> a -> IO ()
+shouldBe x y =
+  unless (x == y) $ fail $ show x ++ " is not equal to " ++ show y
+
+ixLaws :: (Ix a, Show a) => a -> a -> a -> IO ()
+ixLaws l u i = do
+    inRange (l,u) i                 `shouldBe` elem i (range (l,u))
+    range (l,u) !! index (l,u) i    `shouldBe` i
+    map (index (l,u)) (range (l,u)) `shouldBe` [0..rangeSize (l,u)-1]
+    rangeSize (l,u)                 `shouldBe` length (range (l,u))
+
+dIsLawfulIx :: IO ()
+dIsLawfulIx = ixLaws MkD MkD MkD
diff --git a/testsuite/tests/cmm/should_compile/all.T b/testsuite/tests/cmm/should_compile/all.T
index 46dc86930a4e975eeec17c658f40708b1c40b9ab..4eba959ba946181782183d46718d8652c1e91e03 100644
--- a/testsuite/tests/cmm/should_compile/all.T
+++ b/testsuite/tests/cmm/should_compile/all.T
@@ -1,3 +1,4 @@
 #
 test('selfloop', [cmm_src], compile, [''])
 test('T16930', normal, makefile_test, ['T16930'])
+test('T17442', normal, compile, [''])