Skip to main content

IOG GHC Update #3 (2023-02-09)

ยท 6 min read

Biweekly update from the GHC DevX team at IOG.

Previous updates can be found here.

JavaScript backendโ€‹

Template Haskellโ€‹

Luite: fixed the support for one-shot mode (GHC's -c command-line flag) in the TH JS linker.

Luite: Investigated a warning about the temporary directory not being removed after running Template Haskell with the JavaScript backend. It turned out that GHC's GHC.Utils.TmpFs.newTempDir, which is used by the Template Haskell linker, does not allow the newly created directory to be removed (see #22952).

Sylvain: cleaned up the merge request, removing unnecessary changes and adding documentation.

JavaScript backend CIโ€‹

Jeff: JavaScript backend CI was finally merged! Now that we have CI we are unblocked on several fronts, such as, implementing faster arithmetic and fixing some async exceptions. In general, we can now have confidence that our work is progressing the JavaScript backend to a better state.

Sylvain: fixed a spurious failure on JS CI due to some test passing on fast runners while it was expected to fail (see !9934).

Josh: fixed some inaccurate test predicates !9939


Josh: rebased and merged !9755. This patch changes the representation of the JavaScript equivalent of the C struct stat to make its field offsets match the C ones: some Haskell codes directly access fields of this structure using hsc2hs to get the field offsets from the C headers.

This patch also adds fields to the JavaScript file stat that were previously not included, such as modification and access times.

JavaScript RTS refactorโ€‹

Josh: rebased !9794 which consists in the refactor of the module generating some part of the RTS. The new JS CI job found a bug in the patch that caused ~50 tests to time out, so waiting for CI to be set up before merging this MR was judicious.

This MR also became an opportunity to revisit some arbitrary cache sizes in the RTS code generator. This is still ongoing work.


Luite: We accidently removed the check for the "javascript" calling convention on foreign imports, allowing this convention to be wrongly used on native platform. Fixed in !9880.

Fix for asynchronous exceptionsโ€‹

Luite: Fixed an issue in the garbage collector for the JavaScript backend: A thread that posts an asynchronous exception (throwTo) to another thread is temporarily suspended until the exception has been delivered. The garbage collector did not correctly follow the list of threads suspended in this way, potentially considering them unreachable and cleaning up data referenced by them. See #22836 and !9879.

Integer performanceโ€‹

Evaluating the following expression is very slow in general but especially with the JS backend:

1 `shiftL` (1 `shiftL` 20) :: Integer

We've had to mark a test computing this as broken on CI because it triggers a timeout error. Luckily the identification of slow operations is easy with JavaScript profiling tools (see graphs in and we know that Word32 primops are the culprit in this case.

Sylvain started replacing the uses of JavaScript's BigInt in the implementation of these primops with usual JavaScript numbers. See !9825.

JavaScript EDSLโ€‹

Jeff: JavaScript eDSL based on sunroof close to MR, see #22736 for background. Compiler is complete. Major items left are: the interpreter to translate to JStat, filling in documentation, and testing now that CI has been merged.


Jeff: Wrote the JavaScript backend release notes. Notes pending approval. We cite the JavaScript backend wiki page in the release notes. So Jeff and Sylvain heavily edited the wiki pages to make them suitable for external customers.

Change from js to javascript architectureโ€‹

In #22740 it was noticed that hackage-server would prevent the upload of the upcoming base package bundled with GHC 9.6. The reason is that hackage-server relies on the cabal --check feature which filters out perfectly valid packages (it happened before, for example with ghc-api-compat).

In our case, the package was rejected because the js architecture wasn't recognized as a built-in one, but luckily we could fall back to the existing javascript built-in architecture defined for GHCJS (if it wasn't a JS backend, we would have had to fix Cabal, update hackage-server dependencies, and redeploy hackage-server...).

Sylvain completed Ben's MR in !9814.

For early users of the JS backend the change means that from now on:

  • configure command is: emconfigure ./configure --target=javascript-unknown-ghcjs
  • Cabal condition is: arch(javascript)
  • CPP condition is: #if defined(javascript_HOST_ARCH)

Compiler performanceโ€‹

More-strict breakโ€‹

Josh: opened merge request !9868 to add a stricter break' version to base - however, this would require a CLC proposal.

Further analysis was done using ticky profiles on a simple test program that benchmarks GHC's startup code. This has found an example where a more-strict break is an improvement in GHC, which will provide motivation for the CLC proposal.

Unboxed CodeBuffersโ€‹

Josh: implemented changes to GHC's text encoding buffers to use unboxed tuples on handle encoders/decoders. The buffers pass around and repeatedly pack/unpack a tuple in an IO inner loop, which causes a significant number of unnecessary allocations. By replacing this with an unboxed tuple, and replacing the IO with manually passing around a State# RealWorld in the same tuple, we're able to reduce allocations by nearly 50% in a pathological example (non-allocating loop printing characters). See #22946 and !9948.

Optimization Handbookโ€‹

Jeff: opened IT ticket to move the Optimization Handbook to Haskell Foundation's repository. IT stated they need to check with legal, of course.

Jeff: almost finished the lambda lifting chapter; major remaining items are adding some glossary terms and describing the interaction between lambda lifting and calling conventions.

Constant folding for division operationsโ€‹

While looking into his old merge requests still opened, Sylvain nerd-snipped himself into fixing constant folding rules for division operations (see #22152).

MR !8956 adds the following rewrite rules:

   case quotRemInt# x y of
(# q, _ #) -> body
case quotInt# x y of
q -> body

case quotRemInt# x y of
(# _, r #) -> body
case remInt# x y of
r -> body

For all primitive numerical types:

(x `quot` l1) `quot` l2
| l1 /= 0
| l2 /= 0
| l1*l2 doesn't overflow/underflow
x `quot` (l1 * l2)

It also makes some division primops (Word64/Int64 Quot/Rem, WordQuotRem2Op) ok-for-speculation when the divisor is known to be non-zero, similarly to other division primops. Otherwise the last rule wasn't firing in the added test because we got the following Core (simplified for the presentation):

case quotWord64# x# 10#Word64 of
ds1 -> case quotWord64# ds1 20#Word64 of
ds2 -> ...

and not:

case quotWord64# (quotWord64# x# 10#Word64) 20#Word64 of
ds2 -> ...


Luite: A test run of GHC 9.6 with the -fbyte-code-and-object-code flag on head.hackage revealed issue #22888 with bytecode size limits. Many bytecode instructions have Word16 operands, which is not always enough to run programs generated from optimized core. The solution is to enable large operands for all the bytecode instructions that deal with stack offsets. See #22888 and !9957.