[GHC] #14430: lintUnfolding does not allow unfoldings to jump to enclosing join points
#14430: lintUnfolding does not allow unfoldings to jump to enclosing join points -------------------------------------+------------------------------------- Reporter: nomeata | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 8.3 Keywords: | Operating System: Unknown/Multiple Architecture: | Type of failure: None/Unknown Unknown/Multiple | Test Case: | Blocked By: Blocking: | Related Tickets: Differential Rev(s): | Wiki Page: -------------------------------------+------------------------------------- I am not sure why this does not show up in HEAD but it does show up in my loopification branch. It does look like a bug in GHC to me. With loopification, I get the following code in the interface of `Foreign.C.String`: {{{ d5fd65f7834390bebd0e80edc7b8f627 withCAStringLen1 :: String -> (CStringLen -> IO a) -> State# RealWorld -> (# State# RealWorld, a #) {- Arity: 3, HasNoCafRefs, Strictness: <S,U><C(C(S(SL))),C(C1(U(U,U)))><S,U>, Unfolding: (\ @ a (str :: String) (f :: CStringLen -> IO a) (eta :: State# RealWorld) -> case lenAcc @ Char str newCAString2 of wild { I# x -> case newAlignedPinnedByteArray# @ RealWorld x 1# eta of ds2 { (#,#) ipv ipv1 -> case unsafeFreezeByteArray# @ RealWorld ipv1 ipv of ds3 { (#,#) ipv2 ipv3 -> let { ptr :: Addr# = byteArrayContents# ipv3 } in let { $j :: State# RealWorld -> () -> (# State# RealWorld, a #) <join 2> {- Arity: 2, Strictness: <S,U><L,A>, Inline: [0], Unfolding: InlineRule (2, True, False) (\ (w :: State# RealWorld) (w1 :: ()) -> case (f (Ptr @ CChar ptr, wild)) `cast` (N:IO[0] <a>_R) w of ds4 { (#,#) ipv4 ipv5 -> case touch# @ 'UnliftedRep @ ByteArray# ipv3 ipv4 of s4 { DEFAULT -> (# s4, ipv5 #) } }) -} = \ (w :: State# RealWorld)[OneShot] (w1 :: ())[OneShot] -> case (f (Ptr @ CChar ptr, wild)) `cast` (N:IO[0] <a>_R) w of ds4 { (#,#) ipv4 ipv5 -> case touch# @ 'UnliftedRep @ ByteArray# ipv3 ipv4 of s4 { DEFAULT -> (# s4, ipv5 #) } } } in let { go :: [Char] -> Int -> State# RealWorld -> (# State# RealWorld, a #) <join 3> {- Arity: 3, Strictness: <S,1*U><S(S),1*U(U)><S,U>, Inline: [~], Unfolding: InlineRule (3, True, False) (\ (ds :: [Char])[OneShot] (n :: Int)[OneShot] (eta1 :: State# RealWorld)[OneShot] -> letrec { go :: [Char] -> Int -> State# RealWorld -> (# State# RealWorld, a #) <join 3> {- Arity: 3 -} = \ (ds1 :: [Char]) (n1 :: Int) (eta2 :: State# RealWorld) -> case ds1 of wild1 { [] -> case n1 of n2 { I# ipv4 -> $j eta2 () } : c cs -> case n1 of wild2 { I# i -> case c of wild3 { C# c# -> case writeInt8OffAddr# @ RealWorld ptr i (narrow8Int# (ord# c#)) eta2 of s2 { DEFAULT -> go cs (I# (+# i 1#)) s2 } } } } } in go ds n eta1) -} = \ (ds :: [Char])[OneShot] (n :: Int)[OneShot] (eta1 :: State# RealWorld)[OneShot] -> case n of ww { I# ww1 -> let { exit :: State# RealWorld -> (# State# RealWorld, a #) <join 1> {- Arity: 1, Strictness: <S,U> -} = \ (w :: State# RealWorld)[OneShot] -> case (f (Ptr @ CChar ptr, wild)) `cast` (N:IO[0] <a>_R) w of ds4 { (#,#) ipv4 ipv5 -> case touch# @ 'UnliftedRep @ ByteArray# ipv3 ipv4 of s4 { DEFAULT -> (# s4, ipv5 #) } } } in letrec { $wgo :: [Char] -> Int# -> State# RealWorld -> (# State# RealWorld, a #) <join 3> {- Arity: 3, Strictness: <S,1*U><L,U><S,U>, Inline: [0] -} = \ (w :: [Char]) (ww2 :: Int#) (w1 :: State# RealWorld) -> case w of wild1 { [] -> exit w1 : c cs -> case c of wild2 { C# c# -> case writeInt8OffAddr# @ RealWorld ptr ww2 (narrow8Int# (ord# c#)) w1 of s2 { DEFAULT -> $wgo cs (+# ww2 1#) s2 } } } } in $wgo ds ww1 eta1 } } in go str newCAString2 ipv2 } } }) -} }}} Note how `go` references `j` not only in its RHS, but also in its unfolding. When loading this interface, I get this error: {{{ HC [stage 1] libraries/base/dist-install/build/System/Posix/Internals.o ghc-stage1: panic! (the 'impossible' happened) (GHC version 8.3.20171101 for x86_64-unknown-linux): Iface Lint failure In interface for Foreign.C.String Unfolding of go_a6h7 <no location info>: warning: In the expression: jump $j_a6gX eta2_a6hS () Invalid occurrence of a join variable: $j_a6gX The binder is either not a join point, or not valid here go_a6h7 = \ (ds_a6hM [OS=OneShot] :: [Char]) (n_a6hN [OS=OneShot] :: Int) (eta1_a6hO [OS=OneShot] :: State# RealWorld) -> joinrec { go_a6hP :: [Char] -> Int -> State# RealWorld -> (# State# RealWorld, a_a6gD #) [LclId[JoinId(3)], Arity=3] go_a6hP (ds1_a6hQ :: [Char]) (n1_a6hR :: Int) (eta2_a6hS :: State# RealWorld) = case ds1_a6hQ of wild1_a6hT { [] -> case n1_a6hR of n2_a6hW { I# ipv4_a6hY -> jump $j_a6gX eta2_a6hS () }; : c_a6i1 cs_a6i2 -> case n1_a6hR of wild2_a6i4 { I# i_a6i6 -> case c_a6i1 of wild3_a6i8 { C# c#_a6ia -> case writeInt8OffAddr# @ RealWorld ptr_a6gT i_a6i6 (narrow8Int# (ord# c#_a6ia)) eta2_a6hS of s2_a6ic { __DEFAULT -> jump go_a6hP cs_a6i2 (I# (+# i_a6i6 1#)) s2_a6ic } } } }; } in jump go_a6hP ds_a6hM n_a6hN eta1_a6hO Iface expr = \ (ds :: [Char])[OneShot] (n :: Int)[OneShot] (eta1 :: State# RealWorld)[OneShot] -> letrec { go :: [Char] -> Int -> State# RealWorld -> (# State# RealWorld, a #) <join 3> {- Arity: 3 -} = \ (ds1 :: [Char]) (n1 :: Int) (eta2 :: State# RealWorld) -> case ds1 of wild1 { [] -> case n1 of n2 { I# ipv4 -> $j eta2 () } : c cs -> case n1 of wild2 { I# i -> case c of wild3 { C# c# -> case writeInt8OffAddr# @ RealWorld ptr i (narrow8Int# (ord# c#)) eta2 of s2 { DEFAULT -> go cs (I# (+# i 1#)) s2 } } } } } in go ds n eta1 Call stack: CallStack (from HasCallStack): callStackDoc, called at compiler/utils/Outputable.hs:1147:37 in ghc:Outputable pprPanic, called at compiler/iface/TcIface.hs:1664:33 in ghc:TcIface }}} When reading the code in `tcPragExpr` I see that it lints the unfolding of `go` using `lintUnfolding`, which initializes the `LintM` with an empty `le_joins`. But when linting `go`, the join `j` is valid, even in the unfolding, isn't it? Unless of course I just don’t see the problem with the above unfolding, and am barking up the wrong tree. Anyways, either there is a problem in how we lint the unfoldings, or there is a secret invariant that I violated with my patch -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/14430> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
#14430: lintUnfolding does not allow unfoldings to jump to enclosing join points -------------------------------------+------------------------------------- Reporter: nomeata | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 8.3 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj): The key idea is this: an unfolding is like an extra RHS for the function. So yes, this is a bug. On inspection, it's caused by `lintUnfolding` being called in the wrong place: * We want `lintUnfolding` to be called, once for all, on the unfolding (if any) of each top-level Id; that is, in the `IfaceId` case of `tc_iface_decl`. * But it isn't! Instead `lintUnfolding` is called by `tcPragExpr`, which is called from `tcUnfolding` which is called for each nested unfolding in the interface-file declaration. * This is inefficient, because the top-level call will re-lint the nested unfoldings; and it's wrong for the reason you point out. In the olden days there were no nested unfoldings in interface0-file definitions, which is why this bug got started. Might you fix, Joachim? -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/14430#comment:1> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
#14430: lintUnfolding does not allow unfoldings to jump to enclosing join points -------------------------------------+------------------------------------- Reporter: nomeata | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 8.3 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by nomeata): With these pointers, I can give it a shot. -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/14430#comment:2> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
#14430: lintUnfolding does not allow unfoldings to jump to enclosing join points -------------------------------------+------------------------------------- Reporter: nomeata | Owner: nomeata Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 8.3 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by nomeata): * owner: (none) => nomeata -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/14430#comment:3> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
#14430: lintUnfolding does not allow unfoldings to jump to enclosing join points -------------------------------------+------------------------------------- Reporter: nomeata | Owner: nomeata Type: bug | Status: patch Priority: normal | Milestone: Component: Compiler | Version: 8.3 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Phab:D4169 Wiki Page: | -------------------------------------+------------------------------------- Changes (by nomeata): * status: new => patch * differential: => Phab:D4169 Comment: See https://phabricator.haskell.org/D4169 -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/14430#comment:4> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
#14430: lintUnfolding does not allow unfoldings to jump to enclosing join points -------------------------------------+------------------------------------- Reporter: nomeata | Owner: nomeata Type: bug | Status: patch Priority: normal | Milestone: Component: Compiler | Version: 8.3 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Phab:D4169 Wiki Page: | -------------------------------------+------------------------------------- Comment (by Joachim Breitner <mail@…>): In [changeset:"803ed036704aa5bab8b0f1fee407e58d82c85393/ghc" 803ed036/ghc]: {{{ #!CommitTicketReference repository="ghc" revision="803ed036704aa5bab8b0f1fee407e58d82c85393" Invoke lintUnfolding only on top-level unfoldings (#14430) as nested unfoldings are linted together with the top-level unfolding, and lintUnfolding does the wrong things for nestd unfoldings that mention join points. The easiest way of doing that was to pass a TopLevel flag through `tcUnfolding`, which is invoked both for top level and nested unfoldings. Differential Revision: https://phabricator.haskell.org/D4169 }}} -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/14430#comment:5> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
#14430: lintUnfolding does not allow unfoldings to jump to enclosing join points -------------------------------------+------------------------------------- Reporter: nomeata | Owner: nomeata Type: bug | Status: closed Priority: normal | Milestone: Component: Compiler | Version: 8.3 Resolution: fixed | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Phab:D4169 Wiki Page: | -------------------------------------+------------------------------------- Changes (by nomeata): * status: patch => closed * resolution: => fixed -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/14430#comment:6> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
participants (1)
-
GHC