[Git][ghc/ghc][wip/fendor/hpc-bc-support] Make HPC work with bytecode interpreter
by Hannes Siebenhandl (@fendor) 02 Apr '26
by Hannes Siebenhandl (@fendor) 02 Apr '26
02 Apr '26
Hannes Siebenhandl pushed to branch wip/fendor/hpc-bc-support at Glasgow Haskell Compiler / GHC
Commits:
e9b23c14 by fendor at 2026-04-02T10:45:08+02:00
Make HPC work with bytecode interpreter
Add support to generate .tix files from bytecode objects and the
bytecode interpreter.
Conceptually, we insert HPC ticks into the bytecode similar to how we insert
breakpoints.
HPC and breakpoints do not share the same tick array but we use a separate
tick-array for hpc/breakpoint ticks during bytecode generation.
We teach the bytecode interpreter to handle hpc ticks.
The implementation is quite trivial, simply increment the counter in the
global hpc_ticks array for the respective module.
This hpc_ticks array is generated as part of the `CStub`, so we can rely
on it existing.
A tricky bit is "registering" a bytecode object for HPC instrumentation.
In the compiled case, this is achieved via CStub and initializer/finalizers
`.init` sections which are called when the executable is run.
After the initializers have been invoked, which is before `hs_init_ghc`,
we then call `startup_hpc` in `hs_init_ghc` iff any modules were "registered"
for hpc instrumentation via `hs_hpc_module`.
Since bytecode objects are loaded after starting up GHCi, this workflow
doesn't work for supporting `hpc` and the `hpc` run-time is never
started, even if a module is added for instrumentation.
We fix this issue by employing the same technique as is for `SptEntry`s:
* We introduce a new field to `CompiledByteCode`, called `ByteCodeHpcInfo`
which contains enough information to call `hs_hpc_module`, allowing us to
register the module for `hpc` instrumentation`.
* After registering the module, we unconditionally call `startupHpc`, to make
sure the .tix file is written.
Calling `startupHpc` multiple times is safe.
Calling `hs_hpc_module` multiple times for the same module is also safe.
Evaluating a bytecode object instrumented with `-fhpc` without registering it
in the `hpc` run-time will simply not generate any `.tix` files for this
bytecode object.
Closes #27036
- - - - -
31 changed files:
- compiler/GHC/ByteCode/Asm.hs
- compiler/GHC/ByteCode/Instr.hs
- compiler/GHC/ByteCode/Serialize.hs
- compiler/GHC/ByteCode/Types.hs
- compiler/GHC/Driver/Backend.hs
- compiler/GHC/Driver/CodeOutput.hs
- compiler/GHC/Driver/Main.hs
- compiler/GHC/Driver/Session.hs
- compiler/GHC/HsToCore.hs
- compiler/GHC/HsToCore/Coverage.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/Iface/Tidy.hs
- compiler/GHC/Linker/ByteCode.hs
- compiler/GHC/Linker/Loader.hs
- compiler/GHC/Linker/Types.hs
- compiler/GHC/Runtime/Interpreter.hs
- compiler/GHC/StgToByteCode.hs
- compiler/GHC/Types/HpcInfo.hs
- compiler/GHC/Unit/Module/ModGuts.hs
- + libraries/ghci/GHCi/Coverage.hs
- libraries/ghci/GHCi/Message.hs
- libraries/ghci/GHCi/Run.hs
- libraries/ghci/ghci.cabal.in
- rts/Disassembler.c
- rts/Hpc.c
- rts/Interpreter.c
- rts/include/rts/Bytecodes.h
- + testsuite/tests/hpc/ghc_ghci/BytecodeMain.hs
- testsuite/tests/hpc/ghc_ghci/Makefile
- + testsuite/tests/hpc/ghc_ghci/hpc_ghc_ghci_bytecode.stdout
- testsuite/tests/hpc/ghc_ghci/test.T
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/e9b23c146173db723c8f0db5f96a8cd…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/e9b23c146173db723c8f0db5f96a8cd…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/fendor/hpc-bc-support] Make HPC work with bytecode interpreter
by Hannes Siebenhandl (@fendor) 02 Apr '26
by Hannes Siebenhandl (@fendor) 02 Apr '26
02 Apr '26
Hannes Siebenhandl pushed to branch wip/fendor/hpc-bc-support at Glasgow Haskell Compiler / GHC
Commits:
5c1b59b3 by Matthew Pickering at 2026-04-02T10:35:50+02:00
Make HPC work with bytecode interpreter
Add support to generate .tix files from bytecode objects and the
bytecode interpreter.
Conceptually, we insert HPC ticks into the bytecode similar to how we insert
breakpoints.
HPC and breakpoints do not share the same tick array but we use a separate
tick-array for hpc/breakpoint ticks during bytecode generation.
We teach the bytecode interpreter to handle hpc ticks.
The implementation is quite trivial, simply increment the counter in the
global hpc_ticks array for the respective module.
This hpc_ticks array is generated as part of the `CStub`, so we can rely
on it existing.
A tricky bit is "registering" a bytecode object for HPC instrumentation.
In the compiled case, this is achieved via CStub and initializer/finalizers
`.init` sections which are called when the executable is run.
After the initializers have been invoked, which is before `hs_init_ghc`,
we then call `startup_hpc` in `hs_init_ghc` iff any modules were "registered"
for hpc instrumentation via `hs_hpc_module`.
Since bytecode objects are loaded after starting up GHCi, this workflow
doesn't work for supporting `hpc` and the `hpc` run-time is never
started, even if a module is added for instrumentation.
We fix this issue by employing the same technique as is for `SptEntry`s:
* We introduce a new field to `CompiledByteCode`, called `ByteCodeHpcInfo`
which contains enough information to call `hs_hpc_module`, allowing us to
register the module for `hpc` instrumentation`.
* After registering the module, we unconditionally call `startupHpc`, to make
sure the .tix file is written.
Calling `startupHpc` multiple times is safe.
Calling `hs_hpc_module` multiple times for the same module is also safe.
Evaluating a bytecode object instrumented with `-fhpc` without registering it
in the `hpc` run-time will simply not generate any `.tix` files for this
bytecode object.
Closes #27036
- - - - -
31 changed files:
- compiler/GHC/ByteCode/Asm.hs
- compiler/GHC/ByteCode/Instr.hs
- compiler/GHC/ByteCode/Serialize.hs
- compiler/GHC/ByteCode/Types.hs
- compiler/GHC/Driver/Backend.hs
- compiler/GHC/Driver/CodeOutput.hs
- compiler/GHC/Driver/Main.hs
- compiler/GHC/Driver/Session.hs
- compiler/GHC/HsToCore.hs
- compiler/GHC/HsToCore/Coverage.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/Iface/Tidy.hs
- compiler/GHC/Linker/ByteCode.hs
- compiler/GHC/Linker/Loader.hs
- compiler/GHC/Linker/Types.hs
- compiler/GHC/Runtime/Interpreter.hs
- compiler/GHC/StgToByteCode.hs
- compiler/GHC/Types/HpcInfo.hs
- compiler/GHC/Unit/Module/ModGuts.hs
- + libraries/ghci/GHCi/Coverage.hs
- libraries/ghci/GHCi/Message.hs
- libraries/ghci/GHCi/Run.hs
- libraries/ghci/ghci.cabal.in
- rts/Disassembler.c
- rts/Hpc.c
- rts/Interpreter.c
- rts/include/rts/Bytecodes.h
- + testsuite/tests/hpc/ghc_ghci/BytecodeMain.hs
- testsuite/tests/hpc/ghc_ghci/Makefile
- + testsuite/tests/hpc/ghc_ghci/hpc_ghc_ghci_bytecode.stdout
- testsuite/tests/hpc/ghc_ghci/test.T
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/5c1b59b3767d8a1f6cdc7bc55aa01e7…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/5c1b59b3767d8a1f6cdc7bc55aa01e7…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/fendor/hpc-bc-support] Make HPC work with bytecode interpreter
by Hannes Siebenhandl (@fendor) 02 Apr '26
by Hannes Siebenhandl (@fendor) 02 Apr '26
02 Apr '26
Hannes Siebenhandl pushed to branch wip/fendor/hpc-bc-support at Glasgow Haskell Compiler / GHC
Commits:
6832ae44 by fendor at 2026-04-02T10:04:24+02:00
Make HPC work with bytecode interpreter
- - - - -
20 changed files:
- compiler/GHC/ByteCode/Asm.hs
- compiler/GHC/ByteCode/Serialize.hs
- compiler/GHC/ByteCode/Types.hs
- compiler/GHC/Driver/CodeOutput.hs
- compiler/GHC/Driver/Main.hs
- compiler/GHC/HsToCore/Coverage.hs
- compiler/GHC/Iface/Tidy.hs
- compiler/GHC/Linker/ByteCode.hs
- compiler/GHC/Linker/Loader.hs
- compiler/GHC/Linker/Types.hs
- compiler/GHC/Runtime/Interpreter.hs
- compiler/GHC/StgToByteCode.hs
- compiler/GHC/Types/HpcInfo.hs
- compiler/GHC/Unit/Module/ModGuts.hs
- + libraries/ghci/GHCi/Coverage.hs
- libraries/ghci/GHCi/Message.hs
- libraries/ghci/GHCi/Run.hs
- libraries/ghci/ghci.cabal.in
- rts/Hpc.c
- rts/Interpreter.c
Changes:
=====================================
compiler/GHC/ByteCode/Asm.hs
=====================================
@@ -71,6 +71,7 @@ import GHC.Float (castFloatToWord32, castDoubleToWord64)
import qualified Data.List as List ( any )
import GHC.Exts
+import qualified GHC.Data.Strict as Strict
-- -----------------------------------------------------------------------------
@@ -110,8 +111,9 @@ assembleBCOs
-> [(Name, ByteString)]
-> Maybe InternalModBreaks
-> [SptEntry]
+ -> Strict.Maybe ByteCodeHpcInfo
-> IO CompiledByteCode
-assembleBCOs profile proto_bcos tycons top_strs modbreaks spt_entries = do
+assembleBCOs profile proto_bcos tycons top_strs modbreaks spt_entries use_hpc = do
-- TODO: the profile should be bundled with the interpreter: the rts ways are
-- fixed for an interpreter
let itbls = mkITbls profile tycons
@@ -122,6 +124,7 @@ assembleBCOs profile proto_bcos tycons top_strs modbreaks spt_entries = do
, bc_strs = top_strs
, bc_breaks = modbreaks
, bc_spt_entries = spt_entries
+ , bc_hpc_info = use_hpc
}
-- Note [Allocating string literals]
=====================================
compiler/GHC/ByteCode/Serialize.hs
=====================================
@@ -295,13 +295,15 @@ instance Binary CompiledByteCode where
replicateM bc_strs_len $ (,) <$> getViaBinName bh <*> get bh
bc_breaks <- get bh
bc_spt_entries <- get bh
+ bc_hpc_info <- get bh
return $
CompiledByteCode
{ bc_bcos,
bc_itbls,
bc_strs,
bc_breaks,
- bc_spt_entries
+ bc_spt_entries,
+ bc_hpc_info
}
put_ bh CompiledByteCode {..} = do
@@ -314,6 +316,26 @@ instance Binary CompiledByteCode where
for_ bc_strs $ \(nm, str) -> putViaBinName bh nm *> put_ bh str
put_ bh bc_breaks
put_ bh bc_spt_entries
+ put_ bh bc_hpc_info
+
+instance Binary ByteCodeHpcInfo where
+ put_ bh ByteCodeHpcInfo{bchi_tick_count,bchi_hash,bchi_tickboxes,bchi_module_name} = do
+ put_ bh bchi_tick_count
+ put_ bh bchi_hash
+ put_ bh bchi_tickboxes
+ put_ bh bchi_module_name
+
+ get bh = do
+ bchi_tick_count <- get bh
+ bchi_hash <- get bh
+ bchi_tickboxes <- get bh
+ bchi_module_name <- get bh
+ pure ByteCodeHpcInfo
+ { bchi_tick_count
+ , bchi_hash
+ , bchi_tickboxes
+ , bchi_module_name
+ }
instance Binary UnlinkedBCO where
get bh =
=====================================
compiler/GHC/ByteCode/Types.hs
=====================================
@@ -22,6 +22,9 @@ module GHC.ByteCode.Types
-- * Mod Breaks
, ModBreaks (..), BreakpointId(..), BreakTickIndex
+ -- * Hpc Info
+ , ByteCodeHpcInfo(..)
+
-- * Internal Mod Breaks
, InternalModBreaks(..), CgBreakInfo(..), seqInternalModBreaks
-- ** Internal breakpoint identifier
@@ -32,6 +35,7 @@ import GHC.Prelude
import GHC.Data.FastString
import GHC.Data.FlatBag
+import qualified GHC.Data.Strict as Strict
import GHC.Types.Name
import GHC.Types.Name.Env
import GHC.Utils.Binary
@@ -76,6 +80,14 @@ data CompiledByteCode = CompiledByteCode
-- ^ Static pointer table entries which should be loaded along with the
-- BCOs. See Note [Grand plan for static forms] in
-- "GHC.Iface.Tidy.StaticPtrTable".
+ , bc_hpc_info :: !(Strict.Maybe ByteCodeHpcInfo) -- ^ TODO: @fendor
+ }
+
+data ByteCodeHpcInfo = ByteCodeHpcInfo
+ { bchi_tick_count :: {-# UNPACK #-} !Int
+ , bchi_hash :: {-# UNPACK #-} !Int
+ , bchi_tickboxes :: !ByteString
+ , bchi_module_name :: !ByteString
}
-- | A libffi ffi_cif function prototype.
=====================================
compiler/GHC/Driver/CodeOutput.hs
=====================================
@@ -278,13 +278,12 @@ outputForeignStubs logger tmpfs dflags unit_state mod location stubs
ForeignStubs (CHeader h_code) cstub -> do
let
- stub_c_output_d = pprCode (getCStub cstub $$ pprCStubInitFiniDecls platform cstub)
+ stub_c_output_d = pprCode (getCStub cstub)
stub_c_output_w = showSDoc dflags stub_c_output_d
-- Header file protos for "foreign export"ed functions.
stub_h_output_d = pprCode h_code
stub_h_output_w = showSDoc dflags stub_h_output_d
- platform = targetPlatform dflags
putDumpFileMaybe logger Opt_D_dump_foreign
"Foreign export header file"
@@ -344,29 +343,6 @@ outputForeignStubs logger tmpfs dflags unit_state mod location stubs
cplusplus_hdr = "#if defined(__cplusplus)\nextern \"C\" {\n#endif\n"
cplusplus_ftr = "#if defined(__cplusplus)\n}\n#endif\n"
-pprCStubInitFiniDecls :: Platform -> CStub -> SDoc
-pprCStubInitFiniDecls platform cstub =
- vcat (zipWith (pprInitOrFiniDecl "ini" ".init_array") [0 :: Int ..] (getInitializers cstub))
- $$ vcat (zipWith (pprInitOrFiniDecl "fini" ".fini_array") [0 :: Int ..] (getFinalizers cstub))
- where
- pprInitOrFiniDecl :: String -> String -> Int -> CLabel -> SDoc
- pprInitOrFiniDecl suf section_name n lbl =
- vcat
- [ hsep [text "extern void", pprCLabel platform lbl, text "(void);"]
- , hsep [ text "static void (*"
- <> text "__ghc_" <> text suf <> text "_"
- <> int n
- <> text ")(void)"
- , text "__attribute__((used, section("
- <> doubleQuotes (text section_name)
- <> text ")))"
- , equals
- , pprCLabel platform lbl
- <> semi
- ]
- ]
-
-
-- It is more than likely that the stubs file will
-- turn out to be empty, in which case no file should be created.
outputForeignStubs_help :: FilePath -> String -> String -> String -> IO Bool
=====================================
compiler/GHC/Driver/Main.hs
=====================================
@@ -151,6 +151,7 @@ import GHC.Hs.Dump
import GHC.Hs.Stats ( ppSourceStats )
import GHC.HsToCore
+import GHC.HsToCore.Coverage ( hpcTickBoxes )
import GHC.StgToByteCode ( byteCodeGen )
import GHC.StgToJS ( stgToJS )
@@ -237,6 +238,7 @@ import GHC.Types.Var.Set
import GHC.Types.Error
import GHC.Types.Fixity.Env
import GHC.Types.CostCentre
+import GHC.Types.HpcInfo (HpcInfo (..))
import GHC.Types.IPE
import GHC.Types.SourceFile
import GHC.Types.SrcLoc
@@ -299,6 +301,9 @@ import qualified GHC.Unit.Home.Graph as HUG
import GHC.Unit.Home.PackageTable
import GHC.ByteCode.Serialize
+import GHC.Driver.Ppr (showSDoc)
+import qualified Data.ByteString.Char8 as BS8
+import qualified GHC.Data.Strict as Strict
{- **********************************************************************
%* *
@@ -1186,7 +1191,7 @@ compileWholeCoreBindings hsc_env type_env wcb = do
gen_bytecode core_binds stubs foreign_files = do
let cgi_guts = CgInteractiveGuts wcb_module core_binds
(typeEnvTyCons type_env) stubs foreign_files
- Nothing []
+ Nothing [] NoHpcInfo
trace_if logger (text "Generating ByteCode for" <+> ppr wcb_module)
mkModuleByteCode hsc_env wcb_module wcb_mod_location cgi_guts
@@ -2136,11 +2141,12 @@ data CgInteractiveGuts = CgInteractiveGuts { cgi_module :: Module
, cgi_foreign_files :: [(ForeignSrcLang, FilePath)]
, cgi_modBreaks :: Maybe ModBreaks
, cgi_spt_entries :: [SptEntry]
+ , cgi_hpc_info :: HpcInfo
}
mkCgInteractiveGuts :: CgGuts -> CgInteractiveGuts
-mkCgInteractiveGuts CgGuts{cg_module, cg_binds, cg_tycons, cg_foreign, cg_foreign_files, cg_modBreaks, cg_spt_entries}
- = CgInteractiveGuts cg_module cg_binds cg_tycons cg_foreign cg_foreign_files cg_modBreaks cg_spt_entries
+mkCgInteractiveGuts CgGuts{cg_module, cg_binds, cg_tycons, cg_foreign, cg_foreign_files, cg_modBreaks, cg_spt_entries, cg_hpc_info}
+ = CgInteractiveGuts cg_module cg_binds cg_tycons cg_foreign cg_foreign_files cg_modBreaks cg_spt_entries cg_hpc_info
hscInteractive :: HscEnv
-> CgInteractiveGuts
@@ -2163,13 +2169,15 @@ hscGenerateByteCode :: HscEnv -> CgInteractiveGuts -> ModLocation -> IO Compiled
hscGenerateByteCode hsc_env cgguts location = do
let dflags = hsc_dflags hsc_env
let logger = hsc_logger hsc_env
+ let platform = targetPlatform dflags
let CgInteractiveGuts{ -- This is the last use of the ModGuts in a compilation.
-- From now on, we just use the bits we need.
cgi_module = this_mod,
cgi_binds = core_binds,
cgi_tycons = tycons,
cgi_modBreaks = mod_breaks,
- cgi_spt_entries = spt_entries } = cgguts
+ cgi_spt_entries = spt_entries,
+ cgi_hpc_info = hpc_info } = cgguts
-------------------
-- ADD IMPLICIT BINDINGS
@@ -2194,8 +2202,22 @@ hscGenerateByteCode hsc_env cgguts location = do
let (stg_binds,_stg_deps) = unzip stg_binds_with_deps
+ -------------------
+ -- Setup HPC info
+ let
+ -- Strict to not retain a reference to the 'CgInteractiveGuts' of 'cgguts'
+ !bytecodeHpcInfo = case hpc_info of
+ NoHpcInfo -> Strict.Nothing
+ HpcInfo{hpcInfoTickCount, hpcInfoHash} ->
+ Strict.Just ByteCodeHpcInfo
+ { bchi_tick_count = hpcInfoTickCount
+ , bchi_hash = hpcInfoHash
+ , bchi_tickboxes = BS8.pack . (++ "\0") . showSDoc dflags $ hpcTickBoxes platform this_mod
+ , bchi_module_name = BS8.pack . (++ "\0") . showSDoc dflags $ ppr this_mod
+ }
+
----------------- Generate byte code ------------------
- byteCodeGen hsc_env this_mod stg_binds tycons mod_breaks spt_entries
+ byteCodeGen hsc_env this_mod stg_binds tycons mod_breaks spt_entries bytecodeHpcInfo
-- | Generate a byte code object linkable and write it to a file if `-fwrite-byte-code` is enabled.
generateAndWriteByteCode :: HscEnv -> CgInteractiveGuts -> ModLocation -> IO ModuleByteCode
@@ -2844,6 +2866,7 @@ hscCompileCoreExpr' hsc_env srcspan ds_expr = do
[]
Nothing -- modbreaks
[] -- spt entries
+ Strict.Nothing -- no hpc info
{- load it -}
bco_time <- getCurrentTime
=====================================
compiler/GHC/HsToCore/Coverage.hs
=====================================
@@ -6,6 +6,9 @@
module GHC.HsToCore.Coverage
( writeMixEntries
, hpcInitCode
+ , hpcStubLabel
+ , hpcModuleName
+ , hpcTickBoxes
) where
import GHC.Prelude as Prelude
@@ -116,24 +119,33 @@ hpcInitCode _ _ (NoHpcInfo {}) = mempty
hpcInitCode platform this_mod (HpcInfo tickCount hashNo)
= initializerCStub platform fn_name decls body
where
- fn_name = mkInitializerStubLabel this_mod (fsLit "hpc")
+ fn_name = hpcStubLabel this_mod
decls = text "StgWord64 " <> tickboxes <> brackets (int tickCount) <> semi
body = text "hs_hpc_module" <>
parens (hcat (punctuate comma [
- doubleQuotes full_name_str,
+ doubleQuotes (hpcModuleName this_mod),
int tickCount, -- really StgWord32
int hashNo, -- really StgWord32
tickboxes
])) <> semi
+ tickboxes = hpcTickBoxes platform this_mod
- tickboxes = pprCLabel platform (mkHpcTicksLabel $ this_mod)
-
- module_name = hcat (map (text.charToC) $ BS.unpack $
- bytesFS (moduleNameFS (moduleName this_mod)))
- package_name = hcat (map (text.charToC) $ BS.unpack $
- bytesFS (unitFS (moduleUnit this_mod)))
- full_name_str
- | moduleUnit this_mod == mainUnit
- = module_name
- | otherwise
- = package_name <> char '/' <> module_name
+hpcStubLabel :: Module -> CLabel
+hpcStubLabel this_mod = mkInitializerStubLabel this_mod (fsLit "hpc")
+
+hpcModuleName :: Module -> SDoc
+hpcModuleName this_mod = full_name_str
+ where
+ full_name_str
+ | moduleUnit this_mod == mainUnit
+ = module_name
+ | otherwise
+ = package_name <> char '/' <> module_name
+ module_name = hcat (map (text.charToC) $ BS.unpack $
+ bytesFS (moduleNameFS (moduleName this_mod)))
+
+ package_name = hcat (map (text.charToC) $ BS.unpack $
+ bytesFS (unitFS (moduleUnit this_mod)))
+
+hpcTickBoxes :: Platform -> Module -> SDoc
+hpcTickBoxes platform this_mod = pprCLabel platform (mkHpcTicksLabel this_mod)
=====================================
compiler/GHC/Iface/Tidy.hs
=====================================
@@ -402,6 +402,7 @@ tidyProgram opts (ModGuts { mg_module = mod
, mg_foreign_files = foreign_files
, mg_modBreaks = modBreaks
, mg_boot_exports = boot_exports
+ , mg_hpc_info = hpc_info
}) = do
(unfold_env, tidy_occ_env) <- chooseExternalIds opts mod tcs binds imp_rules
@@ -471,6 +472,7 @@ tidyProgram opts (ModGuts { mg_module = mod
, cg_dep_pkgs = S.map snd (dep_direct_pkgs deps)
, cg_modBreaks = modBreaks
, cg_spt_entries = spt_entries
+ , cg_hpc_info = hpc_info
}
, ModDetails { md_types = tidy_type_env
, md_rules = tidy_rules
=====================================
compiler/GHC/Linker/ByteCode.hs
=====================================
@@ -67,4 +67,4 @@ mkInterpreterLib hsc_env files =
return $ Just (InterpreterSharedObject foreign_stub_lib_path foreign_stub_lib_dir foreign_stub_lib_name)
Nothing -> pure Nothing
False -> do
- pure $ Just (InterpreterStaticObjects files)
\ No newline at end of file
+ pure $ Just (InterpreterStaticObjects files)
=====================================
compiler/GHC/Linker/Loader.hs
=====================================
@@ -137,6 +137,10 @@ import qualified Data.IntMap.Strict as IM
import qualified Data.Map.Strict as M
import Foreign.Ptr (nullPtr)
import GHC.ByteCode.Serialize
+-- TODO: this import is wrong
+import GHC.HsToCore.Coverage (hpcModuleName)
+import qualified Data.ByteString.Char8 as BS8
+import qualified GHC.Data.Strict as Strict
-- Note [Linkers and loaders]
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -988,10 +992,9 @@ dynLinkBCOs interp pls keep_spec bcos =
let (bcos_loaded', new_bcos) = rmDupLinkables (bcos_loaded pls) bcos
pls1 = pls { bcos_loaded = bcos_loaded' }
- cbcs :: [CompiledByteCode]
- cbcs = concatMap linkableBCOs new_bcos
+ mbcs = concatMap linkableBCOs new_bcos
in do
- bco_state <- dynLinkCompiledByteCode interp (pkgs_loaded pls) (bco_loader_state pls) traverseHomePackageBytecodeState keep_spec cbcs
+ bco_state <- dynLinkCompiledByteCode interp (pkgs_loaded pls) (bco_loader_state pls) traverseHomePackageBytecodeState keep_spec mbcs
return $! pls1 { bco_loader_state = bco_state }
dynLinkCompiledByteCode :: Interp
@@ -1001,22 +1004,22 @@ dynLinkCompiledByteCode :: Interp
-> KeepModuleLinkableDefinitions
-> [CompiledByteCode]
-> IO BytecodeLoaderState
-dynLinkCompiledByteCode interp pkgs_loaded whole_bytecode_state traverse_bytecode_state keep_spec cbcs = do
+dynLinkCompiledByteCode interp pkgs_loaded whole_bytecode_state traverse_bytecode_state keep_spec mbcs = do
st1 <- traverse_bytecode_state whole_bytecode_state $ \bytecode_state -> do
let
le1 = bco_linker_env bytecode_state
lb1 = bco_linked_breaks bytecode_state
- ie2 <- linkITbls interp (itbl_env le1) (concatMap bc_itbls cbcs)
- ae2 <- foldlM (\env cbc -> allocateTopStrings interp (bc_strs cbc) env) (addr_env le1) cbcs
- be2 <- allocateBreakArrays interp (breakarray_env lb1) (catMaybes $ map bc_breaks cbcs)
- ce2 <- allocateCCS interp (ccs_env lb1) (catMaybes $ map bc_breaks cbcs)
+ ie2 <- linkITbls interp (itbl_env le1) (concatMap bc_itbls mbcs)
+ ae2 <- foldlM (\env cbc -> allocateTopStrings interp (bc_strs cbc) env) (addr_env le1) mbcs
+ be2 <- allocateBreakArrays interp (breakarray_env lb1) (catMaybes $ map bc_breaks mbcs)
+ ce2 <- allocateCCS interp (ccs_env lb1) (catMaybes $ map bc_breaks mbcs)
let le2 = le1 { itbl_env = ie2, addr_env = ae2 }
let lb2 = lb1 { breakarray_env = be2, ccs_env = ce2 }
return $! bytecode_state { bco_linker_env = le2, bco_linked_breaks = lb2 }
-- NB: Important to pass the whole bytecode loader state to linkSomeBCOs so that you can find Names in local
-- and external packages.
- names_and_refs <- linkSomeBCOs interp pkgs_loaded st1 cbcs
+ names_and_refs <- linkSomeBCOs interp pkgs_loaded st1 mbcs
-- We only want to add the external ones to the ClosureEnv
let (to_add, to_drop) = partition (keepDefinitions keep_spec . fst) names_and_refs
@@ -1029,7 +1032,9 @@ dynLinkCompiledByteCode interp pkgs_loaded whole_bytecode_state traverse_bytecod
traverse_bytecode_state st1 $ \bytecode_state -> do
let ce2 = extendClosureEnv (closure_env (bco_linker_env bytecode_state)) new_binds
-- Add SPT entries
- mapM_ (linkSptEntry interp ce2) (concatMap bc_spt_entries cbcs)
+ mapM_ (linkSptEntry interp ce2) (concatMap bc_spt_entries mbcs)
+ -- Load HPC modules
+ mapM_ (linkHpcEntry interp . bc_hpc_info) mbcs
return $! bytecode_state { bco_linker_env = (bco_linker_env bytecode_state) { closure_env = ce2 } }
-- | Register SPT entries for this module in the interpreter
@@ -1042,8 +1047,14 @@ linkSptEntry interp ce (SptEntry name fpr) = do
Nothing -> pprPanic "linkSptEntry" (ppr name)
Just (_, hval) -> addSptEntry interp fpr hval
-
-
+linkHpcEntry :: Interp -> Strict.Maybe ByteCodeHpcInfo -> IO ()
+linkHpcEntry _interp Strict.Nothing = pure ()
+linkHpcEntry interp (Strict.Just info) = do
+ addHpcModule interp
+ (bchi_module_name info)
+ (bchi_tick_count info)
+ (bchi_hash info)
+ (bchi_tickboxes info)
-- Link a bunch of BCOs and return references to their values
linkSomeBCOs :: Interp
=====================================
compiler/GHC/Linker/Types.hs
=====================================
@@ -204,36 +204,37 @@ data BytecodeLoaderState = BytecodeLoaderState
-- ^ Information about bytecode objects from the home package we have loaded into the interpreter.
, externalPackage_loaded :: BytecodeState
-- ^ Information about bytecode objects from external packages we have loaded into the interpreter.
+ , hpcInitialised :: !Bool
}
-- | Find a name loaded from bytecode
lookupNameBytecodeState :: BytecodeLoaderState -> Name -> Maybe (Name, ForeignHValue)
-lookupNameBytecodeState (BytecodeLoaderState home_package external_package) name = do
+lookupNameBytecodeState (BytecodeLoaderState home_package external_package _) name = do
lookupNameEnv (closure_env (bco_linker_env home_package)) name
<|> lookupNameEnv (closure_env (bco_linker_env external_package)) name
-- | Look up a break array in the bytecode loader state.
lookupBreakArrayBytecodeState :: BytecodeLoaderState -> Module -> Maybe (ForeignRef BreakArray)
-lookupBreakArrayBytecodeState (BytecodeLoaderState home_package external_package) break_mod = do
+lookupBreakArrayBytecodeState (BytecodeLoaderState home_package external_package _) break_mod = do
lookupModuleEnv (breakarray_env (bco_linked_breaks home_package)) break_mod
<|> lookupModuleEnv (breakarray_env (bco_linked_breaks external_package)) break_mod
-- | Look up an info table in the bytecode loader state.
lookupInfoTableBytecodeState :: BytecodeLoaderState -> Name -> Maybe (Name, ItblPtr)
-lookupInfoTableBytecodeState (BytecodeLoaderState home_package external_package) info_mod = do
+lookupInfoTableBytecodeState (BytecodeLoaderState home_package external_package _) info_mod = do
lookupNameEnv (itbl_env (bco_linker_env home_package)) info_mod
<|> lookupNameEnv (itbl_env (bco_linker_env external_package)) info_mod
-- | Look up an address in the bytecode loader state.
lookupAddressBytecodeState :: BytecodeLoaderState -> Name -> Maybe (Name, AddrPtr)
-lookupAddressBytecodeState (BytecodeLoaderState home_package external_package) addr_mod = do
+lookupAddressBytecodeState (BytecodeLoaderState home_package external_package _) addr_mod = do
lookupNameEnv (addr_env (bco_linker_env home_package)) addr_mod
<|> lookupNameEnv (addr_env (bco_linker_env external_package)) addr_mod
-- | Look up a cost centre stack in the bytecode loader state.
lookupCCSBytecodeState :: BytecodeLoaderState -> Module -> Maybe (Array BreakTickIndex (RemotePtr CostCentre))
-lookupCCSBytecodeState (BytecodeLoaderState home_package external_package) ccs_mod = do
+lookupCCSBytecodeState (BytecodeLoaderState home_package external_package _) ccs_mod = do
lookupModuleEnv (ccs_env (bco_linked_breaks home_package)) ccs_mod
<|> lookupModuleEnv (ccs_env (bco_linked_breaks external_package)) ccs_mod
@@ -241,6 +242,7 @@ emptyBytecodeLoaderState :: BytecodeLoaderState
emptyBytecodeLoaderState = BytecodeLoaderState
{ homePackage_loaded = emptyBytecodeState
, externalPackage_loaded = emptyBytecodeState
+ , hpcInitialised = False
}
emptyBytecodeState :: BytecodeState
=====================================
compiler/GHC/Runtime/Interpreter.hs
=====================================
@@ -17,6 +17,7 @@ module GHC.Runtime.Interpreter
, mallocData
, createBCOs
, addSptEntry
+ , addHpcModule
, mkCostCentres
, costCentreStackInfo
, newBreakArray
@@ -366,6 +367,10 @@ addSptEntry interp fpr ref =
withForeignRef ref $ \val ->
interpCmd interp (AddSptEntry fpr val)
+addHpcModule :: Interp -> ByteString -> Int -> Int -> ByteString -> IO ()
+addHpcModule interp modLabel tickNo hash tickboxes =
+ interpCmd interp (AddHpcModule modLabel tickNo hash tickboxes)
+
costCentreStackInfo :: Interp -> RemotePtr CostCentreStack -> IO [String]
costCentreStackInfo interp ccs =
interpCmd interp (CostCentreStackInfo ccs)
=====================================
compiler/GHC/StgToByteCode.hs
=====================================
@@ -74,7 +74,6 @@ import Data.List ( genericReplicate, intersperse
import Foreign hiding (shiftL, shiftR)
import Control.Monad
import Data.Char
-import Data.Word
import GHC.Unit.Module
@@ -98,6 +97,7 @@ import Control.Monad.IO.Class
import Control.Monad.Trans.Reader (ReaderT(..))
import Control.Monad.Trans.State (StateT(..))
import Data.Bifunctor (Bifunctor(..))
+import qualified GHC.Data.Strict as Strict
-- -----------------------------------------------------------------------------
-- Generating byte code for a complete module
@@ -108,8 +108,9 @@ byteCodeGen :: HscEnv
-> [TyCon]
-> Maybe ModBreaks
-> [SptEntry]
+ -> Strict.Maybe ByteCodeHpcInfo
-> IO CompiledByteCode
-byteCodeGen hsc_env this_mod binds tycs mb_modBreaks spt_entries
+byteCodeGen hsc_env this_mod binds tycs mb_modBreaks spt_entries hpc_info
= withTiming logger
(text "GHC.StgToByteCode"<+>brackets (ppr this_mod))
(const ()) $ do
@@ -135,7 +136,7 @@ byteCodeGen hsc_env this_mod binds tycs mb_modBreaks spt_entries
let mod_breaks = case mb_modBreaks of
Nothing -> Nothing
Just mb -> Just $ mkInternalModBreaks this_mod breakInfo mb
- cbc <- assembleBCOs profile proto_bcos tycs strings mod_breaks spt_entries
+ cbc <- assembleBCOs profile proto_bcos tycs strings mod_breaks spt_entries hpc_info
-- Squash space leaks in the CompiledByteCode. This is really
-- important, because when loading a set of modules into GHCi
=====================================
compiler/GHC/Types/HpcInfo.hs
=====================================
@@ -18,4 +18,3 @@ data HpcInfo
emptyHpcInfo :: HpcInfo
emptyHpcInfo = NoHpcInfo
-
=====================================
compiler/GHC/Unit/Module/ModGuts.hs
=====================================
@@ -141,8 +141,9 @@ data CgGuts
cg_dep_pkgs :: !(Set UnitId), -- ^ Dependent packages, used to
-- generate #includes for C code gen
cg_modBreaks :: !(Maybe ModBreaks), -- ^ Module breakpoints
- cg_spt_entries :: [SptEntry]
+ cg_spt_entries :: [SptEntry],
-- ^ Static pointer table entries for static forms defined in
-- the module.
-- See Note [Grand plan for static forms] in "GHC.Iface.Tidy.StaticPtrTable"
+ cg_hpc_info :: HpcInfo
}
=====================================
libraries/ghci/GHCi/Coverage.hs
=====================================
@@ -0,0 +1,32 @@
+{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE ForeignFunctionInterface #-}
+{-# LANGUAGE LambdaCase #-}
+
+module GHCi.Coverage ( hpcAddModule ) where
+
+import Prelude -- See note [Why do we import Prelude here?]
+import Data.Word
+import Foreign
+import GHC.Fingerprint
+import GHCi.RemoteTypes
+import Data.ByteString
+import GHC.Foreign (CString)
+import qualified Data.ByteString.Unsafe as B
+import qualified Data.ByteString.Char8 as BS8
+import GHCi.ObjLink (lookupSymbol)
+import Debug.Trace
+
+hpcAddModule :: ByteString -> Int -> Int -> ByteString -> IO ()
+hpcAddModule modl ticks hash tickboxes = do
+ B.unsafeUseAsCString modl $ \modlLiteral -> do
+ lookupSymbol (BS8.unpack tickboxes) >>= \ case
+ Nothing -> pure ()
+ Just tickBoxRef -> do
+ hpc_register_module modlLiteral (fromIntegral ticks) (fromIntegral hash) (castPtr tickBoxRef)
+ hpc_startup
+
+foreign import ccall "hs_hpc_module"
+ hpc_register_module :: CString -> Word32 -> Word32 -> Ptr Word64 -> IO ()
+
+foreign import ccall "startupHpc"
+ hpc_startup :: IO ()
=====================================
libraries/ghci/GHCi/Message.hs
=====================================
@@ -111,6 +111,8 @@ data Message a where
-- | Add entries to the Static Pointer Table
AddSptEntry :: Fingerprint -> HValueRef -> Message ()
+ -- | Add module to hpc
+ AddHpcModule :: ByteString -> Int -> Int -> ByteString -> Message ()
-- | Malloc some data and return a 'RemotePtr' to it
MallocData :: ByteString -> Message (RemotePtr ())
@@ -602,6 +604,7 @@ getMessage = do
38 -> Msg <$> (ResumeSeq <$> get)
39 -> Msg <$> (LookupSymbolInDLL <$> get <*> get)
40 -> Msg <$> (WhereFrom <$> get)
+ 41 -> Msg <$> (AddHpcModule <$> get <*> get <*> get <*> get)
_ -> error $ "Unknown Message code " ++ (show b)
putMessage :: Message a -> Put
@@ -648,6 +651,7 @@ putMessage m = case m of
ResumeSeq a -> putWord8 38 >> put a
LookupSymbolInDLL dll str -> putWord8 39 >> put dll >> put str
WhereFrom a -> putWord8 40 >> put a
+ AddHpcModule lbl ticks hash tickboxes -> putWord8 41 >> put lbl >> put ticks >> put hash >> put tickboxes
{-
Note [Parallelize CreateBCOs serialization]
=====================================
libraries/ghci/GHCi/Run.hs
=====================================
@@ -19,6 +19,7 @@ import GHCi.CreateBCO
import GHCi.InfoTable
#endif
+import GHCi.Coverage
import qualified GHC.InfoProv as InfoProv
import GHCi.Debugger
import GHCi.FFI
@@ -88,6 +89,7 @@ run m = case m of
fmap toRemotePtr <$> lookupSymbolInDLL (fromRemotePtr dll) str
FreeHValueRefs rs -> mapM_ freeRemoteRef rs
AddSptEntry fpr r -> localRef r >>= sptAddEntry fpr
+ AddHpcModule modl ticks hash tickboxes -> hpcAddModule modl ticks hash tickboxes
EvalStmt opts r -> evalStmt opts r
ResumeStmt opts r -> resumeStmt opts r
AbandonStmt r -> abandonStmt r
=====================================
libraries/ghci/ghci.cabal.in
=====================================
@@ -59,6 +59,7 @@ library
if flag(internal-interpreter)
CPP-Options: -DHAVE_INTERNAL_INTERPRETER
exposed-modules:
+ GHCi.Coverage
GHCi.Run
GHCi.Debugger
GHCi.CreateBCO
=====================================
rts/Hpc.c
=====================================
@@ -323,8 +323,6 @@ hs_hpc_module(char *modName,
}
tmpModule->from_file = false;
}
-
- startupHpc();
}
static void
=====================================
rts/Interpreter.c
=====================================
@@ -1740,7 +1740,6 @@ run_BCO:
&&lbl_bci_PRIMCALL - &&lbl_bci_DEFAULT,
&&lbl_bci_BCO_NAME - &&lbl_bci_DEFAULT,
&&lbl_bci_HPC_TICK - &&lbl_bci_DEFAULT,
- &&lbl_bci_DEFAULT - &&lbl_bci_DEFAULT,
&&lbl_bci_OP_ADD_64 - &&lbl_bci_DEFAULT,
&&lbl_bci_OP_SUB_64 - &&lbl_bci_DEFAULT,
&&lbl_bci_OP_AND_64 - &&lbl_bci_DEFAULT,
@@ -2111,6 +2110,9 @@ run_BCO:
W_ arg1_ticks_array, arg2_tick_index;
arg1_ticks_array = BCO_GET_LARGE_ARG;
arg2_tick_index = BCO_READ_NEXT_32;
+ IF_DEBUG(hpc,
+ debugBelch("\tHPC Tick %lu %lu %lu\n", BCO_LIT(arg1_ticks_array), arg1_ticks_array, arg2_tick_index);
+ );
((StgWord64*)BCO_LIT(arg1_ticks_array))[arg2_tick_index]++;
NEXT_INSTRUCTION;
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/6832ae4471e3f75e4484e078689209d…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/6832ae4471e3f75e4484e078689209d…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
Simon Peyton Jones pushed to branch wip/T27078 at Glasgow Haskell Compiler / GHC
Commits:
e4baa453 by Simon Peyton Jones at 2026-04-02T08:49:13+01:00
Wibbles
- - - - -
1 changed file:
- compiler/GHC/Core/Lint.hs
Changes:
=====================================
compiler/GHC/Core/Lint.hs
=====================================
@@ -423,8 +423,7 @@ lintCoreBindings' cfg binds
; checkL (null ext_dups) (dupExtVars ext_dups)
-- Typecheck the bindings
- ; lintRecBindings TopLevel all_pairs $ \_ ->
- return () }
+ ; lintRecBindings TopLevel all_pairs $ return () }
where
all_pairs = flattenBinds binds
-- Put all the top-level binders in scope at the start
@@ -2359,7 +2358,7 @@ lintCoercion co@(ForAllCo {})
= do { mb_lk <- case kind_mco of
MRefl -> return Nothing
MCo kind_co -> Just <$> lintStarCoercion kind_co
- ; lintTyCoBndr tcv $
+ ; lintTyCoBndr tcv $
do { case mb_lk of
Nothing -> return ()
Just lk -> ensureEqTys (varType tcv) lk $
@@ -2806,7 +2805,7 @@ lint_axiom ax@(CoAxiom { co_ax_tc = tc, co_ax_branches = branches
lint_branch :: TyCon -> CoAxBranch -> LintM ()
lint_branch ax_tc (CoAxBranch { cab_tvs = tvs, cab_cvs = cvs
, cab_lhs = lhs_args, cab_rhs = rhs })
- = lintBinders LambdaBind (tvs ++ cvs) $
+ = lintBinders LambdaBind (tvs ++ cvs) $
do { let lhs = mkTyConApp ax_tc lhs_args
; lintType lhs
; lintType rhs
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/e4baa453015a7134d49da08309f37cf…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/e4baa453015a7134d49da08309f37cf…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/dcoutts/posix-ticker] 13 commits: Bump default language edition to GHC2024
by Duncan Coutts (@dcoutts) 02 Apr '26
by Duncan Coutts (@dcoutts) 02 Apr '26
02 Apr '26
Duncan Coutts pushed to branch wip/dcoutts/posix-ticker at Glasgow Haskell Compiler / GHC
Commits:
7fbb4fcb by Rodrigo Mesquita at 2026-04-01T12:16:33+00:00
Bump default language edition to GHC2024
As per the accepted ghc-proposal#632
Fixes #26039
- - - - -
5ae43275 by Peng Fan at 2026-04-01T19:01:06-04:00
NCG/LA64: add cmpxchg and xchg primops
And append some new instructions for LA664 uarch.
Apply fix to cmpxchg-prim by Andreas Klebinger.
Suggestions in https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15515
- - - - -
8f95534a by Duncan Coutts at 2026-04-01T19:01:52-04:00
Remove signal-based ticker implementations
Fixes issue #27073
All supported platforms should work with the pthreads + nanosleep based
ticker implementation. This avoids all the problems with using signals.
In practice, all supported platforms were probably using the non-signal
tickers already, which is probably why we do not get lots of reports
about deadlocks and other weirdness: we were definately using functions
that are not async signal safe in the tick handler (such as fflush to
flussh the eventlog).
Only Solaris was explicitly using the timer_create ticker impl, and even
Solaris could probably use the pthreads one (if anyone cared: Solaris is
no longer a Teir 3 supported platform).
Plausibly the only supported platform that this will change will be AIX,
which should now use the pthreads impl.
- - - - -
51b32b0d by Duncan Coutts at 2026-04-01T19:01:52-04:00
Tidy up some timer/ticker comments elsewhere
- - - - -
7562bcd7 by Duncan Coutts at 2026-04-01T19:01:52-04:00
Remove now-unused install_vtalrm_handler
Support function used by both of the signal-based ticker
implementations.
- - - - -
6da127c7 by Duncan Coutts at 2026-04-01T19:01:52-04:00
No longer probe for timer_create in rts/configure
It was only used by the TimerCreate.c ticker impl.
- - - - -
3fd490fa by Duncan Coutts at 2026-04-01T19:01:53-04:00
Note that rtsTimerSignal is deprecated.
- - - - -
63099b0f by Simon Jakobi at 2026-04-01T19:02:39-04:00
Add perf test for #13960
Closes #13960.
- - - - -
af3acc89 by Duncan Coutts at 2026-04-02T08:19:39+01:00
Add a rts posix FdWakup utility module
This will be used to implement wakeupIOManager for in-RTS I/O managers.
It provides a notification/wakeup mechanism using FDs, suitable for
situations when a thread is blocked on a set of fds anyway. It uses the
classic self-pipe trick, or equivalently eventfd on supported platforms.
This will initially be used to implement prompt interrupt or shutdown of
the posix ticker thread.
- - - - -
0d466c40 by Duncan Coutts at 2026-04-02T08:19:39+01:00
Add prompt shutdown to the pthread ticker implementation.
The Linux timerfd ticker monitors a pipe which is used by exitTicker to
ensure a prompt wakeup and shutdown. The pthread ticker lacked this and
so would only exit at the next ticker wakeup (10ms by default).
This patch adds the same mechanism to the pthread ticker.
This changes the pthread ticker from waiting by using nanosleep() to
waiting using either ppoll() or select(), so that it can wait on both
a time and a file descriptor. On Linux at least, a test program to
compare the timing jitter of these APIs shows that using nanpsleep,
ppoll or select makes no statistical difference to the maximum or
average jitter.
This is a step towards unifying the posix ticker implementations, so
that we can have just one portable one (albeit with some limited cpp).
It is also a step towards using the ticker as part of a more general
implementation of wakeUpRts, since this will require a method to wake
the rts from a signal handler context (ctl-c handler).
- - - - -
3e158b1e by Duncan Coutts at 2026-04-02T08:19:39+01:00
Update ticker header commentary
It was antique and didn't apply even to the previous implementation, and
certainly not to the updated one.
- - - - -
ebbfdf15 by Duncan Coutts at 2026-04-02T08:19:39+01:00
Remove the timerfd-based ticker implementation
There does not appear to be any remaining advantage on Linux to using
the timerfd ticker implementation over the portable one (using ppoll on
Linux for precise timing).
The eventfd implementation was originally added at a time when Linux was
still using a signal based implementation. So it made sense at the time.
See (closed) issue #10840.
- - - - -
c6f8847c by Duncan Coutts at 2026-04-02T08:19:39+01:00
Consolidate to a single posix ticker implementation
Previously we had four implementations, two using signals and two using
threads. Having just one should make behaviour more consistent between
platforms, and should make maintenance easier.
- - - - -
142 changed files:
- .gitlab/ci.sh
- compiler/GHC/CmmToAsm/LA64/CodeGen.hs
- compiler/GHC/CmmToAsm/LA64/Instr.hs
- compiler/GHC/CmmToAsm/LA64/Ppr.hs
- compiler/GHC/Driver/Flags.hs
- docs/users_guide/exts/control.rst
- − m4/fp_check_timer_create.m4
- rts/Timer.c
- rts/configure.ac
- rts/include/rts/Timer.h
- rts/include/stg/SMP.h
- + rts/posix/FdWakeup.c
- + rts/posix/FdWakeup.h
- rts/posix/Signals.c
- rts/posix/Signals.h
- rts/posix/Ticker.c
- − rts/posix/ticker/Pthread.c
- − rts/posix/ticker/Setitimer.c
- − rts/posix/ticker/TimerCreate.c
- − rts/posix/ticker/TimerFd.c
- rts/rts.cabal
- testsuite/tests/ado/ado004.hs
- testsuite/tests/annotations/should_fail/annfail02.hs
- testsuite/tests/annotations/should_fail/annfail02.stderr
- testsuite/tests/array/should_run/arr020.hs
- testsuite/tests/core-to-stg/T19700.hs
- testsuite/tests/deSugar/should_fail/DsStrictFail.hs
- testsuite/tests/deriving/should_compile/T15798b.hs
- testsuite/tests/deriving/should_compile/T15798c.hs
- testsuite/tests/deriving/should_compile/T15798c.stderr
- testsuite/tests/deriving/should_compile/T24955a.hs
- testsuite/tests/deriving/should_compile/T24955a.stderr
- testsuite/tests/deriving/should_compile/T24955b.hs
- testsuite/tests/deriving/should_compile/T24955c.hs
- testsuite/tests/deriving/should_fail/T10598_fail4.hs
- testsuite/tests/deriving/should_fail/T10598_fail4.stderr
- testsuite/tests/deriving/should_fail/T10598_fail5.hs
- testsuite/tests/deriving/should_fail/T10598_fail5.stderr
- testsuite/tests/dmdanal/sigs/T22241.hs
- testsuite/tests/gadt/T20485.hs
- testsuite/tests/ghci.debugger/scripts/all.T
- testsuite/tests/ghci.debugger/scripts/break012.hs
- testsuite/tests/ghci.debugger/scripts/break012.stdout
- testsuite/tests/ghci/prog-mhu002/all.T
- testsuite/tests/ghci/scripts/Makefile
- testsuite/tests/ghci/should_run/all.T
- testsuite/tests/indexed-types/should_compile/T15322.hs
- testsuite/tests/indexed-types/should_compile/T15322.stderr
- testsuite/tests/linear/should_fail/T18888.hs
- testsuite/tests/module/T20007.hs
- testsuite/tests/module/T20007.stderr
- testsuite/tests/module/mod90.hs
- testsuite/tests/module/mod90.stderr
- testsuite/tests/overloadedrecflds/should_fail/NoFieldSelectorsFail.hs
- testsuite/tests/overloadedrecflds/should_fail/T18999_NoDisambiguateRecordFields.hs
- testsuite/tests/overloadedrecflds/should_fail/all.T
- testsuite/tests/parser/should_fail/ParserNoLambdaCase.hs
- testsuite/tests/parser/should_fail/ParserNoLambdaCase.stderr
- testsuite/tests/parser/should_fail/T16270h.hs
- testsuite/tests/parser/should_fail/T16270h.stderr
- testsuite/tests/parser/should_fail/readFail001.hs
- testsuite/tests/parser/should_fail/readFail001.stderr
- testsuite/tests/partial-sigs/should_compile/SomethingShowable.hs
- + testsuite/tests/perf/compiler/T13960.hs
- testsuite/tests/perf/compiler/all.T
- testsuite/tests/plugins/late-plugin/LatePlugin.hs
- testsuite/tests/polykinds/T7151.hs
- testsuite/tests/polykinds/T7151.stderr
- testsuite/tests/polykinds/T7433.hs
- testsuite/tests/polykinds/T7433.stderr
- testsuite/tests/programs/andy_cherry/test.T
- testsuite/tests/rename/should_fail/T10668.hs
- testsuite/tests/rename/should_fail/T10668.stderr
- testsuite/tests/rename/should_fail/T12681.hs
- testsuite/tests/rename/should_fail/T12681.stderr
- testsuite/tests/rename/should_fail/T13568.hs
- testsuite/tests/rename/should_fail/T13568.stderr
- testsuite/tests/rename/should_fail/T13644.hs
- testsuite/tests/rename/should_fail/T13644.stderr
- testsuite/tests/rename/should_fail/T13847.hs
- testsuite/tests/rename/should_fail/T13847.stderr
- testsuite/tests/rename/should_fail/T14032c.hs
- testsuite/tests/rename/should_fail/T19843l.hs
- testsuite/tests/rename/should_fail/T19843l.stderr
- testsuite/tests/rename/should_fail/T25901_imp_hq_fail_5.stderr
- testsuite/tests/rename/should_fail/T25901_imp_sq_fail_2.stderr
- testsuite/tests/rename/should_fail/T5385.hs
- testsuite/tests/rename/should_fail/T5385.stderr
- testsuite/tests/roles/should_fail/Roles5.hs
- testsuite/tests/roles/should_fail/Roles5.stderr
- testsuite/tests/showIface/DocsInHiFile.hs
- testsuite/tests/showIface/DocsInHiFile1.stdout
- testsuite/tests/showIface/DocsInHiFileTH.hs
- testsuite/tests/showIface/DocsInHiFileTH.stdout
- testsuite/tests/showIface/DocsInHiFileTHExternal.hs
- testsuite/tests/showIface/HaddockIssue849.hs
- testsuite/tests/showIface/HaddockIssue849.stdout
- testsuite/tests/showIface/HaddockOpts.hs
- testsuite/tests/showIface/HaddockOpts.stdout
- testsuite/tests/showIface/HaddockSpanIssueT24378.hs
- testsuite/tests/showIface/HaddockSpanIssueT24378.stdout
- testsuite/tests/showIface/MagicHashInHaddocks.hs
- testsuite/tests/showIface/MagicHashInHaddocks.stdout
- testsuite/tests/showIface/Makefile
- testsuite/tests/showIface/NoExportList.hs
- testsuite/tests/showIface/NoExportList.stdout
- testsuite/tests/showIface/PragmaDocs.stdout
- testsuite/tests/showIface/ReExports.stdout
- testsuite/tests/simplCore/T9646/test.T
- testsuite/tests/simplCore/should_compile/T21960.hs
- testsuite/tests/simplCore/should_compile/T26709.stderr
- testsuite/tests/th/TH_Promoted1Tuple.hs
- testsuite/tests/th/TH_Roles1.hs
- testsuite/tests/typecheck/should_compile/MutRec.hs
- testsuite/tests/typecheck/should_compile/T10770a.hs
- testsuite/tests/typecheck/should_compile/T11339.hs
- testsuite/tests/typecheck/should_compile/T11397.hs
- testsuite/tests/typecheck/should_compile/T13526.hs
- testsuite/tests/typecheck/should_compile/T18467.hs
- testsuite/tests/typecheck/should_compile/T18467.stderr
- testsuite/tests/typecheck/should_compile/tc081.hs
- testsuite/tests/typecheck/should_compile/tc141.hs
- testsuite/tests/typecheck/should_fail/T23427.hs
- testsuite/tests/typecheck/should_fail/T6078.hs
- testsuite/tests/typecheck/should_fail/T7453.hs
- testsuite/tests/typecheck/should_fail/T7453.stderr
- testsuite/tests/typecheck/should_fail/T8570.hs
- testsuite/tests/typecheck/should_fail/T8570.stderr
- testsuite/tests/typecheck/should_fail/tcfail083.hs
- testsuite/tests/typecheck/should_fail/tcfail083.stderr
- testsuite/tests/typecheck/should_fail/tcfail084.hs
- testsuite/tests/typecheck/should_fail/tcfail084.stderr
- testsuite/tests/typecheck/should_fail/tcfail094.hs
- testsuite/tests/typecheck/should_fail/tcfail094.stderr
- testsuite/tests/typecheck/should_run/T1735.hs
- testsuite/tests/typecheck/should_run/T1735_Help/Basics.hs
- testsuite/tests/typecheck/should_run/T3731.hs
- testsuite/tests/vdq-rta/should_fail/T24159_type_syntax_th_fail.script
- testsuite/tests/warnings/should_fail/CaretDiagnostics1.hs
- testsuite/tests/warnings/should_fail/CaretDiagnostics1.stderr
- testsuite/tests/warnings/should_fail/T24396c.hs
- testsuite/tests/warnings/should_fail/T24396c.stderr
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/4f1b216f4b7517ac212fac9bd60e7a…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/4f1b216f4b7517ac212fac9bd60e7a…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/T27078] Delete the substition stuff in Lint
by Simon Peyton Jones (@simonpj) 01 Apr '26
by Simon Peyton Jones (@simonpj) 01 Apr '26
01 Apr '26
Simon Peyton Jones pushed to branch wip/T27078 at Glasgow Haskell Compiler / GHC
Commits:
28e0ef3c by Simon Peyton Jones at 2026-04-02T00:09:09+01:00
Delete the substition stuff in Lint
- - - - -
1 changed file:
- compiler/GHC/Core/Lint.hs
Changes:
=====================================
compiler/GHC/Core/Lint.hs
=====================================
@@ -179,65 +179,7 @@ Note [Linting function types]
All saturated applications of funTyCon are represented with the FunTy constructor.
See Note [Function type constructors and FunTy] in GHC.Builtin.Types.Prim
- We check this invariant in lintType.
-
-Note [Linting type lets]
-~~~~~~~~~~~~~~~~~~~~~~~~
-In the desugarer, it's very very convenient to be able to say (in effect)
- let a = Type Bool in
- let x::a = True in <body>
-That is, use a type let. See Note [Core type and coercion invariant] in "GHC.Core".
-One place it is used is in mkWwBodies; see Note [Join points and beta-redexes]
-in GHC.Core.Opt.WorkWrap.Utils. (Maybe there are other "clients" of this feature; I'm not sure).
-
-* Hence when linting <body> we need to remember that a=Int, else we
- might reject a correct program. So we carry a type substitution (in
- this example [a -> Bool]) and apply this substitution before
- comparing types. In effect, in Lint, type equality is always
- equality-modulo-le-subst. This is in the le_subst field of
- LintEnv. But nota bene:
-
- (SI1) The le_subst substitution is applied to types and coercions only
-
- (SI2) The result of that substitution is used only to check for type
- equality, to check well-typed-ness, /but is then discarded/.
- The result of substitution does not outlive the CoreLint pass.
-
- (SI3) The InScopeSet of le_subst includes only TyVar and CoVar binders.
-
-* The function
- lintInTy :: Type -> LintM (Type, Kind)
- returns a substituted type.
-
-* When we encounter a binder (like x::a) we must apply the substitution
- to the type of the binding variable. lintBinders does this.
-
-* Clearly we need to clone tyvar binders as we go.
-
-* But take care (#17590)! We must also clone CoVar binders:
- let a = TYPE (ty |> cv)
- in \cv -> blah
- blindly substituting for `a` might capture `cv`.
-
-* Alas, when cloning a coercion variable we might choose a unique
- that happens to clash with an inner Id, thus
- \cv_66 -> let wild_X7 = blah in blah
- We decide to clone `cv_66` because it's already in scope. Fine,
- choose a new unique. Aha, X7 looks good. So we check the lambda
- body with le_subst of [cv_66 :-> cv_X7]
-
- This is all fine, even though we use the same unique as wild_X7.
- As (SI2) says, we do /not/ return a new lambda
- (\cv_X7 -> let wild_X7 = blah in ...)
- We simply use the le_subst substitution in types/coercions only, when
- checking for equality.
-
-* We still need to check that Id occurrences are bound by some
- enclosing binding. We do /not/ use the InScopeSet for the le_subst
- for this purpose -- it contains only TyCoVars. Instead we have a separate
- le_ids for the in-scope Id binders.
-
-Sigh. We might want to explore getting rid of type-let!
+We check this invariant in lintType.
Note [Bad unsafe coercion]
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -564,11 +506,11 @@ Check a core binding, returning the list of variables bound.
-- Let
lintRecBindings :: TopLevelFlag -> [(Id, CoreExpr)]
- -> ([OutId] -> LintM a) -> LintM (a, [UsageEnv])
+ -> LintM a -> LintM (a, [UsageEnv])
lintRecBindings top_lvl pairs thing_inside
- = lintIdBndrs top_lvl bndrs $ \ bndrs' ->
- do { ues <- zipWithM lint_pair bndrs' rhss
- ; a <- thing_inside bndrs'
+ = lintIdBndrs top_lvl bndrs $
+ do { ues <- zipWithM lint_pair bndrs rhss
+ ; a <- thing_inside
; return (a, ues) }
where
(bndrs, rhss) = unzip pairs
@@ -578,14 +520,14 @@ lintRecBindings top_lvl pairs thing_inside
; lintLetBind top_lvl Recursive bndr' rhs rhs_ty
; return ue }
-lintLetBody :: LintLocInfo -> [OutId] -> CoreExpr -> LintM (OutType, UsageEnv)
+lintLetBody :: LintLocInfo -> [Id] -> CoreExpr -> LintM (Type, UsageEnv)
lintLetBody loc bndrs body
= do { (body_ty, body_ue) <- addLoc loc (lintCoreExpr body)
; mapM_ (lintJoinBndrType body_ty) bndrs
; return (body_ty, body_ue) }
-lintLetBind :: TopLevelFlag -> RecFlag -> OutId
- -> CoreExpr -> OutType -> LintM ()
+lintLetBind :: TopLevelFlag -> RecFlag -> Id
+ -> CoreExpr -> Type -> LintM ()
-- Binder's type, and the RHS, have already been linted
-- This function checks other invariants
lintLetBind top_lvl rec_flag binder rhs rhs_ty
@@ -676,7 +618,7 @@ lintLetBind top_lvl rec_flag binder rhs rhs_ty
-- join point.
--
-- See Note [Checking StaticPtrs].
-lintRhs :: Id -> CoreExpr -> LintM (OutType, UsageEnv)
+lintRhs :: Id -> CoreExpr -> LintM (Type, UsageEnv)
-- NB: the Id can be Linted or not -- it's only used for
-- its OccInfo and join-pointer-hood
lintRhs bndr rhs
@@ -691,7 +633,7 @@ lintRhs _bndr rhs = fmap lf_check_static_ptrs getLintFlags >>= go
where
-- Allow occurrences of 'makeStatic' at the top-level but produce errors
-- otherwise.
- go :: StaticPtrCheck -> LintM (OutType, UsageEnv)
+ go :: StaticPtrCheck -> LintM (Type, UsageEnv)
go AllowAtTopLevel
| (binders0, rhs') <- collectTyBinders rhs
, Just (fun, t, info, e) <- collectMakeStaticArgs rhs'
@@ -708,7 +650,7 @@ lintRhs _bndr rhs = fmap lf_check_static_ptrs getLintFlags >>= go
-- | Lint the RHS of a join point with expected join arity of @n@ (see Note
-- [Join points] in "GHC.Core").
-lintJoinLams :: JoinArity -> Maybe Id -> CoreExpr -> LintM (OutType, UsageEnv)
+lintJoinLams :: JoinArity -> Maybe Id -> CoreExpr -> LintM (Type, UsageEnv)
lintJoinLams join_arity enforce rhs
= go join_arity rhs
where
@@ -896,13 +838,8 @@ suspicious and worth investigating if you have a seg-fault or bizarre behaviour.
************************************************************************
-}
-lintCoreExpr :: InExpr -> LintM (OutType, UsageEnv)
--- The returned type has the substitution from the monad
--- already applied to it:
--- lintCoreExpr e subst = exprType (subst e)
---
--- The returned "type" can be a kind, if the expression is (Type ty)
-
+lintCoreExpr :: CoreExpr -> LintM (Type, UsageEnv)
+-- The returned type is the type of the expression
-- If you edit this function, you may need to update the GHC formalism
-- See Note [GHC Formalism]
@@ -929,7 +866,7 @@ lintCoreExpr (Cast expr co)
; lintCoercion co
; lintRole co Representational (coercionRole co)
- ; Pair from_ty to_ty <- substCoKindM co
+ ; let Pair from_ty to_ty = coercionKind co
; checkValueType (typeKind to_ty) $
text "target of cast" <+> quotes (ppr co)
; ensureEqTys from_ty expr_ty (mkCastErr expr co from_ty expr_ty)
@@ -944,13 +881,12 @@ lintCoreExpr (Tick tickish expr)
lintCoreExpr (Let (NonRec tv (Type ty)) body)
| isTyVar tv
= -- See Note [Linting type lets]
- do { ty' <- lintTypeAndSubst ty
- ; lintTyCoBndr tv $ \ tv' ->
- do { addLoc (RhsOf tv) $ lintTyKind tv' ty'
+ do { lintType ty
+ ; lintTyCoBndr tv $
+ do { addLoc (RhsOf tv) $ lintTyKind tv ty
-- Now extend the substitution so we
-- take advantage of it in the body
- ; -- extendTvSubstL tv ty' $
- addLoc (BodyOfLet tv) $
+ ; addLoc (BodyOfLet tv) $
lintCoreExpr body } }
lintCoreExpr (Let (NonRec bndr rhs) body)
@@ -960,10 +896,10 @@ lintCoreExpr (Let (NonRec bndr rhs) body)
-- See Note [Multiplicity of let binders] in Var
-- Now lint the binder
- ; lintBinder LetBind bndr $ \bndr' ->
- do { lintLetBind NotTopLevel NonRecursive bndr' rhs rhs_ty
- ; addAliasUE bndr' let_ue $
- lintLetBody (BodyOfLet bndr') [bndr'] body } }
+ ; lintBinder LetBind bndr $
+ do { lintLetBind NotTopLevel NonRecursive bndr rhs rhs_ty
+ ; addAliasUE bndr let_ue $
+ lintLetBody (BodyOfLet bndr) [bndr] body } }
| otherwise
= failWithL (mkLetErr bndr rhs) -- Not quite accurate
@@ -982,8 +918,8 @@ lintCoreExpr e@(Let (Rec pairs) body)
-- See Note [Multiplicity of let binders] in Var
; ((body_type, body_ue), ues) <-
- lintRecBindings NotTopLevel pairs $ \ bndrs' ->
- lintLetBody (BodyOfLetRec bndrs') bndrs' body
+ lintRecBindings NotTopLevel pairs $
+ lintLetBody (BodyOfLetRec bndrs) bndrs body
; return (body_type, body_ue `addUE` scaleUE ManyTy (foldr1WithDefault zeroUE addUE ues)) }
where
bndrs = map fst pairs
@@ -995,7 +931,7 @@ lintCoreExpr e@(App _ _)
-- N.B. we may have an over-saturated application of the form:
-- runRW (\s -> \x -> ...) y
, ty_arg1 : ty_arg2 : cont_arg : rest <- args
- = do { let lint_rw_cont :: CoreArg -> Mult -> UsageEnv -> LintM (OutType, UsageEnv)
+ = do { let lint_rw_cont :: CoreArg -> Mult -> UsageEnv -> LintM (Type, UsageEnv)
lint_rw_cont expr@(Lam _ _) mult fun_ue
= do { (arg_ty, arg_ue) <- lintJoinLams 1 (Just fun) expr
; let app_ue = addUE fun_ue (scaleUE mult arg_ue)
@@ -1045,53 +981,42 @@ lintCoreExpr (Type ty)
lintCoreExpr (Coercion co)
-- See Note [Coercions in terms]
= do { addLoc (InCo co) $ lintCoercion co
- ; ty <- substTyM (coercionType co)
+ ; let ty = coercionType co
; return (ty, zeroUE) }
----------------------
-lintIdOcc :: InId -> Int -- Number of arguments (type or value) being passed
- -> LintM (OutType, UsageEnv) -- returns type of the *variable*
-lintIdOcc in_id nargs
- = addLoc (OccOf in_id) $
- do { checkL (isNonCoVarId in_id)
- (text "Non term variable" <+> ppr in_id)
+lintIdOcc :: Id -> Int -- Number of arguments (type or value) being passed
+ -> LintM (Type, UsageEnv) -- returns type of the *variable*
+lintIdOcc id nargs
+ = addLoc (OccOf id) $
+ do { checkL (isNonCoVarId id)
+ (text "Non term variable" <+> ppr id)
-- See GHC.Core Note [Variable occurrences in Core]
- -- Check that the type of the occurrence is the same
- -- as the type of the binding site. The inScopeIds are
- -- /un-substituted/, so this checks that the occurrence type
- -- is identical to the binder type.
- -- This makes things much easier for things like:
- -- /\a. \(x::Maybe a). /\a. ...(x::Maybe a)...
- -- The "::Maybe a" on the occurrence is referring to the /outer/ a.
- -- If we compared /substituted/ types we'd risk comparing
- -- (Maybe a) from the binding site with bogus (Maybe a1) from
- -- the occurrence site. Comparing un-substituted types finesses
- -- this altogether
- ; out_ty <- lintVarOcc in_id
+ ; lintVarOcc id
-- Check for a nested occurrence of the StaticPtr constructor.
-- See Note [Checking StaticPtrs].
; when (nargs /= 0) $
- checkL (idName in_id /= makeStaticName) $
+ checkL (idName id /= makeStaticName) $
text "Found makeStatic nested in an expression"
- ; checkDeadIdOcc in_id
+ ; checkDeadIdOcc id
- ; case isDataConId_maybe in_id of
+ ; case isDataConId_maybe id of
Nothing -> return ()
Just dc -> checkTypeDataConOcc "expression" dc
- ; checkJoinOcc in_id nargs
- ; usage <- varCallSiteUsage in_id
+ ; checkJoinOcc id nargs
+ ; usage <- varCallSiteUsage id
- ; return (out_ty, usage) }
+ ; return (idType id, usage) }
lintCoreFun :: CoreExpr
-> Int -- Number of arguments (type or val) being passed
- -> LintM (OutType, UsageEnv) -- Returns type of the *function*
+ -> LintM (Type, UsageEnv) -- Returns type of the *function*
lintCoreFun (Var var) nargs
= lintIdOcc var nargs
@@ -1109,10 +1034,10 @@ lintCoreFun expr nargs
lintLambda :: Var -> LintM (Type, UsageEnv) -> LintM (Type, UsageEnv)
lintLambda var lintBody =
addLoc (LambdaBodyOf var) $
- lintBinder LambdaBind var $ \ var' ->
+ lintBinder LambdaBind var $
do { (body_ty, ue) <- lintBody
- ; ue' <- checkLinearity ue var'
- ; return (mkLamType var' body_ty, ue') }
+ ; ue' <- checkLinearity ue var
+ ; return (mkLamType var body_ty, ue') }
------------------
checkDeadIdOcc :: Id -> LintM ()
-- Occurrences of an Id should never be dead....
@@ -1126,8 +1051,8 @@ checkDeadIdOcc id
= return ()
------------------
-lintJoinBndrType :: OutType -- Type of the body
- -> OutId -- Possibly a join Id
+lintJoinBndrType :: Type -- Type of the body
+ -> Id -- Possibly a join Id
-> LintM ()
-- Checks that the return type of a join Id matches the body
-- E.g. join j x = rhs in body
@@ -1458,23 +1383,24 @@ subtype of the required type, as one would expect.
-- Takes the functions type and arguments as argument.
-- Returns the *result* of applying the function to arguments.
-- e.g. f :: Int -> Bool -> Int would return `Int` as result type.
-lintCoreArgs :: (OutType, UsageEnv) -> [InExpr] -> LintM (OutType, UsageEnv)
+lintCoreArgs :: (Type, UsageEnv) -> [CoreExpr] -> LintM (Type, UsageEnv)
lintCoreArgs (fun_ty, fun_ue) args
= lintApp (text "expression")
lintTyArg lintValArg fun_ty args fun_ue
-lintTyArg :: InExpr -> LintM OutType
+lintTyArg :: CoreExpr -> LintM Type
-- Type argument
lintTyArg (Type arg_ty)
= do { checkL (not (isCoercionTy arg_ty))
(text "Unnecessary coercion-to-type injection:"
<+> ppr arg_ty)
- ; lintTypeAndSubst arg_ty }
+ ; lintType arg_ty
+ ; return arg_ty }
lintTyArg arg
= failWithL (hang (text "Expected type argument but found") 2 (ppr arg))
-lintValArg :: InExpr -> Mult -> UsageEnv -> LintM (OutType, UsageEnv)
+lintValArg :: CoreExpr -> Mult -> UsageEnv -> LintM (Type, UsageEnv)
lintValArg arg mult fun_ue
= do { (arg_ty, arg_ue) <- markAllJoinsBad $ lintCoreExpr arg
-- See Note [Representation polymorphism invariants] in GHC.Core
@@ -1494,8 +1420,8 @@ lintValArg arg mult fun_ue
-----------------
lintAltBinders :: UsageEnv
-> Var -- Case binder
- -> OutType -- Scrutinee type
- -> OutType -- Constructor type
+ -> Type -- Scrutinee type
+ -> Type -- Constructor type
-> [(Mult, OutVar)] -- Binders
-> LintM UsageEnv
-- If you edit this function, you may need to update the GHC formalism
@@ -1545,7 +1471,7 @@ checkCaseLinearity ue case_bndr var_w bndr = do
-----------------
-lintTyApp :: OutType -> OutType -> LintM OutType
+lintTyApp :: Type -> Type -> LintM Type
lintTyApp fun_ty arg_ty
| Just (tv,body_ty) <- splitForAllTyVar_maybe fun_ty
= do { lintTyKind tv arg_ty
@@ -1563,8 +1489,8 @@ lintTyApp fun_ty arg_ty
-- | @lintValApp arg fun_ty arg_ty@ lints an application of @fun arg@
-- where @fun :: fun_ty@ and @arg :: arg_ty@, returning the type of the
-- application.
-lintValApp :: CoreExpr -> OutType -> OutType -> UsageEnv -> UsageEnv
- -> LintM (OutType, UsageEnv)
+lintValApp :: CoreExpr -> Type -> Type -> UsageEnv -> UsageEnv
+ -> LintM (Type, UsageEnv)
lintValApp arg fun_ty arg_ty fun_ue arg_ue
| Just (_, w, arg_ty', res_ty') <- splitFunTy_maybe fun_ty
= do { ensureEqTys arg_ty' arg_ty (mkAppMsg arg_ty' arg_ty arg)
@@ -1575,7 +1501,7 @@ lintValApp arg fun_ty arg_ty fun_ue arg_ue
where
err2 = mkNonFunAppMsg fun_ty arg_ty arg
-lintTyKind :: OutTyVar -> OutType -> LintM ()
+lintTyKind :: OutTyVar -> Type -> LintM ()
-- Both args have had substitution applied
-- If you edit this function, you may need to update the GHC formalism
@@ -1595,36 +1521,36 @@ lintTyKind tyvar arg_ty
************************************************************************
-}
-lintCaseExpr :: CoreExpr -> InId -> InType -> [CoreAlt] -> LintM (OutType, UsageEnv)
+lintCaseExpr :: CoreExpr -> Id -> Type -> [CoreAlt] -> LintM (Type, UsageEnv)
lintCaseExpr scrut case_bndr alt_ty alts
= do { let e = Case scrut case_bndr alt_ty alts -- Just for error messages
-- Check the scrutinee
- ; (scrut_ty', scrut_ue) <- markAllJoinsBad $ lintCoreExpr scrut
+ ; (scrut_ty, scrut_ue) <- markAllJoinsBad $ lintCoreExpr scrut
-- See Note [Join points are less general than the paper]
-- in GHC.Core
- ; alt_ty' <- addLoc (CaseTy scrut) $ lintValueType alt_ty
+ ; addLoc (CaseTy scrut) $ lintValueType alt_ty
- ; checkCaseAlts e scrut scrut_ty' alts
+ ; checkCaseAlts e scrut scrut_ty alts
-- Lint the case-binder. Must do this after linting the scrutinee
-- because the case-binder isn't in scope in the scrutineex
- ; lintBinder CaseBind case_bndr $ \case_bndr' ->
+ ; lintBinder CaseBind case_bndr $
-- Don't use lintIdBndr on case_bndr, because unboxed tuple is legitimate
- do { let case_bndr_ty' = idType case_bndr'
- scrut_mult = idMult case_bndr'
+ do { let case_bndr_ty = idType case_bndr
+ scrut_mult = idMult case_bndr
- ; ensureEqTys case_bndr_ty' scrut_ty' (mkScrutMsg case_bndr case_bndr_ty' scrut_ty')
+ ; ensureEqTys case_bndr_ty scrut_ty (mkScrutMsg case_bndr case_bndr_ty scrut_ty)
-- See GHC.Core Note [Case expression invariants] item (7)
; -- Check the alternatives
- ; alt_ues <- mapM (lintCoreAlt case_bndr' scrut_ty' scrut_mult alt_ty') alts
+ ; alt_ues <- mapM (lintCoreAlt case_bndr scrut_ty scrut_mult alt_ty) alts
; let case_ue = (scaleUE scrut_mult scrut_ue) `addUE` supUEs alt_ues
- ; return (alt_ty', case_ue) } }
+ ; return (alt_ty, case_ue) } }
-checkCaseAlts :: InExpr -> InExpr -> OutType -> [CoreAlt] -> LintM ()
+checkCaseAlts :: CoreExpr -> CoreExpr -> Type -> [CoreAlt] -> LintM ()
-- a) Check that the alts are non-empty
-- b1) Check that the DEFAULT comes first, if it exists
-- b2) Check that the others are in increasing order
@@ -1699,17 +1625,17 @@ checkCaseAlts e scrut scrut_ty alts
is_lit_alt (Alt (LitAlt _) _ _) = True
is_lit_alt _ = False
-lintAltExpr :: CoreExpr -> OutType -> LintM UsageEnv
+lintAltExpr :: CoreExpr -> Type -> LintM UsageEnv
lintAltExpr expr ann_ty
= do { (actual_ty, ue) <- lintCoreExpr expr
; ensureEqTys actual_ty ann_ty (mkCaseAltMsg expr actual_ty ann_ty)
; return ue }
-- See GHC.Core Note [Case expression invariants] item (6)
-lintCoreAlt :: OutId -- Case binder
- -> OutType -- Type of scrutinee
+lintCoreAlt :: Id -- Case binder
+ -> Type -- Type of scrutinee
-> Mult -- Multiplicity of scrutinee
- -> OutType -- Type of the alternative
+ -> Type -- Type of the alternative
-> CoreAlt
-> LintM UsageEnv
-- If you edit this function, you may need to update the GHC formalism
@@ -1754,11 +1680,11 @@ lintCoreAlt case_bndr scrut_ty _scrut_mult alt_ty alt@(Alt (DataAlt con) args rh
; multiplicities = map binderMult $ fst $ splitPiTys con_payload_ty }
-- And now bring the new binders into scope
- ; lintBinders CasePatBind args $ \ args' -> do
+ ; lintBinders CasePatBind args $ do
{ rhs_ue <- lintAltExpr rhs alt_ty
; rhs_ue' <- addLoc (CasePat alt) $
lintAltBinders rhs_ue case_bndr scrut_ty con_payload_ty
- (zipEqual multiplicities args')
+ (zipEqual multiplicities args)
; return $ deleteUE rhs_ue' case_bndr
}
}
@@ -1803,49 +1729,50 @@ lintLinearBinder doc actual_usage described_usage
-- 1. Lint var types or kinds (possibly substituting)
-- 2. Add the binder to the in scope set, and if its a coercion var,
-- we may extend the substitution to reflect its (possibly) new kind
-lintBinders :: HasDebugCallStack => BindingSite -> [InVar] -> ([OutVar] -> LintM a) -> LintM a
-lintBinders _ [] linterF = linterF []
-lintBinders site (var:vars) linterF = lintBinder site var $ \var' ->
- lintBinders site vars $ \ vars' ->
- linterF (var':vars')
+lintBinders :: HasDebugCallStack => BindingSite -> [Var] -> LintM a -> LintM a
+lintBinders _ [] linterF = linterF
+lintBinders site (var:vars) linterF = lintBinder site var $
+ lintBinders site vars $
+ linterF
-- If you edit this function, you may need to update the GHC formalism
-- See Note [GHC Formalism]
-lintBinder :: HasDebugCallStack => BindingSite -> InVar -> (OutVar -> LintM a) -> LintM a
+lintBinder :: HasDebugCallStack => BindingSite -> Var -> LintM a -> LintM a
lintBinder site var linterF
| isTyCoVar var = lintTyCoBndr var linterF
| otherwise = lintIdBndr NotTopLevel site var linterF
-lintTyCoBndr :: HasDebugCallStack => TyCoVar -> (OutTyCoVar -> LintM a) -> LintM a
+lintTyCoBndr :: HasDebugCallStack => TyCoVar -> LintM a -> LintM a
lintTyCoBndr tcv thing_inside
- = do { tcv_type' <- lintTypeAndSubst (varType tcv)
- ; let tcv_kind' = typeKind tcv_type'
+ = do { let tcv_type = varType tcv
+ tcv_kind = typeKind tcv_type
+ ; lintType (varType tcv)
-- See (FORALL1) and (FORALL2) in GHC.Core.Type
; if (isTyVar tcv)
then -- Check that in (forall (a:ki). blah) we have ki:Type
- lintL (isLiftedTypeKind tcv_kind') $
+ lintL (isLiftedTypeKind tcv_kind) $
hang (text "TyVar whose kind does not have kind Type:")
- 2 (ppr tcv <+> dcolon <+> ppr tcv_type' <+> dcolon <+> ppr tcv_kind')
+ 2 (ppr tcv <+> dcolon <+> ppr tcv_type <+> dcolon <+> ppr tcv_kind)
else -- Check that in (forall (cv::ty). blah),
-- then ty looks like (t1 ~# t2)
- lintL (isCoVarType tcv_type') $
+ lintL (isCoVarType tcv_type) $
text "CoVar with non-coercion type:" <+> pprTyVar tcv
- ; addInScopeTyCoVar tcv tcv_type' thing_inside }
+ ; addInScopeTyCoVar tcv thing_inside }
-lintIdBndrs :: forall a. TopLevelFlag -> [InId] -> ([OutId] -> LintM a) -> LintM a
+lintIdBndrs :: forall a. TopLevelFlag -> [Id] -> LintM a -> LintM a
lintIdBndrs top_lvl ids thing_inside
= go ids thing_inside
where
- go :: [Id] -> ([Id] -> LintM a) -> LintM a
- go [] thing_inside = thing_inside []
- go (id:ids) thing_inside = lintIdBndr top_lvl LetBind id $ \id' ->
- go ids $ \ids' ->
- thing_inside (id' : ids')
+ go :: [Id] -> LintM a -> LintM a
+ go [] thing_inside = thing_inside
+ go (id:ids) thing_inside = lintIdBndr top_lvl LetBind id $
+ go ids $
+ thing_inside
lintIdBndr :: TopLevelFlag -> BindingSite
- -> InVar -> (OutVar -> LintM a) -> LintM a
+ -> Var -> LintM a -> LintM a
-- Do substitution on the type of a binder and add the var with this
-- new type to the in-scope set of the second argument
-- ToDo: lint its rules
@@ -1885,9 +1812,9 @@ lintIdBndr top_lvl bind_site id thing_inside
; lintL (not (bind_site == LambdaBind && isEvaldUnfolding (idUnfolding id)))
(text "Lambda binder with value or OtherCon unfolding.")
- ; out_ty <- addLoc (IdTy id) (lintValueType id_ty)
+ ; addLoc (IdTy id) (lintValueType id_ty)
- ; addInScopeId id out_ty thing_inside }
+ ; addInScopeId id thing_inside }
where
id_ty = idType id
@@ -1907,8 +1834,8 @@ lintIdBndr top_lvl bind_site id thing_inside
{- Note [Linting types and coercions]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Notice that
- lintType :: InType -> LintM ()
- lintCoercion :: InCoercion -> LintM ()
+ lintType :: Type -> LintM ()
+ lintCoercion :: Coercion -> LintM ()
Neither returns anything.
If you need the kind of the type, then do `typeKind` and then apply
@@ -1921,48 +1848,41 @@ and then take the kind, becaues the kind is usually smaller.
Note: you might wonder if we should apply the same logic to expressions.
Why do we have
- lintExpr :: InExpr -> LintM OutType
+ lintExpr :: CoreExpr -> LintM Type
Partly inertia; but also taking the type of an expresison involve looking
down a deep chain of let's, whereas that is not true of taking the kind
of a type. It'd be worth an experiment though.
Historical note: in the olden days we had
- lintType :: InType -> LintM OutType
-but that burned a huge amount of allocation building an OutType that was
+ lintType :: Type -> LintM Type
+but that burned a huge amount of allocation building an Type that was
often discarded, or used only to get its kind.
I also experimented with
- lintType :: InType -> LintM OutKind
+ lintType :: Type -> LintM Kind
but that too was slower. It is also much simpler to return ()! If we
return the kind we have to duplicate the logic in `typeKind`; and it is
much worse for coercions.
-}
-lintValueType :: Type -> LintM OutType
+lintValueType :: Type -> LintM ()
-- Types only, not kinds
-- Check the type, and apply the substitution to it
-- See Note [Linting type lets]
lintValueType ty
= addLoc (InType ty) $
- do { ty' <- lintTypeAndSubst ty
- ; let sk = typeKind ty'
+ do { lintType ty
+ ; let sk = typeKind ty
; lintL (isTYPEorCONSTRAINT sk) $
hang (text "Ill-kinded type:" <+> ppr ty)
- 2 (text "has kind:" <+> ppr sk)
- ; return ty' }
+ 2 (text "has kind:" <+> ppr sk)}
checkTyCon :: TyCon -> LintM ()
checkTyCon tc
= checkL (not (isTcTyCon tc)) (text "Found TcTyCon:" <+> ppr tc)
-------------------
-lintTypeAndSubst :: InType -> LintM OutType
-lintTypeAndSubst ty = do { lintType ty; substTyM ty }
- -- In GHCi we may lint an expression with a free
- -- type variable. Then it won't be in the
- -- substitution, but it should be in scope
-
-lintType :: InType -> LintM ()
+lintType :: Type -> LintM ()
-- See Note [Linting types and coercions]
--
-- If you edit this function, you may need to update the GHC formalism
@@ -1972,8 +1892,7 @@ lintType (TyVarTy tv)
= failWithL (mkBadTyVarMsg tv)
| otherwise
- = do { _ <- lintVarOcc tv
- ; return () }
+ = lintVarOcc tv
lintType ty@(AppTy t1 t2)
| TyConApp {} <- t1
@@ -1981,7 +1900,7 @@ lintType ty@(AppTy t1 t2)
| otherwise
= do { let (fun_ty, arg_tys) = collect t1 [t2]
; lintType fun_ty
- ; fun_kind <- substTyM (typeKind fun_ty)
+ ; let fun_kind = typeKind fun_ty
; lint_ty_app ty fun_kind arg_tys }
where
collect (AppTy f a) as = collect f (a:as)
@@ -2013,21 +1932,21 @@ lintType ty@(FunTy af tw t1 t2)
lintType ty@(ForAllTy {})
= go [] ty
where
- go :: [OutTyCoVar] -> InType -> LintM ()
+ go :: [OutTyCoVar] -> Type -> LintM ()
-- Loop, collecting the forall-binders
go tcvs ty@(ForAllTy (Bndr tcv _) body_ty)
| not (isTyCoVar tcv)
= failWithL (text "Non-TyVar or Non-CoVar bound in type:" <+> ppr ty)
| otherwise
- = lintTyCoBndr tcv $ \tcv' ->
+ = lintTyCoBndr tcv $
do { -- See GHC.Core.TyCo.Rep Note [Unused coercion variable in ForAllTy]
-- Suspicious because it works on InTyCoVar; c.f. ForAllCo
when (isCoVar tcv) $
lintL (anyFreeVarsOfType (== tcv) body_ty) $
text "Covar does not occur in the body:" <+> (ppr tcv $$ ppr body_ty)
- ; go (tcv' : tcvs) body_ty }
+ ; go (tcv : tcvs) body_ty }
go tcvs body_ty
= do { lintType body_ty
@@ -2035,7 +1954,7 @@ lintType ty@(ForAllTy {})
lintType (CastTy ty co)
= do { lintType ty
- ; ty_kind <- substTyM (typeKind ty)
+ ; let ty_kind = typeKind ty
; co_lk <- lintStarCoercion co
; ensureEqTys ty_kind co_lk (mkCastTyErr ty co ty_kind co_lk) }
@@ -2043,14 +1962,14 @@ lintType (LitTy l) = lintTyLit l
lintType (CoercionTy co) = lintCoercion co
-----------------
-lintForAllBody :: [OutTyCoVar] -> InType -> LintM ()
+lintForAllBody :: [OutTyCoVar] -> Type -> LintM ()
-- Do the checks for the body of a forall-type
lintForAllBody tcvs body_ty
= do { -- For type variables, check for skolem escape
-- See Note [Phantom type variables in kinds] in GHC.Core.Type
-- The kind of (forall cv. th) is liftedTypeKind, so no
-- need to check for skolem-escape in the CoVar case
- body_kind <- substTyM (typeKind body_ty)
+ let body_kind = typeKind body_ty
; case occCheckExpand tcvs body_kind of
Just {} -> return ()
Nothing -> failWithL $
@@ -2061,7 +1980,7 @@ lintForAllBody tcvs body_ty
; checkValueType body_kind (text "the body of forall:" <+> ppr body_ty) }
-----------------
-lintTySynFamApp :: Bool -> InType -> TyCon -> [InType] -> LintM ()
+lintTySynFamApp :: Bool -> Type -> TyCon -> [Type] -> LintM ()
-- The TyCon is a type synonym or a type family (not a data family)
-- See Note [Linting type synonym applications]
-- c.f. GHC.Tc.Validity.check_syn_tc_app
@@ -2087,21 +2006,21 @@ lintTySynFamApp report_unsat ty tc tys
-----------------
-- Confirms that a kind is really TYPE r or Constraint
-checkValueType :: OutKind -> SDoc -> LintM ()
+checkValueType :: Kind -> SDoc -> LintM ()
checkValueType kind doc
= lintL (isTYPEorCONSTRAINT kind)
(text "Non-Type-like kind when Type-like expected:" <+> ppr kind $$
text "when checking" <+> doc)
-----------------
-lintArrow :: SDoc -> FunTyFlag -> InType -> InType -> InType -> LintM ()
+lintArrow :: SDoc -> FunTyFlag -> Type -> Type -> Type -> LintM ()
-- If you edit this function, you may need to update the GHC formalism
-- See Note [GHC Formalism]
lintArrow what af t1 t2 tw -- Eg lintArrow "type or kind `blah'" k1 k2 kw
-- or lintArrow "coercion `blah'" k1 k2 kw
- = do { k1 <- substTyM (typeKind t1)
- ; k2 <- substTyM (typeKind t2)
- ; kw <- substTyM (typeKind tw)
+ = do { let k1 = typeKind t1
+ k2 = typeKind t2
+ kw = typeKind tw
; unless (isTYPEorCONSTRAINT k1) (report (text "argument") t1 k1)
; unless (isTYPEorCONSTRAINT k2) (report (text "result") t2 k2)
; unless (isMultiplicityTy kw) (report (text "multiplicity") tw kw)
@@ -2127,29 +2046,29 @@ lintTyLit (StrTyLit _) = return ()
lintTyLit (CharTyLit _) = return ()
-----------------
-lint_ty_app :: InType -> OutKind -> [InType] -> LintM ()
+lint_ty_app :: Type -> Kind -> [Type] -> LintM ()
lint_ty_app ty = lint_tyco_app (text "type" <+> quotes (ppr ty))
-lint_co_app :: HasDebugCallStack => Coercion -> OutKind -> [InType] -> LintM ()
+lint_co_app :: HasDebugCallStack => Coercion -> Kind -> [Type] -> LintM ()
lint_co_app co = lint_tyco_app (text "coercion" <+> quotes (ppr co))
-lint_tyco_app :: SDoc -> OutKind -> [InType] -> LintM ()
+lint_tyco_app :: SDoc -> Kind -> [Type] -> LintM ()
lint_tyco_app msg fun_kind arg_tys
-- See Note [Avoiding compiler perf traps when constructing error messages.]
- = do { _ <- lintApp msg (\ty -> do { lintType ty; substTyM ty })
- (\ty _ _ -> do { lintType ty; ki <- substTyM (typeKind ty); return (ki,()) })
- fun_kind arg_tys ()
+ = do { _ <- lintApp msg (\ty -> do { lintType ty; return ty })
+ (\ty _ _ -> do { lintType ty; return (typeKind ty,()) })
+ fun_kind arg_tys ()
; return () }
----------------
lintApp :: forall in_a acc. Outputable in_a =>
SDoc
- -> (in_a -> LintM OutType) -- Lint the thing and return its value
- -> (in_a -> Mult -> acc -> LintM (OutKind, acc)) -- Lint the thing and return its type
- -> OutType
+ -> (in_a -> LintM Type) -- Lint the thing and return its value
+ -> (in_a -> Mult -> acc -> LintM (Kind, acc)) -- Lint the thing and return its type
+ -> Type
-> [in_a] -- The arguments, always "In" things
-> acc -- Used (only) for UsageEnv in /term/ applications
- -> LintM (OutType,acc)
+ -> LintM (Type,acc)
-- lintApp is a performance-critical function, which deals with multiple
-- applications such as (/\a./\b./\c. expr) @ta @tb @tc
-- When returning the type of this expression we want to avoid substituting a:=ta,
@@ -2174,7 +2093,7 @@ lintApp msg lint_forall_arg lint_arrow_arg !orig_fun_ty all_args acc
; let init_subst = mkEmptySubst in_scope
- go :: Subst -> OutType -> acc -> [in_a] -> LintM (OutType, acc)
+ go :: Subst -> Type -> acc -> [in_a] -> LintM (Type, acc)
-- The Subst applies (only) to the fun_ty
-- c.f. GHC.Core.Type.piResultTys, which has a similar loop
@@ -2218,7 +2137,7 @@ lintApp msg lint_forall_arg lint_arrow_arg !orig_fun_ty all_args acc
-- explicitly and don't capture them as free variables. Otherwise this binder might
-- become a thunk that get's allocated in the hot code path.
-- See Note [Avoiding compiler perf traps when constructing error messages.]
-lint_app_fail_msg :: (Outputable a2) => SDoc -> OutType -> a2 -> SDoc -> SDoc
+lint_app_fail_msg :: (Outputable a2) => SDoc -> Type -> a2 -> SDoc -> SDoc
lint_app_fail_msg msg kfn arg_tys extra
= vcat [ hang (text "Application error in") 2 msg
, nest 2 (text "Function type =" <+> ppr kfn)
@@ -2231,7 +2150,7 @@ lint_app_fail_msg msg kfn arg_tys extra
* *
********************************************************************* -}
-lintCoreRule :: OutVar -> OutType -> CoreRule -> LintM ()
+lintCoreRule :: OutVar -> Type -> CoreRule -> LintM ()
lintCoreRule _ _ (BuiltinRule {})
= return () -- Don't bother
@@ -2239,7 +2158,7 @@ lintCoreRule fun fun_ty rule@(Rule { ru_name = name, ru_bndrs = bndrs
, ru_args = args, ru_rhs = rhs })
= noMultiplicityChecks $ -- Skip linearity checking for rules
-- See Note [Linting linearity]
- lintBinders LambdaBind bndrs $ \ _ ->
+ lintBinders LambdaBind bndrs $
do { (lhs_ty, _) <- lintCoreArgs (fun_ty, zeroUE) args
; (rhs_ty, _) <- case idJoinPointHood fun of
JoinPoint join_arity
@@ -2349,23 +2268,16 @@ which is what used to happen. But that proved tricky and error prone
-- lintStarCoercion lints a coercion, confirming that its lh kind and
-- its rh kind are both *; also ensures that the role is Nominal
-- Returns the lh kind
-lintStarCoercion :: InCoercion -> LintM OutType
+lintStarCoercion :: Coercion -> LintM Type
lintStarCoercion g
= do { lintCoercion g
- ; Pair t1 t2 <- substCoKindM g
+ ; let Pair t1 t2 = coercionKind g
; checkValueType (typeKind t1) (text "the kind of the left type in" <+> ppr g)
; checkValueType (typeKind t2) (text "the kind of the right type in" <+> ppr g)
; lintRole g Nominal (coercionRole g)
; return t1 }
-substCoKindM :: InCoercion -> LintM (Pair OutType)
-substCoKindM co
- = do { let !(Pair lk rk) = coercionKind co
- ; lk' <- substTyM lk
- ; rk' <- substTyM rk
- ; return (Pair lk' rk') }
-
-lintCoercion :: HasDebugCallStack => InCoercion -> LintM ()
+lintCoercion :: HasDebugCallStack => Coercion -> LintM ()
-- See Note [Linting types and coercions]
--
-- If you edit this function, you may need to update the GHC formalism
@@ -2377,7 +2289,7 @@ lintCoercion (CoVarCo cv)
2 (text "With offending type:" <+> ppr (varType cv)))
| otherwise -- C.f. lintType (TyVarTy tv), which has better docs
- = do { _ <- lintVarOcc cv; return () }
+ = lintVarOcc cv
lintCoercion (Refl ty) = lintType ty
lintCoercion (GRefl _r ty MRefl) = lintType ty
@@ -2385,8 +2297,8 @@ lintCoercion (GRefl _r ty MRefl) = lintType ty
lintCoercion (GRefl _r ty (MCo co))
= do { lintType ty
; lintCoercion co
- ; tk <- substTyM (typeKind ty)
- ; tl <- substTyM (coercionLKind co)
+ ; let tk = typeKind ty
+ tl = coercionLKind co
; ensureEqTys tk tl $
hang (text "GRefl coercion kind mis-match:" <+> ppr co)
2 (vcat [ppr ty, ppr tk, ppr tl])
@@ -2419,8 +2331,8 @@ lintCoercion co@(AppCo co1 co2)
= do { lintCoercion co1
; lintCoercion co2
; let !(Pair lt1 rt1) = coercionKind co1
- ; lk1 <- substTyM (typeKind lt1)
- ; rk1 <- substTyM (typeKind rt1)
+ lk1 = typeKind lt1
+ rk1 = typeKind rt1
; lint_co_app co lk1 [coercionLKind co2]
; lint_co_app co rk1 [coercionRKind co2]
@@ -2437,7 +2349,7 @@ lintCoercion co@(ForAllCo {})
= do { _ <- go [] co; return () }
where
go :: [OutTyCoVar] -- Binders in reverse order
- -> InCoercion -> LintM Role
+ -> Coercion -> LintM Role
go tcvs co@(ForAllCo { fco_tcv = tcv, fco_visL = visL, fco_visR = visR
, fco_kind = kind_mco, fco_body = body_co })
| not (isTyCoVar tcv)
@@ -2447,15 +2359,15 @@ lintCoercion co@(ForAllCo {})
= do { mb_lk <- case kind_mco of
MRefl -> return Nothing
MCo kind_co -> Just <$> lintStarCoercion kind_co
- ; lintTyCoBndr tcv $ \tcv' ->
+ ; lintTyCoBndr tcv $
do { case mb_lk of
Nothing -> return ()
- Just lk -> ensureEqTys (varType tcv') lk $
+ Just lk -> ensureEqTys (varType tcv) lk $
text "Kind mis-match in ForallCo" <+> ppr co
-- I'm not very sure about this part, because it traverses body_co
-- but at least it's on a cold path (a ForallCo for a CoVar)
- -- Also it works on InTyCoVar and InCoercion, which is suspect
+ -- Also it works on InTyCoVar and Coercion, which is suspect
; when (isCoVar tcv) $
do { lintL (visL == coreTyLamForAllTyFlag && visR == coreTyLamForAllTyFlag) $
text "Invalid visibility flags in CoVar ForAllCo" <+> ppr co
@@ -2464,7 +2376,7 @@ lintCoercion co@(ForAllCo {})
text "Covar can only appear in Refl and GRefl: " <+> ppr co }
-- See (FC6) in Note [ForAllCo] in GHC.Core.TyCo.Rep
- ; role <- go (tcv':tcvs) body_co
+ ; role <- go (tcv:tcvs) body_co
; when (role == Nominal) $
lintL (visL `eqForAllVis` visR) $
@@ -2521,8 +2433,8 @@ lintCoercion co@(UnivCo { uco_role = r, uco_prov = prov
-- Check the to and from types
; lintType ty1
; lintType ty2
- ; tk1 <- substTyM (typeKind ty1)
- ; tk2 <- substTyM (typeKind ty2)
+ ; let tk1 = typeKind ty1
+ tk2 = typeKind ty2
; when (r /= Phantom && isTYPEorCONSTRAINT tk1 && isTYPEorCONSTRAINT tk2)
(checkTypes ty1 ty2)
@@ -2576,8 +2488,8 @@ lintCoercion (SymCo co) = lintCoercion co
lintCoercion co@(TransCo co1 co2)
= do { lintCoercion co1
; lintCoercion co2
- ; rk1 <- substTyM (coercionRKind co1)
- ; lk2 <- substTyM (coercionLKind co2)
+ ; let rk1 = coercionRKind co1
+ lk2 = coercionLKind co2
; ensureEqTys rk1 lk2
(hang (text "Trans coercion mis-match:" <+> ppr co)
2 (vcat [ppr (coercionKind co1), ppr (coercionKind co2)]))
@@ -2585,7 +2497,7 @@ lintCoercion co@(TransCo co1 co2)
lintCoercion the_co@(SelCo cs co)
= do { lintCoercion co
- ; Pair s t <- substCoKindM co
+ ; let Pair s t = coercionKind co
; if -- forall (both TyVar and CoVar)
| Just _ <- splitForAllTyCoVar_maybe s
@@ -2620,7 +2532,7 @@ lintCoercion the_co@(SelCo cs co)
lintCoercion the_co@(LRCo _lr co)
= do { lintCoercion co
- ; Pair s t <- substCoKindM co
+ ; let Pair s t = coercionKind co
; lintRole co Nominal (coercionRole co)
; case (splitAppTy_maybe s, splitAppTy_maybe t) of
(Just {}, Just {}) -> return ()
@@ -2634,14 +2546,12 @@ lintCoercion orig_co@(InstCo co arg)
go (InstCo co arg) args = do { lintCoercion arg; go co (arg:args) }
go co args = do { lintCoercion co
; let Pair lty rty = coercionKind co
- ; lty' <- substTyM lty
- ; rty' <- substTyM rty
; in_scope <- getInScope
; let subst = mkEmptySubst in_scope
- ; go_args (subst, lty') (subst,rty') args }
+ ; go_args (subst, lty) (subst,rty) args }
-------------
- go_args :: (Subst, OutType) -> (Subst,OutType) -> [InCoercion]
+ go_args :: (Subst, Type) -> (Subst,Type) -> [Coercion]
-> LintM ()
go_args _ _ []
= return ()
@@ -2650,11 +2560,11 @@ lintCoercion orig_co@(InstCo co arg)
; go_args lty1 rty1 args }
-------------
- go_arg :: (Subst, OutType) -> (Subst,OutType) -> InCoercion
- -> LintM ((Subst,OutType), (Subst,OutType))
+ go_arg :: (Subst, Type) -> (Subst,Type) -> Coercion
+ -> LintM ((Subst,Type), (Subst,Type))
go_arg (lsubst,lty) (rsubst,rty) arg
= do { lintRole arg Nominal (coercionRole arg)
- ; Pair arg_lty arg_rty <- substCoKindM arg
+ ; let Pair arg_lty arg_rty = coercionKind arg
; case (splitForAllTyCoVar_maybe lty, splitForAllTyCoVar_maybe rty) of
-- forall over tvar
@@ -2678,11 +2588,11 @@ lintCoercion orig_co@(InstCo co arg)
lintCoercion this_co@(AxiomCo ax cos)
= do { mapM_ lintCoercion cos
; lint_roles 0 (coAxiomRuleArgRoles ax) cos
- ; prs <- mapM substCoKindM cos
+ ; let prs = map coercionKind cos
; lint_ax ax prs }
where
- lint_ax :: CoAxiomRule -> [Pair OutType] -> LintM ()
+ lint_ax :: CoAxiomRule -> [Pair Type] -> LintM ()
lint_ax (BuiltInFamRew bif) prs
= checkL (isJust (bifrw_proves bif prs)) bad_bif
lint_ax (BuiltInFamInj bif) prs
@@ -2770,8 +2680,8 @@ lintBranch this_co fam_tc branch arg_kinds
= do { checkL (arg_kinds `equalLength` (ktvs ++ cvs)) $
(bad_ax this_co (text "lengths"))
- ; subst <- getSubst
- ; let empty_subst = zapSubst subst
+ ; in_scope <- getInScope
+ ; let empty_subst = mkEmptySubst in_scope
; _ <- foldlM check_ki (empty_subst, empty_subst)
(zip (ktvs ++ cvs) arg_kinds)
@@ -2896,12 +2806,12 @@ lint_axiom ax@(CoAxiom { co_ax_tc = tc, co_ax_branches = branches
lint_branch :: TyCon -> CoAxBranch -> LintM ()
lint_branch ax_tc (CoAxBranch { cab_tvs = tvs, cab_cvs = cvs
, cab_lhs = lhs_args, cab_rhs = rhs })
- = lintBinders LambdaBind (tvs ++ cvs) $ \_ ->
+ = lintBinders LambdaBind (tvs ++ cvs) $
do { let lhs = mkTyConApp ax_tc lhs_args
; lintType lhs
; lintType rhs
- ; lhs_kind <- substTyM (typeKind lhs)
- ; rhs_kind <- substTyM (typeKind rhs)
+ ; let lhs_kind = typeKind lhs
+ rhs_kind = typeKind rhs
; lintL (not (lhs_kind `typesAreApart` rhs_kind)) $
hang (text "Inhomogeneous axiom")
2 (text "lhs:" <+> ppr lhs <+> dcolon <+> ppr lhs_kind $$
@@ -2985,35 +2895,26 @@ type LintLevel = Int
-- If you edit this type, you may need to update the GHC formalism
-- See Note [GHC Formalism]
data LintEnv
- = LE { le_flags :: LintFlags -- Linting the result of this pass
- , le_loc :: [LintLocInfo] -- Locations
-
- , le_subst :: Subst
- -- Current substitution, for TyCoVars only.
- -- Non-CoVar Ids don't appear in here, not even in the InScopeSet
- -- Used for (a) cloning to avoid shadowing of TyCoVars,
- -- so that eqType works ok
- -- (b) substituting for let-bound tyvars, when we have
- -- (let @a = Int -> Int in ...)
-
- , le_level :: LintLevel
- , le_in_vars :: VarEnv (InVar, OutType, LintLevel)
- -- Maps an InVar (i.e. its unique) to its binding InVar
- -- and to its OutType
- -- /All/ in-scope variables are here (term variables,
- -- type variables, and coercion variables)
- -- Used at an occurrence of the InVar
+ = LE { le_flags :: LintFlags -- Linting the result of this pass
+ , le_loc :: [LintLocInfo] -- Locations
+ , le_level :: LintLevel
+ , le_in_scope :: InScopeSet
+
+ , le_in_vars :: VarEnv (Var, LintLevel)
+ -- Maps an Var (i.e. its unique) to its binding Var and level
+ -- /All/ in-scope variables are here (term variables,
+ -- type variables, and coercion variables)
+ -- Used at an occurrence of the Var
, le_joins :: UniqMap Id JoinOcc
-- ^ Join points in scope that are valid
- -- A subset of the InScopeSet in le_subst
-- See Note [Join points]
, le_ue_aliases :: NameEnv UsageEnv
-- See Note [Linting linearity]
-- Assigns usage environments to the alias-like binders,
-- as found in non-recursive lets.
- -- Domain is OutIds
+ -- Domain is Ids
, le_platform :: Platform -- ^ Target platform
, le_diagOpts :: DiagOpts -- ^ Target platform
@@ -3369,12 +3270,12 @@ initL cfg m
where
vars = l_vars cfg
init_level = 0
- env = LE { le_flags = l_flags cfg
- , le_subst = mkEmptySubst (mkInScopeSetList vars)
- , le_level = init_level
- , le_in_vars = mkVarEnv [ (v,(v, varType v, init_level)) | v <- vars ]
- , le_joins = emptyUniqMap
- , le_loc = []
+ env = LE { le_flags = l_flags cfg
+ , le_level = init_level
+ , le_in_vars = mkVarEnv [ (v,(v, init_level)) | v <- vars ]
+ , le_in_scope = mkInScopeSetList vars
+ , le_joins = emptyUniqMap
+ , le_loc = []
, le_ue_aliases = emptyNameEnv
, le_platform = l_platform cfg
, le_diagOpts = l_diagOpts cfg
@@ -3437,8 +3338,7 @@ addMsg show_context env msgs msg
loc_msgs :: [(SrcLoc, SDoc)] -- Innermost first
loc_msgs = map dumpLoc (le_loc env)
- cxt_doc = vcat [ vcat $ reverse $ map snd loc_msgs
- , text "Substitution:" <+> ppr (le_subst env) ]
+ cxt_doc = vcat $ reverse $ map snd loc_msgs
context | show_context = cxt_doc
| otherwise = whenPprDebug cxt_doc
@@ -3465,78 +3365,45 @@ inCasePat = LintM $ \ env errs -> fromBoxedLResult (Just (is_case_pat env), errs
is_case_pat (LE { le_loc = CasePat {} : _ }) = True
is_case_pat _other = False
-addInScopeId :: InId -> OutType -> (OutId -> LintM a) -> LintM a
+addInScopeId :: Id -> LintM a -> LintM a
-- Unlike addInScopeTyCoVar, this function does no cloning; Ids never get cloned
-addInScopeId in_id out_ty thing_inside
+addInScopeId id thing_inside
= LintM $ \ env errs ->
- let !(out_id, env') = add env
- in unLintM (thing_inside out_id) env' errs
-
+ unLintM thing_inside (add env) errs
where
add env@(LE { le_level = level, le_in_vars = id_vars, le_joins = valid_joins
- , le_ue_aliases = aliases, le_subst = subst })
- = (out_id, env1)
+ , le_ue_aliases = aliases, le_in_scope = in_scope })
+ = env { le_level = level1, le_in_vars = in_vars'
+ , le_in_scope = in_scope `extendInScopeSet` id
+ , le_joins = valid_joins', le_ue_aliases = aliases' }
where
level1 = level + 1
- env1 = env { le_level = level1, le_in_vars = in_vars'
- , le_joins = valid_joins', le_ue_aliases = aliases' }
- in_vars' = extendVarEnv id_vars in_id (in_id, out_ty, level1)
- aliases' = delFromNameEnv aliases (idName in_id)
+ in_vars' = extendVarEnv id_vars id (id, level1)
+ aliases' = delFromNameEnv aliases (idName id)
-- aliases': when shadowing an alias, we need to make sure the
-- Id is no longer classified as such. E.g.
-- let x = <e1> in case x of x { _DEFAULT -> <e2> }
-- Occurrences of 'x' in e2 shouldn't count as occurrences of e1.
- -- A very tiny optimisation, not sure if it's really worth it
- -- Short-cut when the substitution is a no-op
- out_id | isEmptyTCvSubst subst = in_id
- | otherwise = setIdType in_id out_ty
-
valid_joins'
- | isJoinId out_id = addToUniqMap valid_joins in_id NormalJoinOcc -- Overwrite with new arity
- | otherwise = delFromUniqMap valid_joins in_id -- Remove any existing binding
+ | isJoinId id = addToUniqMap valid_joins id NormalJoinOcc -- Overwrite with new arity
+ | otherwise = delFromUniqMap valid_joins id -- Remove any existing binding
-addInScopeTyCoVar :: InTyCoVar -> OutType -> (OutTyCoVar -> LintM a) -> LintM a
+addInScopeTyCoVar :: TyCoVar -> LintM a -> LintM a
-- This function clones to avoid shadowing of TyCoVars
-addInScopeTyCoVar tcv tcv_type thing_inside
- = LintM $ \ env@(LE { le_level = level, le_in_vars = in_vars, le_subst = subst }) errs ->
- let (tcv', subst') = subst_bndr subst
- level' = level + 1
+addInScopeTyCoVar tcv thing_inside
+ = LintM $ \ env@(LE { le_level = level, le_in_vars = in_vars
+ , le_in_scope = in_scope }) errs ->
+ let level' = level + 1
env' = env { le_level = level'
- , le_in_vars = extendVarEnv in_vars tcv (tcv, tcv_type, level')
- , le_subst = subst' }
- in unLintM (thing_inside tcv') env' errs
- where
- subst_bndr subst
- = (tcv, subst `extendSubstInScope` tcv)
-{-
- | isEmptyTCvSubst subst -- No change in kind
- , not (tcv `elemInScopeSet` in_scope) -- Not already in scope
- = -- Do not extend the substitution, just the in-scope set
- (if (varType tcv `eqType` tcv_type) then (\x->x) else
- pprTrace "addInScopeTyCoVar" (
- vcat [ text "tcv" <+> ppr tcv <+> dcolon <+> ppr (varType tcv)
- , text "tcv_type" <+> ppr tcv_type ])) $
- (tcv, subst `extendSubstInScope` tcv)
-
- -- Clone, and extend the substitution
- | let tcv' = uniqAway in_scope (setVarType tcv tcv_type)
- = (tcv', extendTCvSubstWithClone subst tcv tcv')
- where
- in_scope = substInScopeSet subst
--}
+ , le_in_scope = in_scope `extendInScopeSet` tcv
+ , le_in_vars = extendVarEnv in_vars tcv (tcv, level') }
+ in unLintM thing_inside env' errs
-getInVarEnv :: LintM (VarEnv (InId, OutType, LintLevel))
+getInVarEnv :: LintM (VarEnv (Id, LintLevel))
getInVarEnv = LintM (\env errs -> fromBoxedLResult (Just (le_in_vars env), errs))
-{-
-extendTvSubstL :: TyVar -> Type -> LintM a -> LintM a
-extendTvSubstL tv ty m
- = LintM $ \ env errs ->
- unLintM m (env { le_subst = Type.extendTvSubst (le_subst env) tv ty }) errs
--}
-
markAllJoinsBad :: LintM a -> LintM a
markAllJoinsBad m
= LintM $ \ env errs -> unLintM m (env { le_joins = emptyUniqMap }) errs
@@ -3570,54 +3437,42 @@ markAllJoinsBadIf False m = m
getValidJoins :: LintM (UniqMap Id JoinOcc)
getValidJoins = LintM (\ env errs -> fromBoxedLResult (Just (le_joins env), errs))
-getSubst :: LintM Subst
-getSubst = LintM (\ env errs -> fromBoxedLResult (Just (le_subst env), errs))
-
-substTyM :: InType -> LintM OutType
--- Apply the substitution to the type
--- The substitution is often empty, in which case it is a no-op
-substTyM ty
- = do { subst <- getSubst
- ; return (substTy subst ty) }
-
getUEAliases :: LintM (NameEnv UsageEnv)
getUEAliases = LintM (\ env errs -> fromBoxedLResult (Just (le_ue_aliases env), errs))
getInScope :: LintM InScopeSet
-getInScope = LintM (\ env errs -> fromBoxedLResult (Just (substInScopeSet $ le_subst env), errs))
+getInScope = LintM (\ env errs -> fromBoxedLResult (Just (le_in_scope env), errs))
-lintVarOcc :: InVar -> LintM OutType
+lintVarOcc :: Var -> LintM ()
-- Used at an occurrence of a variable: term variables, type variables, and coercion variables
-- Checks
-- - that it is in scope
-- - that it is not a GlobalId bound by a LocalId
--- - that the InType at the ocurrence matches the InType at the binding site
+-- - that the Type at the ocurrence matches the Type at the binding site
-- - that the variables free in its type are not shadowed at the occurrence site
lintVarOcc v_occ
| isGlobalId v_occ
- = return (idType v_occ)
+ = return ()
| otherwise
= do { in_var_env <- getInVarEnv
; case lookupVarEnv in_var_env v_occ of
Nothing -> failWithL (text pp_what <+> quotes (ppr v_occ)
<+> text "is out of scope")
- Just (v_bndr, out_ty, bind_level)
+ Just (v_bndr, bind_level)
-> do { let bndr_ty = idType v_bndr
; check_bad_global v_bndr
; check_occ_type_match bndr_ty
- ; check_occ_type_scope in_var_env bndr_ty bind_level
- ; return out_ty }
-
+ ; check_occ_type_scope in_var_env bndr_ty bind_level }
}
where
- occ_ty :: InType
+ occ_ty :: Type
occ_ty = idType v_occ
pp_what | isTyVar v_occ = "The type variable"
| isCoVar v_occ = "The coercion variable"
| otherwise = "The value variable"
- check_bad_global :: InVar -> LintM ()
+ check_bad_global :: Var -> LintM ()
-- 'check_bad_global' checks for the case where an /occurrence/ is
-- a GlobalId, but there is an enclosing binding for a LocalId.
-- NB: the in-scope variables are mostly LocalIds, checked by lintIdBndr,
@@ -3637,26 +3492,26 @@ lintVarOcc v_occ
| otherwise
= return ()
- check_occ_type_match :: InType -> LintM ()
+ check_occ_type_match :: Type -> LintM ()
-- Check that the type in /binder/ and the type in the /occurrence/ are the same
check_occ_type_match bndr_ty
- = ensureEqTys bndr_ty occ_ty $ -- Compares InTypes
+ = ensureEqTys bndr_ty occ_ty $ -- Compares Types
mkBndrOccTypeMismatchMsg v_occ bndr_ty occ_ty
- check_occ_type_scope :: VarEnv (InVar,OutType,LintLevel) -> InType -> LintLevel -> LintM ()
+ check_occ_type_scope :: VarEnv (Var,LintLevel) -> Type -> LintLevel -> LintM ()
-- Check that the free vars of the binder's type
-- are not shadowed at the occurrence site
check_occ_type_scope in_var_env bndr_ty bind_level
= checkL (null bad_fvs) $
mkBndrOccFreeVarMsg v_occ occ_ty bad_fvs
where
- bad_fvs :: [InVar]
+ bad_fvs :: [Var]
bad_fvs = filter is_bad (tyCoVarsOfTypeList bndr_ty)
- is_bad :: InVar -> Bool
+ is_bad :: Var -> Bool
-- True of a variable bound inside bind_level
is_bad v = case lookupVarEnv in_var_env v of
- Just (_, _, v_level) -> v_level > bind_level
+ Just (_, v_level) -> v_level > bind_level
Nothing -> True
lookupJoinId :: Id -> LintM (Maybe (JoinArity, JoinOcc))
@@ -3668,21 +3523,21 @@ lookupJoinId id
Just join_occ -> return $ Just (idJoinArity id, join_occ)
Nothing -> return Nothing }
-addAliasUE :: OutId -> UsageEnv -> LintM a -> LintM a
+addAliasUE :: Id -> UsageEnv -> LintM a -> LintM a
addAliasUE id ue thing_inside = LintM $ \ env errs ->
let new_ue_aliases =
extendNameEnv (le_ue_aliases env) (getName id) ue
in
unLintM thing_inside (env { le_ue_aliases = new_ue_aliases }) errs
-varCallSiteUsage :: OutId -> LintM UsageEnv
+varCallSiteUsage :: Id -> LintM UsageEnv
varCallSiteUsage id =
do m <- getUEAliases
return $ case lookupNameEnv m (getName id) of
Nothing -> singleUsageUE id
Just id_ue -> id_ue
-ensureEqTys :: OutType -> OutType -> SDoc -> LintM ()
+ensureEqTys :: Type -> Type -> SDoc -> LintM ()
-- check ty2 is subtype of ty1 (ie, has same structure but usage
-- annotations need only be consistent, not equal)
-- Assumes ty1,ty2 are have already had the substitution applied
@@ -3906,7 +3761,7 @@ mkLetErr bndr rhs
hang (text "Rhs:")
4 (ppr rhs)]
-mkTyAppMsg :: OutType -> Type -> SDoc
+mkTyAppMsg :: Type -> Type -> SDoc
mkTyAppMsg ty arg_ty
= vcat [text "Illegal type application:",
hang (text "Function type:")
@@ -4027,13 +3882,13 @@ mkJoinBndrOccMismatchMsg bndr join_arity_bndr join_arity_occ
, text "Arity at binding site:" <+> ppr join_arity_bndr
, text "Arity at occurrence: " <+> ppr join_arity_occ ]
-mkBndrOccTypeMismatchMsg :: InVar -> InType -> InType -> SDoc
+mkBndrOccTypeMismatchMsg :: Var -> Type -> Type -> SDoc
mkBndrOccTypeMismatchMsg var bndr_ty occ_ty
= vcat [ text "Mismatch in type between binder and occurrence"
, text "Binder: " <+> ppr var <+> dcolon <+> ppr bndr_ty
, text "Occurrence:" <+> ppr var <+> dcolon <+> ppr occ_ty ]
-mkBndrOccFreeVarMsg :: InVar -> InType -> [TyCoVar] -> SDoc
+mkBndrOccFreeVarMsg :: Var -> Type -> [TyCoVar] -> SDoc
mkBndrOccFreeVarMsg var occ_ty bad_tvs
= vcat [ text "Free vars of type are shadowed:" <+> ppr bad_tvs
, text "Occurrence:" <+> ppr var <+> dcolon <+> ppr occ_ty ]
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/28e0ef3c647b0be6876bd52fd595363…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/28e0ef3c647b0be6876bd52fd595363…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/spj-reinstallable-base] Add Real to known-key names [skip ci]
by Simon Peyton Jones (@simonpj) 01 Apr '26
by Simon Peyton Jones (@simonpj) 01 Apr '26
01 Apr '26
Simon Peyton Jones pushed to branch wip/spj-reinstallable-base at Glasgow Haskell Compiler / GHC
Commits:
a9950708 by Simon Peyton Jones at 2026-04-02T00:05:18+01:00
Add Real to known-key names [skip ci]
- - - - -
2 changed files:
- compiler/GHC/Builtin/Names.hs
- libraries/base/src/GHC/KnownKeyNames.hs
Changes:
=====================================
compiler/GHC/Builtin/Names.hs
=====================================
@@ -213,6 +213,7 @@ basicKnownKeyTable
, (mkTcOcc "Bounded", boundedClassKey)
, (mkTcOcc "Enum", enumClassKey)
, (mkTcOcc "Integral", integralClassKey)
+ , (mkTcOcc "Real", realClassKey)
, (mkTcOcc "Data", dataClassKey)
, (mkTcOcc "Ix", ixClassKey)
, (mkTcOcc "Alternative", alternativeClassKey)
=====================================
libraries/base/src/GHC/KnownKeyNames.hs
=====================================
@@ -29,7 +29,7 @@ module GHC.KnownKeyNames
, (<>), mappend
-- Numbers
- , Num, Integral
+ , Num, Integral, Real
, (-), negate, fromInteger, fromRational
, mkRationalBase2, mkRationalBase10
@@ -64,10 +64,10 @@ import GHC.Internal.OverloadedLabels( fromLabel )
import GHC.Internal.Records( HasField, getField )
import qualified GHC.Internal.IsList as IL
-
{- Note [Known-key names and IsList]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Very annoyingly both Foldable and isList
+Very annoyingly both the classes Foldable and IsList have a method `toList`.
+we can't have two known-key names with the same OccName.
-}
isList_toList :: IL.IsList l => l -> [IL.Item l]
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/a9950708ae6cfe0836b58f0bd2c2e5f…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/a9950708ae6cfe0836b58f0bd2c2e5f…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
63099b0f by Simon Jakobi at 2026-04-01T19:02:39-04:00
Add perf test for #13960
Closes #13960.
- - - - -
2 changed files:
- + testsuite/tests/perf/compiler/T13960.hs
- testsuite/tests/perf/compiler/all.T
Changes:
=====================================
testsuite/tests/perf/compiler/T13960.hs
=====================================
@@ -0,0 +1,72 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+-- GHC used to run out of simplifier ticks due to inlining the internals of
+-- `toStrict . toLazyByteString`.
+module T13960 (breaks) where
+
+import Data.ByteString (ByteString)
+import Data.ByteString.Builder (Builder, stringUtf8, toLazyByteString)
+import Data.ByteString.Lazy (toStrict)
+import Data.String (IsString(..))
+
+newtype Query = Query ByteString
+
+toByteString :: Builder -> ByteString
+toByteString x = toStrict (toLazyByteString x)
+
+instance IsString Query where
+ fromString = Query . toByteString . stringUtf8
+
+breaks :: [(Query, Query)]
+breaks =
+ [ ("query001a", "query001b")
+ , ("query002a", "query002b")
+ , ("query003a", "query003b")
+ , ("query004a", "query004b")
+ , ("query005a", "query005b")
+ , ("query006a", "query006b")
+ , ("query007a", "query007b")
+ , ("query008a", "query008b")
+ , ("query009a", "query009b")
+ , ("query010a", "query010b")
+ , ("query011a", "query011b")
+ , ("query012a", "query012b")
+ , ("query013a", "query013b")
+ , ("query014a", "query014b")
+ , ("query015a", "query015b")
+ , ("query016a", "query016b")
+ , ("query017a", "query017b")
+ , ("query018a", "query018b")
+ , ("query019a", "query019b")
+ , ("query020a", "query020b")
+ , ("query021a", "query021b")
+ , ("query022a", "query022b")
+ , ("query023a", "query023b")
+ , ("query024a", "query024b")
+ , ("query025a", "query025b")
+ , ("query026a", "query026b")
+ , ("query027a", "query027b")
+ , ("query028a", "query028b")
+ , ("query029a", "query029b")
+ , ("query030a", "query030b")
+ , ("query031a", "query031b")
+ , ("query032a", "query032b")
+ , ("query033a", "query033b")
+ , ("query034a", "query034b")
+ , ("query035a", "query035b")
+ , ("query036a", "query036b")
+ , ("query037a", "query037b")
+ , ("query038a", "query038b")
+ , ("query039a", "query039b")
+ , ("query040a", "query040b")
+ , ("query041a", "query041b")
+ , ("query042a", "query042b")
+ , ("query043a", "query043b")
+ , ("query044a", "query044b")
+ , ("query045a", "query045b")
+ , ("query046a", "query046b")
+ , ("query047a", "query047b")
+ , ("query048a", "query048b")
+ , ("query049a", "query049b")
+ , ("query050a", "query050b")
+ ]
=====================================
testsuite/tests/perf/compiler/all.T
=====================================
@@ -686,6 +686,12 @@ test ('T13820',
],
compile,
['-v0'])
+test ('T13960',
+ [ collect_compiler_stats('peak_megabytes_allocated', 20),
+ collect_compiler_stats('bytes allocated', 2),
+ ],
+ compile,
+ ['-O'])
test ('T14766',
[ collect_compiler_stats('bytes allocated',2),
pre_cmd('python3 genT14766.py > T14766.hs'),
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/63099b0fd7b1083fd0dcf9d1795dc6b…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/63099b0fd7b1083fd0dcf9d1795dc6b…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][master] 5 commits: Remove signal-based ticker implementations
by Marge Bot (@marge-bot) 01 Apr '26
by Marge Bot (@marge-bot) 01 Apr '26
01 Apr '26
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
8f95534a by Duncan Coutts at 2026-04-01T19:01:52-04:00
Remove signal-based ticker implementations
Fixes issue #27073
All supported platforms should work with the pthreads + nanosleep based
ticker implementation. This avoids all the problems with using signals.
In practice, all supported platforms were probably using the non-signal
tickers already, which is probably why we do not get lots of reports
about deadlocks and other weirdness: we were definately using functions
that are not async signal safe in the tick handler (such as fflush to
flussh the eventlog).
Only Solaris was explicitly using the timer_create ticker impl, and even
Solaris could probably use the pthreads one (if anyone cared: Solaris is
no longer a Teir 3 supported platform).
Plausibly the only supported platform that this will change will be AIX,
which should now use the pthreads impl.
- - - - -
51b32b0d by Duncan Coutts at 2026-04-01T19:01:52-04:00
Tidy up some timer/ticker comments elsewhere
- - - - -
7562bcd7 by Duncan Coutts at 2026-04-01T19:01:52-04:00
Remove now-unused install_vtalrm_handler
Support function used by both of the signal-based ticker
implementations.
- - - - -
6da127c7 by Duncan Coutts at 2026-04-01T19:01:52-04:00
No longer probe for timer_create in rts/configure
It was only used by the TimerCreate.c ticker impl.
- - - - -
3fd490fa by Duncan Coutts at 2026-04-01T19:01:53-04:00
Note that rtsTimerSignal is deprecated.
- - - - -
10 changed files:
- − m4/fp_check_timer_create.m4
- rts/Timer.c
- rts/configure.ac
- rts/include/rts/Timer.h
- rts/include/stg/SMP.h
- rts/posix/Signals.c
- rts/posix/Signals.h
- rts/posix/Ticker.c
- − rts/posix/ticker/Setitimer.c
- − rts/posix/ticker/TimerCreate.c
Changes:
=====================================
m4/fp_check_timer_create.m4 deleted
=====================================
@@ -1,110 +0,0 @@
-# Check for a working timer_create(). We need a pretty detailed check
-# here, because there exist partially-working implementations of
-# timer_create() in certain versions of Linux (see bug #1933).
-#
-AC_DEFUN([FP_CHECK_TIMER_CREATE],[
-AC_CHECK_FUNC([timer_create],[HAVE_timer_create=yes],[HAVE_timer_create=no])
-
-if test "$HAVE_timer_create" = "yes"
-then
- if test "$cross_compiling" = "yes"
- then
- # We can't test timer_create when we're cross-compiling, so we
- # optimistiaclly assume that it actually works properly.
- AC_DEFINE([USE_TIMER_CREATE], 1, [Define to 1 if we can use timer_create(CLOCK_REALTIME,...)])
- else
- AC_CACHE_CHECK([for a working timer_create(CLOCK_REALTIME)],
- [fptools_cv_timer_create_works],
- [AC_TRY_RUN([
-#include <stdio.h>
-#if defined(HAVE_STDLIB_H)
-#include <stdlib.h>
-#endif
-#include <time.h>
-#if defined(HAVE_SIGNAL_H)
-#include <signal.h>
-#endif
-#if defined(HAVE_UNISTD_H)
-#include <unistd.h>
-#endif
-
-static volatile int tock = 0;
-static void handler(int i)
-{
- tock = 1;
-}
-
-static void timeout(int i)
-{
- // timer_settime() has been known to hang, so just in case
- // we install a 1-second timeout (see #2257)
- exit(99);
-}
-
-int main(int argc, char *argv[])
-{
-
- struct sigevent ev;
- timer_t timer;
- struct itimerspec it;
- struct sigaction action;
- int m,n,count = 0;
-
- ev.sigev_notify = SIGEV_SIGNAL;
- ev.sigev_signo = SIGVTALRM;
-
- action.sa_handler = handler;
- action.sa_flags = 0;
- sigemptyset(&action.sa_mask);
- if (sigaction(SIGVTALRM, &action, NULL) == -1) {
- fprintf(stderr,"SIGVTALRM problem\n");
- exit(3);
- }
-
- action.sa_handler = timeout;
- action.sa_flags = 0;
- sigemptyset(&action.sa_mask);
- if (sigaction(SIGALRM, &action, NULL) == -1) {
- fprintf(stderr,"SIGALRM problem\n");
- exit(3);
- }
- alarm(1);
-
- if (timer_create(CLOCK_REALTIME, &ev, &timer) != 0) {
- fprintf(stderr,"No CLOCK_REALTIME timer\n");
- exit(2);
- }
-
- tock = 0;
-
- it.it_value.tv_sec = 0;
- it.it_value.tv_nsec = 1000000; // 1ms
- it.it_interval = it.it_value;
- if (timer_settime(timer, 0, &it, NULL) != 0) {
- fprintf(stderr,"settime problem\n");
- exit(4);
- }
-
- // some environments have coarse scheduler/timer granularity of ~10ms and worse
- usleep(100000); // 100ms
-
- if (!tock) {
- fprintf(stderr,"no CLOCK_REALTIME signal\n");
- exit(5);
- }
-
- timer_delete(timer);
-
- exit(0);
-}
- ],
- [fptools_cv_timer_create_works=yes],
- [fptools_cv_timer_create_works=no])
- ])
-case $fptools_cv_timer_create_works in
- yes) AC_DEFINE([USE_TIMER_CREATE], 1,
- [Define to 1 if we can use timer_create(CLOCK_REALTIME,...)]);;
-esac
- fi
-fi
-])
=====================================
rts/Timer.c
=====================================
@@ -7,12 +7,14 @@
* ---------------------------------------------------------------------------*/
/*
- * The interval timer is used for profiling and for context switching in the
- * threaded build.
+ * The interval timer is used for profiling and for context switching.
*
* This file defines the platform-independent view of interval timing, relying
- * on platform-specific services to install and run the timers.
+ * on platform-specific services to install and run the timers. See
+ * posix/Ticker.c and win32/Ticker.c for the platform specific parts.
*
+ * If you are looking for Itimer.c then you either file or one of the
+ * platform-specific Ticker.c files.
*/
#include "rts/PosixSource.h"
=====================================
rts/configure.ac
=====================================
@@ -204,7 +204,6 @@ FP_MUSTTAIL
dnl ** check for librt
AC_CHECK_FUNCS(clock_gettime timer_settime)
-FP_CHECK_TIMER_CREATE
dnl ** check for Apple's "interesting" long double compatibility scheme
AC_MSG_CHECKING(for printf\$LDBLStub)
=====================================
rts/include/rts/Timer.h
=====================================
@@ -15,4 +15,4 @@
void startTimer (void);
void stopTimer (void);
-int rtsTimerSignal (void);
+int rtsTimerSignal (void); // Deprecated: see issue #27073
=====================================
rts/include/stg/SMP.h
=====================================
@@ -21,7 +21,7 @@ void arm_atomic_spin_unlock(void);
// Unconditionally atomic operations
// These are atomic even in the non-threaded RTS. These are necessary in the
// Proftimer implementation, which may be called from the pthreads-based
-// ITimer implementation.
+// Ticker implementation.
#define RELAXED_LOAD_ALWAYS(ptr) __atomic_load_n(ptr, __ATOMIC_RELAXED)
#define RELAXED_STORE_ALWAYS(ptr,val) __atomic_store_n(ptr, val, __ATOMIC_RELAXED)
#define RELAXED_ADD_ALWAYS(ptr,val) __atomic_add_fetch(ptr, val, __ATOMIC_RELAXED)
=====================================
rts/posix/Signals.c
=====================================
@@ -640,35 +640,6 @@ set_sigtstp_action (bool handle)
}
}
-/* Used by ItimerTimerCreate and ItimerSetitimer implementations */
-void
-install_vtalrm_handler(int sig, TickProc handle_tick)
-{
- struct sigaction action;
- memset(&action, 0, sizeof(struct sigaction));
-
- action.sa_handler = handle_tick;
-
- sigemptyset(&action.sa_mask);
-
-#if defined(SA_RESTART)
- // specify SA_RESTART. One consequence if we don't do this is
- // that readline gets confused by the -threaded RTS. It seems
- // that if a SIGALRM handler is installed without SA_RESTART,
- // readline installs its own SIGALRM signal handler (see
- // readline's signals.c), and this somehow causes readline to go
- // wrong when the input exceeds a single line (try it).
- action.sa_flags = SA_RESTART;
-#else
- action.sa_flags = 0;
-#endif
-
- if (sigaction(sig, &action, NULL) == -1) {
- sysErrorBelch("sigaction");
- stg_exit(EXIT_FAILURE);
- }
-}
-
/* -----------------------------------------------------------------------------
* Install default signal handlers.
*
=====================================
rts/posix/Signals.h
=====================================
@@ -25,8 +25,6 @@ extern siginfo_t *next_pending_handler;
void startSignalHandlers(Capability *cap);
#endif
-void install_vtalrm_handler(int sig, TickProc handle_tick);
-
/* Communicating with the IO manager thread (see GHC.Conc).
*
* TODO: these I/O manager things are not related to signals and ought to live
=====================================
rts/posix/Ticker.c
=====================================
@@ -2,116 +2,38 @@
*
* (c) The GHC Team, 1995-2007
*
- * Interval timer for profiling and pre-emptive scheduling.
+ * Posix implementation(s) of the interval timer for profiling and pre-emptive
+ * scheduling.
*
* ---------------------------------------------------------------------------*/
-/*
- * The interval timer is used for profiling and for context switching in the
- * threaded build. Though POSIX 1003.1b includes a standard interface for
- * such things, no one really seems to be implementing them yet. Even
- * Solaris 2.3 only seems to provide support for @CLOCK_REAL@, whereas we're
- * keen on getting access to @CLOCK_VIRTUAL@.
- *
- * Hence, we often use the old-fashioned @setitimer@ that just about everyone
- * seems to support. So much for standards.
- *
- * If you are looking for Itimer.c then this is the right file. I renamed it
- * Ticker.c for consistency.
+/* The interval timer is used for profiling and for context switching.
+ * This file defines the platform-specific services to install and run the
+ * timers, and we call this the ticker. See rts/Timer.c for the
+ * platform-dependent view of interval timing.
+ *
+ * Historically we had ticker implementations using signals. This was always a
+ * rather shakey thing to do but we had few alternatives.
+ * - One problem with using signals is that there are severe limits on what
+ * code can be called from signal handlers. In particular it's not possible
+ * to take locks in a signal handler contex. This was enough for contex
+ * switching, but it's no good for things like flushing the eventlog, or
+ * waking up rts tasks.
+ * - We also want to avoid using alarm signals, as these can interrupt system
+ * calls (#10840) or can be overwritten by user code.
*/
-#include "rts/PosixSource.h"
-
-/* We've defined _POSIX_SOURCE via "rts/PosixSource.h", and yet still use
- some non-POSIX features. With _POSIX_SOURCE defined, visibility of
- non-POSIX extension prototypes requires _DARWIN_C_SOURCE on Mac OS X,
- __BSD_VISIBLE on FreeBSD and DragonflyBSD, and _NetBSD_SOURCE on
- NetBSD. Otherwise, for example, code using pthread_setname_np(3) and
- variants will not compile. We must therefore define the additional
- macros that expose non-POSIX APIs early, before any of the relevant
- system headers are included via "Rts.h".
-
- An alternative approach could be to write portable wrappers or stubs for all
- the non-posix functions in a C-module that does not include "rts/PosixSource.h",
- and then use only POSIX features and the portable wrapper functions in all
- other C-modules. */
-#include "ghcconfig.h"
-#if defined(freebsd_HOST_OS) || defined(dragonfly_HOST_OS)
-#define __BSD_VISIBLE 1
-#endif
-#if defined(darwin_HOST_OS)
-#define _DARWIN_C_SOURCE 1
-#endif
-#if defined(netbsd_HOST_OS)
-#define _NETBSD_SOURCE 1
-#endif
-
-#include "Rts.h"
-
-/*
- * It used to be that timer_create doesn't exist on iOS and setitimer doesn't fire on iOS
- * during debugging. See #7723. Seems to be an issue with signals.
- *
- * We also want to avoid using alarm signals, as these can interrupt system calls (#10840)
- * or can be overwritten by user code.
+/* Select a ticker implementation to use:
*
- * So we are using the pthread based implementation.
- */
-#if defined(ios_HOST_OS) || defined(darwin_HOST_OS)
-#define USE_PTHREAD_FOR_ITIMER
-#endif
-
-/*
- * On Linux we can use timerfd_* (introduced in Linux
- * 2.6.25) and a thread instead of alarm signals. It avoids the risk of
- * interrupting syscalls (see #10840) and the risk of being accidentally
- * modified in user code using signals. NetBSD has also added timerfd
- * support since version 10.
+ * On modern Linux, FreeBSD and NetBSD we can use timerfd_create and a thread
+ * that waits on it using poll. Linux has had timerfd since version 2.6.25.
+ * NetBSD has had timerfd since version 10, and FreeBSD since version 15.
*
- * For older version of linux/netbsd without timerfd we fall back to the
- * pthread based implementation.
+ * For older version of linux/bsd without timerfd, and for all other posix
+ * platforms, we use the implementation using posix pthreads and nanosleep().
*/
#if defined(HAVE_SYS_TIMERFD_H)
-#define USE_TIMERFD_FOR_ITIMER
-#endif
-
-#if defined(linux_HOST_OS)
-#define USE_PTHREAD_FOR_ITIMER
-#endif
-
-#if defined(netbsd_HOST_OS)
-#define USE_PTHREAD_FOR_ITIMER
-#endif
-
-#if defined(freebsd_HOST_OS)
-#define USE_PTHREAD_FOR_ITIMER
-#endif
-
-#if defined(solaris2_HOST_OS)
-/* USE_TIMER_CREATE is usually disabled for Solaris. In fact it is
- supported well on this OS, but requires additional privilege. When
- user does not have it, then the testing configure program fails
- which results in USE_TIMER_CREATE not defined.
- On the other hand when we cross-compile, then we optimistically
- assume usage of timer_create function. The problem is that if we
- cross compile for example from i386-solaris2 to x86_64-solaris2,
- then the build fails with error like this:
-
-ghc-stage2: timer_create: Not owner
-
- which happens on first ghc-stage2 invocation. So to support
- cross-compilation to Solaris we manually undefine USE_TIMER_CREATE
- here */
-#undef USE_TIMER_CREATE
-#endif /* solaris2_HOST_OS */
-
-// Select the variant to use
-#if defined(USE_TIMERFD_FOR_ITIMER)
#include "ticker/TimerFd.c"
-#elif defined(USE_PTHREAD_FOR_ITIMER)
-#include "ticker/Pthread.c"
-#elif defined(USE_TIMER_CREATE)
-#include "ticker/TimerCreate.c"
#else
-#include "ticker/Setitimer.c"
+#include "ticker/Pthread.c"
#endif
=====================================
rts/posix/ticker/Setitimer.c deleted
=====================================
@@ -1,80 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * (c) The GHC Team, 1995-2007
- *
- * Interval timer for profiling and pre-emptive scheduling.
- *
- * ---------------------------------------------------------------------------*/
-
-#include "rts/PosixSource.h"
-#include "Rts.h"
-
-#include "Ticker.h"
-#include "Proftimer.h"
-#include "Schedule.h"
-#include "posix/Clock.h"
-#include "posix/Signals.h"
-
-#include <time.h>
-#if HAVE_SYS_TIME_H
-# include <sys/time.h>
-#endif
-
-#if defined(HAVE_SIGNAL_H)
-# include <signal.h>
-#endif
-
-#include <string.h>
-
-static Time itimer_interval = DEFAULT_TICK_INTERVAL;
-
-void
-initTicker (Time interval, TickProc handle_tick)
-{
- itimer_interval = interval;
- install_vtalrm_handler(SIGALRM, handle_tick);
-}
-
-void
-startTicker(void)
-{
- struct itimerval it;
-
- it.it_value.tv_sec = TimeToSeconds(itimer_interval);
- it.it_value.tv_usec = TimeToUS(itimer_interval) % 1000000;
- it.it_interval = it.it_value;
-
- if (setitimer(ITIMER_REAL, &it, NULL) != 0) {
- sysErrorBelch("setitimer");
- stg_exit(EXIT_FAILURE);
- }
-}
-
-void
-stopTicker(void)
-{
- struct itimerval it;
-
- it.it_value.tv_sec = 0;
- it.it_value.tv_usec = 0;
- it.it_interval = it.it_value;
-
- if (setitimer(ITIMER_REAL, &it, NULL) != 0) {
- sysErrorBelch("setitimer");
- stg_exit(EXIT_FAILURE);
- }
-}
-
-void
-exitTicker (bool wait STG_UNUSED)
-{
- return;
-}
-
-int
-rtsTimerSignal(void)
-{
- return SIGALRM;
- // Using SIGALRM can leads to problems, see #850. But we have no
- // option if timer_create() is not available.
-}
=====================================
rts/posix/ticker/TimerCreate.c deleted
=====================================
@@ -1,92 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * (c) The GHC Team, 1995-2007
- *
- * Interval timer for profiling and pre-emptive scheduling.
- *
- * ---------------------------------------------------------------------------*/
-
-#include "rts/PosixSource.h"
-#include "Rts.h"
-
-#include "Ticker.h"
-#include "Proftimer.h"
-#include "Schedule.h"
-#include "posix/Clock.h"
-#include "posix/Signals.h"
-
-#if defined(HAVE_SIGNAL_H)
-# include <signal.h>
-#endif
-
-#include <string.h>
-
-static Time itimer_interval = DEFAULT_TICK_INTERVAL;
-static timer_t timer;
-
-void
-initTicker (Time interval, TickProc handle_tick)
-{
- itimer_interval = interval;
-
- struct sigevent ev;
-
- // Keep programs like valgrind happy
- memset(&ev, 0, sizeof(ev));
-
- ev.sigev_notify = SIGEV_SIGNAL;
- ev.sigev_signo = SIGVTALRM;
-
- if (timer_create(CLOCK_ID, &ev, &timer) != 0) {
- sysErrorBelch("timer_create");
- stg_exit(EXIT_FAILURE);
- }
-
- install_vtalrm_handler(SIGVTALRM, handle_tick);
-}
-
-void
-startTicker(void)
-{
- struct itimerspec it;
-
- it.it_value.tv_sec = TimeToSeconds(itimer_interval);
- it.it_value.tv_nsec = TimeToNS(itimer_interval) % 1000000000;
- it.it_interval = it.it_value;
-
- if (timer_settime(timer, 0, &it, NULL) != 0) {
- sysErrorBelch("timer_settime");
- stg_exit(EXIT_FAILURE);
- }
-}
-
-void
-stopTicker(void)
-{
- struct itimerspec it;
-
- it.it_value.tv_sec = 0;
- it.it_value.tv_nsec = 0;
- it.it_interval = it.it_value;
-
- if (timer_settime(timer, 0, &it, NULL) != 0) {
- sysErrorBelch("timer_settime");
- stg_exit(EXIT_FAILURE);
- }
-}
-
-void
-exitTicker (bool wait STG_UNUSED)
-{
- // Before deleting the timer set the signal to ignore to avoid the
- // possibility of the signal being delivered after the timer is deleted.
- signal(SIGVTALRM, SIG_IGN);
- timer_delete(timer);
- // ignore errors - we don't really care if it fails.
-}
-
-int
-rtsTimerSignal(void)
-{
- return SIGVTALRM;
-}
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/5ae43275ae8fa1a6938f2efcc774c3…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/5ae43275ae8fa1a6938f2efcc774c3…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
01 Apr '26
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
5ae43275 by Peng Fan at 2026-04-01T19:01:06-04:00
NCG/LA64: add cmpxchg and xchg primops
And append some new instructions for LA664 uarch.
Apply fix to cmpxchg-prim by Andreas Klebinger.
Suggestions in https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15515
- - - - -
3 changed files:
- compiler/GHC/CmmToAsm/LA64/CodeGen.hs
- compiler/GHC/CmmToAsm/LA64/Instr.hs
- compiler/GHC/CmmToAsm/LA64/Ppr.hs
Changes:
=====================================
compiler/GHC/CmmToAsm/LA64/CodeGen.hs
=====================================
@@ -57,6 +57,12 @@ import Control.Monad
import GHC.Cmm.Dataflow.Label
import GHC.Types.Unique.DSM
import GHC.Types.Literal.Floating
+import GHC.Unit.Types ( ghcInternalUnitId )
+
+la664Enabled :: NatM Bool
+la664Enabled = do
+ config <- getConfig
+ return (ncgLa664Enabled config)
-- [General layout of an NCG]
cmmTopCodeGen ::
@@ -1651,6 +1657,10 @@ genPrim (MO_Prefetch_Data _n) [] [_] = return nilOL
genPrim (MO_AtomicRead w mo) [dst] [addr] = genAtomicRead w mo dst addr
genPrim (MO_AtomicWrite w mo) [] [addr,val] = genAtomicWrite w mo addr val
+genPrim (MO_AtomicRMW width amop) [dst] [addr,n] = genLibCCall (atomicRMWLabel width amop) [dst] [addr,n]
+genPrim (MO_Cmpxchg width) [dst] [addr,expe,new] = genCmpxchg width dst addr expe new
+genPrim (MO_Xchg width) [dst] [addr,value] = genXchg width dst addr value
+
genPrim mop@(MO_S_Mul2 _w) _ _ = unsupported mop
genPrim mop@(MO_S_QuotRem _w) _ _ = unsupported mop
genPrim mop@(MO_U_QuotRem _w) _ _ = unsupported mop
@@ -1674,9 +1684,6 @@ genPrim (MO_PopCnt width) [dst] [src] = genLibCCall (popCntLabel w
genPrim (MO_Pdep width) [dst] [src,mask] = genLibCCall (pdepLabel width) [dst] [src,mask]
genPrim (MO_Pext width) [dst] [src,mask] = genLibCCall (pextLabel width) [dst] [src,mask]
genPrim (MO_UF_Conv width) [dst] [src] = genLibCCall (word2FloatLabel width) [dst] [src]
-genPrim (MO_AtomicRMW width amop) [dst] [addr,n] = genLibCCall (atomicRMWLabel width amop) [dst] [addr,n]
-genPrim (MO_Cmpxchg width) [dst] [addr,old,new] = genLibCCall (cmpxchgLabel width) [dst] [addr,old,new]
-genPrim (MO_Xchg width) [dst] [addr,val] = genLibCCall (xchgLabel width) [dst] [addr,val]
genPrim (MO_Memcpy _align) [] [dst,src,n] = genLibCCall (fsLit "memcpy") [] [dst,src,n]
genPrim (MO_Memmove _align) [] [dst,src,n] = genLibCCall (fsLit "memmove") [] [dst,src,n]
genPrim (MO_Memcmp _align) [rst] [dst,src,n] = genLibCCall (fsLit "memcmp") [rst] [dst,src,n]
@@ -1872,6 +1879,20 @@ genBitRev w dst src = do
)
_ -> return ( code_x `snocOL` BITREV (OpReg w dst_reg) (OpReg w reg_x))
+genPrimCCall
+ :: FastString
+ -> [CmmFormal]
+ -> [CmmActual]
+ -> NatM InstrBlock
+
+genPrimCCall name dsts args = do
+ config <- getConfig
+ target <-
+ cmmMakeDynamicReference config CallReference
+ $ mkCmmCodeLabel ghcInternalUnitId name
+ let cconv = ForeignConvention CCallConv [NoHint] [NoHint] CmmMayReturn
+ genCCall target cconv dsts args
+
-- Generate C call to the given function in libc
genLibCCall :: FastString -> [CmmFormal] -> [CmmActual] -> NatM InstrBlock
genLibCCall name dsts args = do
@@ -1945,6 +1966,52 @@ genAtomicWrite w mo addr val = do
)
_ -> panic $ "Unexpected MemOrderAcquire on an AtomicWrite" ++ show mo
+genCmpxchg :: Width -> LocalReg -> CmmExpr -> CmmExpr -> CmmExpr -> NatM InstrBlock
+genCmpxchg w dst addr expe new = do
+ config <- getConfig
+ let
+ platform = ncgPlatform config
+ format = intFormat w
+
+ la664Enabled >>= \case
+
+ True -> do
+ (addr_reg, _, code_addr) <- getSomeReg addr
+ (expe_reg, _, code_expe) <- getSomeReg expe
+ (new_reg, _, code_new) <- getSomeReg new
+ let dst_reg = getRegisterReg platform (CmmLocal dst)
+ return $ code_addr `appOL` code_expe `appOL` code_new `appOL` toOL
+ [
+ -- Behave like the GCC builtin CAS operation
+ AMCASDB format (OpReg w expe_reg) (OpReg w new_reg) (OpReg w addr_reg),
+ MOV (OpReg w dst_reg) (OpReg w expe_reg)
+ ]
+
+ False ->
+ genPrimCCall (cmpxchgLabel w) [dst] [addr,expe,new]
+
+genXchg :: Width -> LocalReg -> CmmExpr -> CmmExpr -> NatM InstrBlock
+genXchg w dst addr val = do
+ config <- getConfig
+ tmp <- getNewRegNat II64
+ let
+ platform = ncgPlatform config
+ format = intFormat w
+
+ la664Enabled >>= \case
+
+ True -> do
+ (addr_reg, _, code_addr) <- getSomeReg addr
+ (val_reg, _, code_val) <- getSomeReg val
+ let dst_reg = getRegisterReg platform (CmmLocal dst)
+ return $ code_addr `appOL` code_val `appOL` toOL
+ [
+ AMSWAPDB format (OpReg w tmp) (OpReg w val_reg) (OpReg w addr_reg),
+ MOV (OpReg W64 dst_reg) (OpReg W64 tmp)
+ ]
+ False ->
+ genPrimCCall (xchgLabel w) [dst] [addr,val]
+
-- -----------------------------------------------------------------------------
{-
Generating C calls
@@ -1977,6 +2044,7 @@ member of a structure or union argument, or a vector/floating-point argument
wider than FRLEN may be passed in a GAR.
-}
+-- Generate C call to the given function in ghc-prim
genCCall
:: CmmExpr -- address of func call
-> ForeignConvention -- calling convention
=====================================
compiler/GHC/CmmToAsm/LA64/Instr.hs
=====================================
@@ -150,10 +150,11 @@ regUsageOfInstr platform instr = case instr of
-- ranges, corresponding to 2 and 1 instruction implementations respectively.
--
-- BCOND1 is selected by default.
- BCOND1 _ j d t -> usage (regTarget t ++ regOp j ++ regOp d, [])
- BCOND _ j d t -> usage (regTarget t ++ regOp j ++ regOp d, [])
- BEQZ j t -> usage (regTarget t ++ regOp j, [])
- BNEZ j t -> usage (regTarget t ++ regOp j, [])
+ BCOND1 _ j d t -> usage (regTarget t ++ regOp j ++ regOp d, [])
+ BCOND _ j d t -> usage (regTarget t ++ regOp j ++ regOp d, [])
+ BEQZ1 o1 o2 -> usage (regOp o1 ++ regOp o2, [])
+ BEQZ j t -> usage (regTarget t ++ regOp j, [])
+ BNEZ j t -> usage (regTarget t ++ regOp j, [])
-- 5. Common Memory Access Instructions --------------------------------------
LD _ dst src -> usage (regOp src, regOp dst)
LDU _ dst src -> usage (regOp src, regOp dst)
@@ -168,7 +169,17 @@ regUsageOfInstr platform instr = case instr of
-- LDCOND dst src1 src2 -> usage (regOp src1 ++ regOp src2, regOp dst)
-- STCOND dst src1 src2 -> usage (regOp src1 ++ regOp src2, regOp dst)
-- 7. Atomic Memory Access Instructions --------------------------------------
+ -- In LoongArch, if the AM* atomic memory access instruction has the same register number as rd and rj,
+ -- the execution will trigger an Instruction Non-defined Exception. Here should be avoided.
AMSWAPDB _ dst src1 src2 -> usage (regOp src1 ++ regOp src2, regOp dst)
+ AMADDDB _ dst src1 src2 -> usage (regOp src1 ++ regOp src2, regOp dst)
+ AMANDDB _ dst src1 src2 -> usage (regOp src1 ++ regOp src2, regOp dst)
+ AMORDB _ dst src1 src2 -> usage (regOp src1 ++ regOp src2, regOp dst)
+ AMXORDB _ dst src1 src2 -> usage (regOp src1 ++ regOp src2, regOp dst)
+ --AMCASDB _ dst src1 src2 -> usage (regOp src1 ++ regOp src2, regOp src1 ++ regOp src2 ++ regOp dst)
+ AMCASDB _ dst src1 src2 -> usage (regOp dst ++ regOp src1 ++ regOp src2, regOp dst)
+ LL _ dst src1 _ -> usage (regOp src1, regOp dst)
+ SC _ dst src1 _ -> usage (regOp src1, regOp dst)
-- 8. Barrier Instructions ---------------------------------------------------
DBAR _hint -> usage ([], [])
IBAR _hint -> usage ([], [])
@@ -330,6 +341,7 @@ patchRegsOfInstr instr env = case instr of
TAIL36 r t -> TAIL36 (patchOp r) (patchTarget t)
BCOND1 c j d t -> BCOND1 c (patchOp j) (patchOp d) (patchTarget t)
BCOND c j d t -> BCOND c (patchOp j) (patchOp d) (patchTarget t)
+ BEQZ1 o1 o2 -> BEQZ1 (patchOp o1) (patchOp o2)
BEQZ j t -> BEQZ (patchOp j) (patchTarget t)
BNEZ j t -> BNEZ (patchOp j) (patchTarget t)
-- 5. Common Memory Access Instructions --------------------------------------
@@ -348,6 +360,13 @@ patchRegsOfInstr instr env = case instr of
-- STCOND o1 o2 o3 -> STCOND (patchOp o1) (patchOp o2) (patchOp o3)
-- 7. Atomic Memory Access Instructions --------------------------------------
AMSWAPDB f o1 o2 o3 -> AMSWAPDB f (patchOp o1) (patchOp o2) (patchOp o3)
+ AMADDDB f o1 o2 o3 -> AMADDDB f (patchOp o1) (patchOp o2) (patchOp o3)
+ AMANDDB f o1 o2 o3 -> AMANDDB f (patchOp o1) (patchOp o2) (patchOp o3)
+ AMORDB f o1 o2 o3 -> AMORDB f (patchOp o1) (patchOp o2) (patchOp o3)
+ AMXORDB f o1 o2 o3 -> AMXORDB f (patchOp o1) (patchOp o2) (patchOp o3)
+ AMCASDB f o1 o2 o3 -> AMCASDB f (patchOp o1) (patchOp o2) (patchOp o3)
+ LL f o1 o2 o3 -> LL f (patchOp o1) (patchOp o2) (patchOp o3)
+ SC f o1 o2 o3 -> SC f (patchOp o1) (patchOp o2) (patchOp o3)
-- 8. Barrier Instructions ---------------------------------------------------
DBAR o1 -> DBAR o1
IBAR o1 -> IBAR o1
@@ -398,6 +417,7 @@ isJumpishInstr instr = case instr of
TAIL36 {} -> True
BCOND1 {} -> True
BCOND {} -> True
+ BEQZ1 {} -> True
BEQZ {} -> True
BNEZ {} -> True
_ -> False
@@ -718,6 +738,7 @@ data Instr
| TAIL36 Operand Target
| BCOND1 Cond Operand Operand Target
| BCOND Cond Operand Operand Target
+ | BEQZ1 Operand Operand
| BEQZ Operand Target
| BNEZ Operand Target
-- 5. Common Memory Access Instructions --------------------------------------
@@ -733,6 +754,13 @@ data Instr
-- 6. Bound Check Memory Access Instructions ---------------------------------
-- 7. Atomic Memory Access Instructions --------------------------------------
| AMSWAPDB Format Operand Operand Operand
+ | AMADDDB Format Operand Operand Operand
+ | AMANDDB Format Operand Operand Operand
+ | AMORDB Format Operand Operand Operand
+ | AMXORDB Format Operand Operand Operand
+ | AMCASDB Format Operand Operand Operand
+ | LL Format Operand Operand Operand
+ | SC Format Operand Operand Operand
-- 8. Barrier Instructions ---------------------------------------------------
| DBAR BarrierType
| IBAR BarrierType
@@ -839,6 +867,7 @@ instrCon i =
TAIL36{} -> "TAIL36"
BCOND1{} -> "BCOND1"
BCOND{} -> "BCOND"
+ BEQZ1{} -> "BEQZ1"
BEQZ{} -> "BEQZ"
BNEZ{} -> "BNEZ"
LD{} -> "LD"
@@ -851,6 +880,13 @@ instrCon i =
STPTR{} -> "STPTR"
PRELD{} -> "PRELD"
AMSWAPDB{} -> "AMSWAPDB"
+ AMADDDB{} -> "AMADDDB"
+ AMANDDB{} -> "AMANDDB"
+ AMORDB{} -> "AMORDB"
+ AMXORDB{} -> "AMXORDB"
+ AMCASDB{} -> "AMCASDB"
+ LL{} -> "LL"
+ SC{} -> "SC"
DBAR{} -> "DBAR"
IBAR{} -> "IBAR"
FCVT{} -> "FCVT"
=====================================
compiler/GHC/CmmToAsm/LA64/Ppr.hs
=====================================
@@ -852,6 +852,7 @@ pprInstr platform instr = case instr of
line $ text "\tbgeu" <+> pprOp platform d <> comma <+> pprOp platform j <> comma <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
UGT ->
line $ text "\tbltu" <+> pprOp platform d <> comma <+> pprOp platform j <> comma <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
+
_ -> line $ text "\t" <> pprBcond c <+> pprOp platform j <> comma <+> pprOp platform d <> comma <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
BCOND1 _ _ _ (TLabel _) -> panic "LA64.ppr: BCOND1: No conditional branching to TLabel!"
@@ -916,17 +917,18 @@ pprInstr platform instr = case instr of
BCOND _ _ _ (TReg _) -> panic "LA64.ppr: BCOND: No conditional branching to registers!"
+ BEQZ1 o1 o2 | isImmOp o2 -> op2 (text "\tbeqz") o1 o2
BEQZ j (TBlock bid) ->
line $ text "\tbeqz" <+> pprOp platform j <> comma <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
BEQZ j (TLabel lbl) ->
line $ text "\tbeqz" <+> pprOp platform j <> comma <+> pprAsmLabel platform lbl
- BEQZ _ (TReg _) -> panic "LA64.ppr: BEQZ: No conditional branching to registers!"
+ BEQZ _ (TReg _) -> panic "LA64.ppr: BEQZ: No conditional branching to registers!"
BNEZ j (TBlock bid) ->
line $ text "\tbnez" <+> pprOp platform j <> comma <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
BNEZ j (TLabel lbl) ->
line $ text "\tbnez" <+> pprOp platform j <> comma <+> pprAsmLabel platform lbl
- BNEZ _ (TReg _) -> panic "LA64.ppr: BNEZ: No conditional branching to registers!"
+ BNEZ _ (TReg _) -> panic "LA64.ppr: BNEZ: No conditional branching to registers!"
-- 5. Common Memory Access Instructions --------------------------------------
-- LD.{B[U]/H[U]/W[U]/D}, ST.{B/H/W/D}: AddrRegImm
@@ -1020,8 +1022,29 @@ pprInstr platform instr = case instr of
AMSWAPDB II32 o1 o2 o3 -> op3 (text "\tamswap_db.w") o1 o2 o3
AMSWAPDB II64 o1 o2 o3 -> op3 (text "\tamswap_db.d") o1 o2 o3
-- AM.{SWAP/ADD}[_DB].{B/H}
+ AMADDDB II8 o1 o2 o3 -> op3 (text "\tamadd_db.b") o1 o2 o3
+ AMADDDB II16 o1 o2 o3 -> op3 (text "\tamadd_db.h") o1 o2 o3
+ AMADDDB II32 o1 o2 o3 -> op3 (text "\tamadd_db.w") o1 o2 o3
+ AMADDDB II64 o1 o2 o3 -> op3 (text "\tamadd_db.d") o1 o2 o3
+
+ AMANDDB II32 o1 o2 o3 -> op3 (text "\tamand_db.w") o1 o2 o3
+ AMANDDB II64 o1 o2 o3 -> op3 (text "\tamand_db.d") o1 o2 o3
+
+ AMORDB II32 o1 o2 o3 -> op3 (text "\tamor_db.w") o1 o2 o3
+ AMORDB II64 o1 o2 o3 -> op3 (text "\tamor_db.d") o1 o2 o3
+
+ AMXORDB II32 o1 o2 o3 -> op3 (text "\tamxor_db.w") o1 o2 o3
+ AMXORDB II64 o1 o2 o3 -> op3 (text "\tamxor_db.d") o1 o2 o3
-- AMCAS[_DB].{B/H/W/D}
+ AMCASDB II8 o1 o2 o3 -> op3 (text "\tamcas_db.b") o1 o2 o3
+ AMCASDB II16 o1 o2 o3 -> op3 (text "\tamcas_db.h") o1 o2 o3
+ AMCASDB II32 o1 o2 o3 -> op3 (text "\tamcas_db.w") o1 o2 o3
+ AMCASDB II64 o1 o2 o3 -> op3 (text "\tamcas_db.d") o1 o2 o3
-- LL.{W/D}, SC.{W/D}
+ LL II32 o1 o2 o3 -> op3 (text "\tll.w") o1 o2 o3
+ SC II32 o1 o2 o3 -> op3 (text "\tsc.w") o1 o2 o3
+ LL II64 o1 o2 o3 -> op3 (text "\tll.d") o1 o2 o3
+ SC II64 o1 o2 o3 -> op3 (text "\tsc.d") o1 o2 o3
-- SC.Q
-- LL.ACQ.{W/D}, SC.REL.{W/D}
-- 8. Barrier Instructions ---------------------------------------------------
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/5ae43275ae8fa1a6938f2efcc774c36…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/5ae43275ae8fa1a6938f2efcc774c36…
You're receiving this email because of your account on gitlab.haskell.org.
1
0