module GHC.CmmToAsm.Reg.Linear.FreeRegs (
    FR(..),
    allFreeRegs,
    maxSpillSlots
)
where

import GHC.Prelude

import GHC.Platform.Reg
import GHC.Platform.Reg.Class
import qualified GHC.Platform.Reg.Class.Unified   as Unified
import qualified GHC.Platform.Reg.Class.Separate  as Separate
import qualified GHC.Platform.Reg.Class.NoVectors as NoVectors

import GHC.CmmToAsm.Config
import GHC.Utils.Panic
import GHC.Platform

-- -----------------------------------------------------------------------------
-- The free register set
-- This needs to be *efficient*
-- Here's an inefficient 'executable specification' of the FreeRegs data type:
--
--      type FreeRegs = [RegNo]
--      noFreeRegs = 0
--      releaseReg n f = if n `elem` f then f else (n : f)
--      initFreeRegs = allocatableRegs
--      getFreeRegs cls f = filter ( (==cls) . regClass . RealReg ) f
--      allocateReg f r = filter (/= r) f

import qualified GHC.CmmToAsm.Reg.Linear.PPC     as PPC
import qualified GHC.CmmToAsm.Reg.Linear.X86     as X86
import qualified GHC.CmmToAsm.Reg.Linear.X86_64  as X86_64
import qualified GHC.CmmToAsm.Reg.Linear.AArch64 as AArch64
import qualified GHC.CmmToAsm.Reg.Linear.RV64    as RV64

import qualified GHC.CmmToAsm.PPC.Instr     as PPC.Instr
import qualified GHC.CmmToAsm.X86.Instr     as X86.Instr
import qualified GHC.CmmToAsm.AArch64.Instr as AArch64.Instr
import qualified GHC.CmmToAsm.RV64.Instr    as RV64.Instr

class Show freeRegs => FR freeRegs where
    frAllocateReg :: Platform -> RealReg -> freeRegs -> freeRegs
    frGetFreeRegs :: Platform -> RegClass -> freeRegs -> [RealReg]
    frInitFreeRegs :: Platform -> freeRegs
    frReleaseReg :: Platform -> RealReg -> freeRegs -> freeRegs

instance FR X86.FreeRegs where
    frAllocateReg  = \_ -> X86.allocateReg
    frGetFreeRegs  = X86.getFreeRegs
    frInitFreeRegs = X86.initFreeRegs
    frReleaseReg   = \_ -> X86.releaseReg

instance FR X86_64.FreeRegs where
    frAllocateReg  = \_ -> X86_64.allocateReg
    frGetFreeRegs  = X86_64.getFreeRegs
    frInitFreeRegs = X86_64.initFreeRegs
    frReleaseReg   = \_ -> X86_64.releaseReg

instance FR PPC.FreeRegs where
    frAllocateReg  = \_ -> PPC.allocateReg
    frGetFreeRegs  = \_ -> PPC.getFreeRegs
    frInitFreeRegs = PPC.initFreeRegs
    frReleaseReg   = \_ -> PPC.releaseReg

instance FR AArch64.FreeRegs where
    frAllocateReg = \_ -> AArch64.allocateReg
    frGetFreeRegs = \_ -> AArch64.getFreeRegs
    frInitFreeRegs = AArch64.initFreeRegs
    frReleaseReg = \_ -> AArch64.releaseReg

instance FR RV64.FreeRegs where
    frAllocateReg = const RV64.allocateReg
    frGetFreeRegs = const RV64.getFreeRegs
    frInitFreeRegs = RV64.initFreeRegs
    frReleaseReg = const RV64.releaseReg

allFreeRegs :: FR freeRegs => Platform -> freeRegs -> [RealReg]
allFreeRegs plat fr = foldMap (\rcls -> frGetFreeRegs plat rcls fr) allRegClasses
  where
    allRegClasses =
      case registerArch (platformArch plat) of
        Unified   ->   Unified.allRegClasses
        Separate  ->  Separate.allRegClasses
        NoVectors -> NoVectors.allRegClasses

maxSpillSlots :: NCGConfig -> Int
maxSpillSlots config = case platformArch (ncgPlatform config) of
   ArchX86       -> X86.Instr.maxSpillSlots config
   ArchX86_64    -> X86.Instr.maxSpillSlots config
   ArchPPC       -> PPC.Instr.maxSpillSlots config
   ArchS390X     -> panic "maxSpillSlots ArchS390X"
   ArchARM _ _ _ -> panic "maxSpillSlots ArchARM"
   ArchAArch64   -> AArch64.Instr.maxSpillSlots config
   ArchPPC_64 _  -> PPC.Instr.maxSpillSlots config
   ArchAlpha     -> panic "maxSpillSlots ArchAlpha"
   ArchMipseb    -> panic "maxSpillSlots ArchMipseb"
   ArchMipsel    -> panic "maxSpillSlots ArchMipsel"
   ArchRISCV64   -> RV64.Instr.maxSpillSlots config
   ArchLoongArch64->panic "maxSpillSlots ArchLoongArch64"
   ArchJavaScript-> panic "maxSpillSlots ArchJavaScript"
   ArchWasm32    -> panic "maxSpillSlots ArchWasm32"
   ArchUnknown   -> panic "maxSpillSlots ArchUnknown"
