Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

init: empty list #371

Closed
bgamari opened this issue Oct 7, 2018 · 4 comments
Closed

init: empty list #371

bgamari opened this issue Oct 7, 2018 · 4 comments
Assignees
Labels
bug patch-provided Patch provided in comments: needs review, merge, etc.

Comments

@bgamari
Copy link
Contributor

bgamari commented Oct 7, 2018

The following program causes the compiler to fail with an "empty list" error from Data.List.init:

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE UndecidableInstances #-}

module Test where

import Clash.Sized.RTree as RTree
import Clash.Prelude
import Data.Functor.Compose
import Data.Proxy
import Data.Singletons.Prelude (type (@@), TyFun, Apply)
import Data.Singletons.TypeLits (withKnownNat)
import Data.Kind (Constraint, Type)
import Debug.Trace

{-# ANN test
    ((defSyn "test")
             { t_name = "test"
             , t_inputs = [ PortName "clock"
                          , PortName "reset"
                          , PortName "x"
                          , PortName "y"
                          ]
             , t_output = PortName "out"
             })
 #-}

test :: Clock System Source
     -> Reset System Synchronous
     -> Signal System (Unsigned N)
     -> Signal System (Unsigned N)
     -> Signal System (Unsigned N)
test clk reset x y = withClockReset clk reset $ fmap fromIntegral $ mul' @4 @_ @4 x y

main = do
    let ns = (,) <$> [100,200,300,400] <*> [0..10]
    print $ fmap (uncurry (*)) ns
    print $ simulate (uncurry (mul' @4 @16 @24) . unbundle) ns

mul' :: forall chunkSz n m nChunks dom gated synth.
        (KnownNat chunkSz, KnownNat n, KnownNat m, KnownNat nChunks,
         HiddenClockReset dom gated synth,
         (2^m) ~ (chunkSz*nChunks), 1 <= nChunks, Obvious nChunks)
     => Signal dom (Unsigned (2^n))
     -> Signal dom (Unsigned (2^m))
     -> Signal dom (Unsigned (2^(n+m+1)))
mul' x y =
    fmap fromIntegral $ delayBy (SNat @1)
    $ sumTree
    $ sequenceA $ fmap (vecToRTree 0)
    $ delayBy (SNat @2) res'
  where
    chunks :: Unsigned (2^m) -> Vec nChunks (Unsigned chunkSz)
    chunks n = bitChunks n

    chunkSz = fromIntegral $ natVal $ Proxy @chunkSz

    partial :: Unsigned (2^n) -> Index nChunks -> Unsigned chunkSz -> Unsigned (2^(n+chunkSz))
    partial y i x = fromIntegral (y `mul` x) `shiftL` (chunkSz * fromIntegral i)

    res :: Signal dom (Vec nChunks (Unsigned (2^(n+chunkSz))))
    res = zipWith3 partial
          <$> fmap pure x
          <*> pure indicesI
          <*> fmap chunks y

    res' :: Signal dom (Vec nChunks (Unsigned (2^(n+m+1))))
    res' = fmap (fmap fromIntegral) res

-- | Latency of @d@.
sumTree :: forall d n dom synth gated.
           (KnownNat d, KnownNat n, HiddenClockReset dom synth gated)
        => RTree d (Signal dom (Unsigned n))
        -> Signal dom (Unsigned (n+d))
sumTree rtree =
    tdfold (Proxy @(IAccum dom n)) id f rtree
  where
    f :: forall l. ()
      => SNat l
      -> IAccum dom n @@ l -> IAccum dom n @@ l -> IAccum dom n @@ (l+1)
    f lvl@SNat l r = add <$> delayBy lvl l <*> delayBy lvl r

data IAccum (dom :: Domain) (n :: Nat) (f :: TyFun Nat Type) :: Type
type instance Apply (IAccum dom n) l = Signal dom (Unsigned (n+l))

vecToRTree :: forall n a. (KnownNat n, 1 <= n, Obvious n)
           => a -> Vec n a -> RTree (CLog 2 n) a
vecToRTree z = v2t . pad
  where
    pad :: Vec n a -> Vec (2^(CLog 2 n)) a
    pad x = x ++ (repeat z :: Vec (2^(CLog 2 n) - n) a)

type Obvious n = ((n <= 2^(CLog 2 n)) :: Constraint)

bitChunks :: forall m n. (KnownNat m, KnownNat n)
          => Unsigned (m*n) -> Vec m (Unsigned n)
bitChunks x =
    reverse $ fmap (\i -> fromIntegral $ x `shiftR` (fromIntegral i*n)) indicesI
  where
    n = fromIntegral $ natVal $ Proxy @n

delayBy :: forall n dom a gated. (Undefined a, HiddenClock dom gated)
        => SNat n -> Signal dom a -> Signal dom a
delayBy n = foldr (.) id (replicate n delay)

naive :: HiddenClockReset dom gated synth
      => Signal dom (Unsigned 16)
      -> Signal dom (Unsigned 16)
      -> Signal dom (Unsigned 16)
naive x y = delayBy (SNat @16) $ x*y

type N = 16

The error appears to come from

*** Exception (reporting due to +RTS -xc): (THUNK_STATIC), stack trace: 
  Clash.Main.main'.\,
  called from Clash.Main.handleClashException,
  called from Clash.Util.traceIf,
  called from Clash.Normalize.PrimitiveReductions.reduceFoldr.go,
  called from Clash.Normalize.PrimitiveReductions.reduceFoldr,
  called from Clash.Rewrite.Types.>>=.\,
  called from Clash.Rewrite.Types.>>=,
  called from Clash.Driver.normalizeEntity.doNorm,
  called from Clash.Rewrite.Util.runRewriteSession.(...),
  called from Clash.Rewrite.Util.runRewriteSession,
  called from Clash.Normalize.runNormalization,
  called from Clash.Driver.normalizeEntity.transformedBindings,
  called from Clash.Driver.normalizeEntity,
  called from Clash.Driver.generateHDL.go.transformedBindings,
  called from Clash.Driver.generateHDL.go,
  called from Clash.Driver.generateHDL,
  called from Clash.GHCi.UI.makeHDL.\,
  called from Clash.GHCi.UI.makeHDL,
  called from Clash.Main.makeHDL',
  called from Clash.Main.makeVerilog,
  called from Clash.Main.main'.clash,
  called from Clash.Main.main',
  called from Clash.Main.defaultMain,
  called from Main.main

I added a bit of tracing to the call site with the following patch,

diff --git a/clash-lib/src/Clash/Normalize/PrimitiveReductions.hs b/clash-lib/src/Clash/Normalize/PrimitiveReductions.hs
index 206f9482..ed694e9f 100644
--- a/clash-lib/src/Clash/Normalize/PrimitiveReductions.hs
+++ b/clash-lib/src/Clash/Normalize/PrimitiveReductions.hs
@@ -303,6 +310,14 @@ reduceFoldr n aTy fun start arg = do
   where
     go tcm (coreView tcm -> Just ty') = go tcm ty'
     go tcm (tyView -> TyConApp vecTcNm _)
+      | (Just vecTc) <- HashMap.lookup (nameOcc vecTcNm) tcm
+      , [_,consCon] <- tyConDataCons vecTc
+      , let (vars,elems)     = second concat . unzip
+                             $ extractElems consCon aTy 'G' n arg
+      , [] <- elems
+      = error $ "reduceFoldr: n="++show n++", ty="++showDoc aTy++", fun="++
+        showDoc fun++", start="++showDoc start++", arg="++showDoc arg
+
       | (Just vecTc) <- HashMap.lookup (nameOcc vecTcNm) tcm
       , [_,consCon] <- tyConDataCons vecTc
       = let (vars,elems)     = second concat . unzip

Which yielded the following,

<no location info>: error:
    Clash error call:
    reduceFoldr: n=0, ty=Clash.Signal.Internal.Signal
  (Clash.Signal.Internal.Dom system 10000)
  (Clash.Sized.Internal.Unsigned.Unsigned 512)
-> Clash.Signal.Internal.Signal
     (Clash.Signal.Internal.Dom system 10000)
     (Clash.Sized.Internal.Unsigned.Unsigned 512), fun=GHC.Base..8214565720323787974
  @(Clash.Signal.Internal.Signal
      (Clash.Signal.Internal.Dom system 10000)
      (Clash.Sized.Internal.Unsigned.Unsigned 512))
  @(Clash.Signal.Internal.Signal
      (Clash.Signal.Internal.Dom system 10000)
      (Clash.Sized.Internal.Unsigned.Unsigned 512))
  @(Clash.Signal.Internal.Signal
      (Clash.Signal.Internal.Dom system 10000)
      (Clash.Sized.Internal.Unsigned.Unsigned 512)), start=GHC.Base.id8214565720323787481
  @(Clash.Signal.Internal.Signal
      (Clash.Signal.Internal.Dom system 10000)
      (Clash.Sized.Internal.Unsigned.Unsigned 512)), arg=Clash.Sized.Vector.replicate @0
  @(Clash.Signal.Internal.Signal
      (Clash.Signal.Internal.Dom system 10000)
      (Clash.Sized.Internal.Unsigned.Unsigned 512)
  -> Clash.Signal.Internal.Signal
       (Clash.Signal.Internal.Dom system 10000)
       (Clash.Sized.Internal.Unsigned.Unsigned 512))
  (Clash.Promoted.Nat.SNat @0
     0)
  (?(i :: Clash.Signal.Internal.Signal
            (Clash.Signal.Internal.Dom system 10000)
            (Clash.Sized.Internal.Unsigned.Unsigned 512)) ->
  Clash.Signal.Internal.delay#
    @(Clash.Sized.Internal.Unsigned.Unsigned 512)
    @(Clash.Signal.Internal.Dom system 10000)
    @Clash.Signal.Internal.Source
    GHC.Stack.Types.EmptyCallStack
    (?($dIP :: GHC.Classes.IP
                 callStack
                 GHC.Stack.Types.CallStack) ->
    ?(eta :: GHC.Types.[] GHC.Types.Char) ->
    case $dIP of
      _ ->
        Clash.Sized.Internal.Unsigned.$fUndefinedUnsigned18214565720323812782
          @512
          $dIP
          eta
      GHC.Stack.Types.FreezeCallStack 
        (ds1 :: GHC.Stack.Types.CallStack) ->
        Clash.XException.errorX
          @(Clash.Sized.Internal.Unsigned.Unsigned 512)
          $dIP
          eta)
    $dIP41933
    i)
@bgamari
Copy link
Contributor Author

bgamari commented Oct 7, 2018

I believe the code being transformed when the error occurs is in delayBy.

@bgamari
Copy link
Contributor Author

bgamari commented Oct 7, 2018

I suspect the problem here is that reduceFoldr doesn't handle the case where the vector length is 0. I suspect adding an equation of the form,

reduceFoldr 0 _ _ start _ = changed start

to its definition will resolve the issue (although there may be a better way).

@martijnbastiaan
Copy link
Member

I think you're right. I'll have a look this afternoon.

@martijnbastiaan martijnbastiaan self-assigned this Oct 8, 2018
@martijnbastiaan
Copy link
Member

Minimal example triggering bug:

module Test4 where

import Clash.Explicit.Prelude

topEntity
  :: Clock System Source
  -> Reset System Synchronous
  -> Signal System ()
topEntity clk rst =
  delayBy clk rst (SNat @0) (pure ())

delayBy clk rst n =
  foldr (.) id (replicate n id)
{-# NOINLINE delayBy #-}

@martijnbastiaan martijnbastiaan added patch-provided Patch provided in comments: needs review, merge, etc. bug labels Oct 8, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug patch-provided Patch provided in comments: needs review, merge, etc.
Projects
None yet
Development

No branches or pull requests

2 participants