Bytestring.Builder rules no longer fire with GHC 9.0.1
Summary
With GHC 9 and forwards (I tested 8.10.7, 9.0.1 and 9.2.1 where I initially discovered this) a rewrite rule from Data.ByteString.Builder no longer seems to fire.
I am unsure how to reproduce this with a minimal example, or even how many other rules this affects, as I am not that familiar with rewrite-rules or Data.ByteString.Builder
's internals. I have been trying to implement a custom Builder to learn more about them, but got stuck on this issue and noticed this wasn't working in bytestring
for ghc 9.X too.
Also I hope that this is right place to report this. bytestring
builders rules have not changed all that much for a very long time and that specific rule hasn't either, so I think this is a GHC issue
Steps to reproduce
{-# OPTIONS_GHC -O2 -ddump-rule-firings -ddump-simpl -dsuppress-idinfo -dsuppress-coercions -dsuppress-type-applications -dsuppress-module-prefixes -dsuppress-type-signatures -dsuppress-uniques #-}
module Test (test) where
import qualified Data.ByteString.Builder as B
test :: B.Builder
test = B.word8 16 <> B.int8 32
Expected behavior
From my understanding this is supposed to trigger this rule and thus produce a single buffer size check.
With GHC 8.10.7 this produces the following output:
[1 of 1] Compiling Test ( Test.hs, Test.o )
Rule fired: Class op <> (BUILTIN)
Rule fired: Class op fromInteger (BUILTIN)
Rule fired: integerToWord (BUILTIN)
Rule fired: narrow8Word# (BUILTIN)
Rule fired: Class op fromInteger (BUILTIN)
Rule fired: integerToInt (BUILTIN)
Rule fired: narrow8Int# (BUILTIN)
Rule fired: append/primBounded (Data.ByteString.Builder.Prim)
Rule fired: +# (BUILTIN)
Rule fired: int2Word# (BUILTIN)
Rule fired: narrow8Word# (BUILTIN)
Rule fired: int2Word# (BUILTIN)
Rule fired: narrow8Word# (BUILTIN)
==================== Tidy Core ====================
Result size of Tidy Core
= {terms: 103, types: 88, coercions: 27, joins: 0/2}
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
$trModule4 = "main"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
$trModule3 = TrNameS $trModule4
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
$trModule2 = "Test"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
$trModule1 = TrNameS $trModule2
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$trModule = Module $trModule3 $trModule1
-- RHS size: {terms: 73, types: 38, coercions: 12, joins: 0/2}
$wtest
= \ @ r w ww ww1 w1 ->
case <# (minusAddr# ww1 ww) 2# of {
__DEFAULT ->
case writeWord8OffAddr# ww 0# 16## w1 of s2 { __DEFAULT ->
let { ipv1 = plusAddr# ww 1# } in
case writeWord8OffAddr# ipv1 0# 32## s2 of s1 { __DEFAULT ->
((w (BufferRange (plusAddr# ipv1 1#) ww1)) `cast` <Co:3>) s1
}
};
1# ->
(# w1,
BufferFull
2#
ww
((\ ds eta ->
case ds of { BufferRange dt dt1 ->
case writeWord8OffAddr# dt 0# 16## eta of s2 { __DEFAULT ->
let { ipv1 = plusAddr# dt 1# } in
case writeWord8OffAddr# ipv1 0# 32## s2 of s1 { __DEFAULT ->
((w (BufferRange (plusAddr# ipv1 1#) dt1)) `cast` <Co:3>) s1
}
}
})
`cast` <Co:6>) #)
}
-- RHS size: {terms: 12, types: 11, coercions: 0, joins: 0/0}
test1
= \ @ r w w1 w2 ->
case w1 of { BufferRange ww1 ww2 -> $wtest w ww1 ww2 w2 }
-- RHS size: {terms: 1, types: 0, coercions: 15, joins: 0/0}
test = test1 `cast` <Co:15>
With GHC 9.0.1 it instead produces this:
[1 of 1] Compiling Test ( Test.hs, Test.o )
Rule fired: Class op <> (BUILTIN)
Rule fired: Class op fromInteger (BUILTIN)
Rule fired: Integer -> Word# (wrap) (BUILTIN)
Rule fired: narrow8Word# (BUILTIN)
Rule fired: Class op fromInteger (BUILTIN)
Rule fired: Integer -> Int# (wrap) (BUILTIN)
Rule fired: narrow8Int# (BUILTIN)
Rule fired: int2Word# (BUILTIN)
Rule fired: narrow8Word# (BUILTIN)
Rule fired: int2Word# (BUILTIN)
Rule fired: narrow8Word# (BUILTIN)
==================== Tidy Core ====================
Result size of Tidy Core
= {terms: 151, types: 129, coercions: 91, joins: 0/3}
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
$trModule4 = "main"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
$trModule3 = TrNameS $trModule4
-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
$trModule2 = "Test"#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
$trModule1 = TrNameS $trModule2
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
$trModule = Module $trModule3 $trModule1
-- RHS size: {terms: 121, types: 79, coercions: 69, joins: 0/3}
$wtest
= \ @r w ww ww1 w1 ->
let {
lvl
= \ ds eta ->
case ds of { BufferRange dt3 dt4 ->
case writeWord8OffAddr# dt3 0# 32## eta of s2 { __DEFAULT ->
((w (BufferRange (plusAddr# dt3 1#) dt4)) `cast` <Co:16>) s2
}
} } in
case <# (minusAddr# ww1 ww) 1# of {
__DEFAULT ->
case writeWord8OffAddr# ww 0# 16## w1 of s2 { __DEFAULT ->
let { dt = plusAddr# ww 1# } in
case <# (minusAddr# ww1 dt) 1# of {
__DEFAULT ->
case writeWord8OffAddr# dt 0# 32## s2 of s1 { __DEFAULT ->
((w (BufferRange (plusAddr# dt 1#) ww1)) `cast` <Co:16>) s1
};
1# -> (# s2, BufferFull 1# dt (lvl `cast` <Co:7>) #)
}
};
1# ->
(# w1,
BufferFull
1#
ww
((\ ds eta ->
case ds of { BufferRange dt3 dt4 ->
case writeWord8OffAddr# dt3 0# 16## eta of s2 { __DEFAULT ->
let { dt = plusAddr# dt3 1# } in
case <# (minusAddr# dt4 dt) 1# of {
__DEFAULT ->
case writeWord8OffAddr# dt 0# 32## s2 of s1 { __DEFAULT ->
((w (BufferRange (plusAddr# dt 1#) dt4)) `cast` <Co:16>) s1
};
1# -> (# s2, BufferFull 1# dt (lvl `cast` <Co:7>) #)
}
}
})
`cast` <Co:7>) #)
}
-- RHS size: {terms: 12, types: 11, coercions: 0, joins: 0/0}
test1
= \ @r w w1 w2 ->
case w1 of { BufferRange ww1 ww2 -> $wtest w ww1 ww2 w2 }
-- RHS size: {terms: 1, types: 0, coercions: 22, joins: 0/0}
test = test1 `cast` <Co:22>
GHC 9.2.1 is similar, the append/primBounded (Data.ByteString.Builder.Prim)
also does not fire.
Environment
- GHC version used: 9.0.1, 9.2.1