clash-lib 1.0.1 → 1.2.0
raw patch · 103 files changed
+9557/−5161 lines, 103 filesdep +data-defaultdep +dlistdep +extradep ~clash-preludedep ~ghcdep ~lensPVP ok
version bump matches the API change (PVP)
Dependencies added: data-default, dlist, extra, ghc-boot-th, haskell-src-meta, ordered-containers
Dependency ranges changed: clash-prelude, ghc, lens
API changes (from Hackage documentation)
- Clash.Core.Evaluator: Apply :: Id -> StackFrame
- Clash.Core.Evaluator: DC :: DataCon -> [Either Term Type] -> Value
- Clash.Core.Evaluator: GPureHeap :: PureHeap -> GPureHeap
- Clash.Core.Evaluator: GUpdate :: Id -> StackFrame
- Clash.Core.Evaluator: Heap :: GlobalHeap -> GPureHeap -> PureHeap -> Supply -> InScopeSet -> Heap
- Clash.Core.Evaluator: Instantiate :: Type -> StackFrame
- Clash.Core.Evaluator: Lambda :: Id -> Term -> Value
- Clash.Core.Evaluator: Lit :: Literal -> Value
- Clash.Core.Evaluator: PrimApply :: Text -> PrimInfo -> [Type] -> [Value] -> [Term] -> StackFrame
- Clash.Core.Evaluator: PrimVal :: Text -> PrimInfo -> [Type] -> [Value] -> Value
- Clash.Core.Evaluator: Scrutinise :: Type -> [Alt] -> StackFrame
- Clash.Core.Evaluator: Suspend :: Term -> Value
- Clash.Core.Evaluator: Tickish :: TickInfo -> StackFrame
- Clash.Core.Evaluator: TyLambda :: TyVar -> Term -> Value
- Clash.Core.Evaluator: Update :: Id -> StackFrame
- Clash.Core.Evaluator: [unGPureHeap] :: GPureHeap -> PureHeap
- Clash.Core.Evaluator: data Heap
- Clash.Core.Evaluator: data StackFrame
- Clash.Core.Evaluator: data Value
- Clash.Core.Evaluator: force :: Heap -> Stack -> Id -> Maybe State
- Clash.Core.Evaluator: gupdate :: Heap -> Stack -> Id -> Value -> State
- Clash.Core.Evaluator: instance Clash.Pretty.ClashPretty Clash.Core.Evaluator.StackFrame
- Clash.Core.Evaluator: instance GHC.Show.Show Clash.Core.Evaluator.StackFrame
- Clash.Core.Evaluator: instance GHC.Show.Show Clash.Core.Evaluator.Value
- Clash.Core.Evaluator: integerLiteral :: Value -> Maybe Integer
- Clash.Core.Evaluator: isScrut :: Stack -> Bool
- Clash.Core.Evaluator: mkAbstr :: (Heap, Term) -> [Either TyVar Type] -> (Heap, Term)
- Clash.Core.Evaluator: mkTickish :: Stack -> [TickInfo] -> Stack
- Clash.Core.Evaluator: naturalLiteral :: Value -> Maybe Integer
- Clash.Core.Evaluator: newLetBindings' :: TyConMap -> Heap -> [Either Term Type] -> (Heap, [Either Term Type])
- Clash.Core.Evaluator: newtype GPureHeap
- Clash.Core.Evaluator: primop :: PrimEvaluator -> TyConMap -> Heap -> Stack -> Text -> PrimInfo -> [Type] -> [Value] -> Value -> [Term] -> Maybe State
- Clash.Core.Evaluator: substAlt :: DataCon -> [TyVar] -> [Id] -> [Either Term Type] -> Term -> Term
- Clash.Core.Evaluator: toType :: TyVar -> Type
- Clash.Core.Evaluator: toVar :: Id -> Term
- Clash.Core.Evaluator: type GlobalHeap = (IntMap Term, Int)
- Clash.Core.Evaluator: type PrimEvaluator = Bool -> TyConMap -> Heap -> Stack -> Text -> PrimInfo -> [Type] -> [Value] -> Maybe State
- Clash.Core.Evaluator: type PureHeap = VarEnv Term
- Clash.Core.Evaluator: type Stack = [StackFrame]
- Clash.Core.Evaluator: type State = (Heap, Stack, Term)
- Clash.Core.Evaluator: uniqueInHeap :: PureHeap -> Supply -> Id -> (Supply, Id)
- Clash.Core.Evaluator: valToTerm :: Value -> Term
- Clash.Core.Evaluator: wrapSigned :: Integer -> Integer -> Integer
- Clash.Core.Evaluator: wrapUnsigned :: Integer -> Integer -> Integer
- Clash.Core.FreeVars: termFreeVarsX :: Fold Term (Var a)
- Clash.Core.Util: availableUniques :: Term -> [Unique]
- Clash.Core.Util: tySym :: TyConMap -> Type -> Except String String
- Clash.Netlist: isWriteToBiSignalPrimitive :: Term -> Bool
- Clash.Netlist.BlackBox.Types: Var :: [Element] -> !Int -> Element
- Clash.Netlist.BlackBox.Util: stripVoid :: HWType -> HWType
- Clash.Netlist.BlackBox.Util: usedArguments :: BlackBox -> [Int]
- Clash.Netlist.Util: fixCustomRepr :: CustomReprs -> Type -> HWType -> HWType
- Clash.Netlist.Util: isBiSignalOut :: HWType -> Bool
- Clash.Netlist.Util: packSP :: CustomReprs -> (Text, c) -> (ConstrRepr', Text, c)
- Clash.Netlist.Util: packSum :: CustomReprs -> Text -> (ConstrRepr', Text)
- Clash.Normalize.Transformations: appProp :: HasCallStack => NormRewrite
- Clash.Rewrite.Util: isWorkFreeClockOrReset :: TyConMap -> Term -> Maybe Bool
+ Clash.Backend: clashVer :: String
+ Clash.Backend: primsRoot :: IO FilePath
+ Clash.Backend.Verilog: noEmptyInit :: (Monad m, Semigroup (m Doc)) => m Doc -> m Doc
+ Clash.Core.Evaluator: newBinder :: [Either TyVar Type] -> Term -> Step
+ Clash.Core.Evaluator: stepApp :: Term -> Term -> Step
+ Clash.Core.Evaluator: stepCase :: Term -> Type -> [Alt] -> Step
+ Clash.Core.Evaluator: stepCast :: Term -> Type -> Type -> Step
+ Clash.Core.Evaluator: stepData :: DataCon -> Step
+ Clash.Core.Evaluator: stepLam :: Id -> Term -> Step
+ Clash.Core.Evaluator: stepLetRec :: [LetBinding] -> Term -> Step
+ Clash.Core.Evaluator: stepLiteral :: Literal -> Step
+ Clash.Core.Evaluator: stepPrim :: PrimInfo -> Step
+ Clash.Core.Evaluator: stepTick :: TickInfo -> Term -> Step
+ Clash.Core.Evaluator: stepTyApp :: Term -> Type -> Step
+ Clash.Core.Evaluator: stepTyLam :: TyVar -> Term -> Step
+ Clash.Core.Evaluator: stepVar :: Id -> Step
+ Clash.Core.Evaluator: substInAlt :: DataCon -> [TyVar] -> [Id] -> [Either Term Type] -> Term -> Term
+ Clash.Core.Evaluator: type Step = Machine -> TyConMap -> Maybe Machine
+ Clash.Core.Evaluator.Types: Apply :: Id -> StackFrame
+ Clash.Core.Evaluator.Types: CastValue :: Value -> Type -> Type -> Value
+ Clash.Core.Evaluator.Types: DC :: DataCon -> [Either Term Type] -> Value
+ Clash.Core.Evaluator.Types: Instantiate :: Type -> StackFrame
+ Clash.Core.Evaluator.Types: Lambda :: Id -> Term -> Value
+ Clash.Core.Evaluator.Types: Lit :: Literal -> Value
+ Clash.Core.Evaluator.Types: Machine :: PrimStep -> PrimUnwind -> PrimHeap -> PureHeap -> PureHeap -> Stack -> Supply -> InScopeSet -> Term -> Machine
+ Clash.Core.Evaluator.Types: PrimApply :: PrimInfo -> [Type] -> [Value] -> [Term] -> StackFrame
+ Clash.Core.Evaluator.Types: PrimVal :: PrimInfo -> [Type] -> [Value] -> Value
+ Clash.Core.Evaluator.Types: Scrutinise :: Type -> [Alt] -> StackFrame
+ Clash.Core.Evaluator.Types: Suspend :: Term -> Value
+ Clash.Core.Evaluator.Types: TickValue :: TickInfo -> Value -> Value
+ Clash.Core.Evaluator.Types: Tickish :: TickInfo -> StackFrame
+ Clash.Core.Evaluator.Types: TyLambda :: TyVar -> Term -> Value
+ Clash.Core.Evaluator.Types: Update :: IdScope -> Id -> StackFrame
+ Clash.Core.Evaluator.Types: [mHeapGlobal] :: Machine -> PureHeap
+ Clash.Core.Evaluator.Types: [mHeapLocal] :: Machine -> PureHeap
+ Clash.Core.Evaluator.Types: [mHeapPrim] :: Machine -> PrimHeap
+ Clash.Core.Evaluator.Types: [mPrimStep] :: Machine -> PrimStep
+ Clash.Core.Evaluator.Types: [mPrimUnwind] :: Machine -> PrimUnwind
+ Clash.Core.Evaluator.Types: [mScopeNames] :: Machine -> InScopeSet
+ Clash.Core.Evaluator.Types: [mStack] :: Machine -> Stack
+ Clash.Core.Evaluator.Types: [mSupply] :: Machine -> Supply
+ Clash.Core.Evaluator.Types: [mTerm] :: Machine -> Term
+ Clash.Core.Evaluator.Types: collectValueTicks :: Value -> (Value, [TickInfo])
+ Clash.Core.Evaluator.Types: data Machine
+ Clash.Core.Evaluator.Types: data StackFrame
+ Clash.Core.Evaluator.Types: data Value
+ Clash.Core.Evaluator.Types: forcePrims :: Machine -> Bool
+ Clash.Core.Evaluator.Types: getTerm :: Machine -> Term
+ Clash.Core.Evaluator.Types: heapContains :: IdScope -> Id -> Machine -> Bool
+ Clash.Core.Evaluator.Types: heapDelete :: IdScope -> Id -> Machine -> Machine
+ Clash.Core.Evaluator.Types: heapInsert :: IdScope -> Id -> Term -> Machine -> Machine
+ Clash.Core.Evaluator.Types: heapLookup :: IdScope -> Id -> Machine -> Maybe Term
+ Clash.Core.Evaluator.Types: instance Clash.Pretty.ClashPretty Clash.Core.Evaluator.Types.StackFrame
+ Clash.Core.Evaluator.Types: instance GHC.Show.Show Clash.Core.Evaluator.Types.Machine
+ Clash.Core.Evaluator.Types: instance GHC.Show.Show Clash.Core.Evaluator.Types.StackFrame
+ Clash.Core.Evaluator.Types: instance GHC.Show.Show Clash.Core.Evaluator.Types.Value
+ Clash.Core.Evaluator.Types: primCount :: Machine -> Int
+ Clash.Core.Evaluator.Types: primInsert :: Int -> Term -> Machine -> Machine
+ Clash.Core.Evaluator.Types: primLookup :: Int -> Machine -> Maybe Term
+ Clash.Core.Evaluator.Types: primUpdate :: Int -> Term -> Machine -> Machine
+ Clash.Core.Evaluator.Types: setTerm :: Term -> Machine -> Machine
+ Clash.Core.Evaluator.Types: stackClear :: Machine -> Machine
+ Clash.Core.Evaluator.Types: stackNull :: Machine -> Bool
+ Clash.Core.Evaluator.Types: stackPop :: Machine -> Maybe (Machine, StackFrame)
+ Clash.Core.Evaluator.Types: stackPush :: StackFrame -> Machine -> Machine
+ Clash.Core.Evaluator.Types: type PrimEvaluator = (PrimStep, PrimUnwind)
+ Clash.Core.Evaluator.Types: type PrimHeap = (IntMap Term, Int)
+ Clash.Core.Evaluator.Types: type PrimStep = TyConMap -> Bool -> PrimInfo -> [Type] -> [Value] -> Machine -> Maybe Machine
+ Clash.Core.Evaluator.Types: type PrimUnwind = TyConMap -> PrimInfo -> [Type] -> [Value] -> Value -> [Term] -> Machine -> Maybe Machine
+ Clash.Core.Evaluator.Types: type PureHeap = VarEnv Term
+ Clash.Core.Evaluator.Types: type Stack = [StackFrame]
+ Clash.Core.Evaluator.Types: valToTerm :: Value -> Term
+ Clash.Core.FreeVars: countFreeOccurances :: Term -> VarEnv Int
+ Clash.Core.Pretty: instance Data.Default.Class.Default Clash.Core.Pretty.PrettyOptions
+ Clash.Core.Subst: deShadowAlt :: HasCallStack => InScopeSet -> (Pat, Term) -> (Pat, Term)
+ Clash.Core.Subst: deshadowLetExpr :: HasCallStack => InScopeSet -> [LetBinding] -> Term -> ([LetBinding], Term)
+ Clash.Core.Subst: substAlt :: HasCallStack => Doc () -> Subst -> (Pat, Term) -> (Pat, Term)
+ Clash.Core.Subst: substId :: HasCallStack => Subst -> Id -> Id
+ Clash.Core.Term: DeDup :: TickInfo
+ Clash.Core.Term: NoDeDup :: TickInfo
+ Clash.Core.Term: SuffixNameP :: NameMod
+ Clash.Core.Term: [primName] :: PrimInfo -> !Text
+ Clash.Core.Term: collectTermIds :: Term -> [Id]
+ Clash.Core.Term: walkTerm :: forall a. (Term -> Maybe a) -> Term -> [a]
+ Clash.Core.TermLiteral: class TermLiteral a
+ Clash.Core.TermLiteral: instance (Clash.Core.TermLiteral.TermLiteral a, Clash.Core.TermLiteral.TermLiteral b) => Clash.Core.TermLiteral.TermLiteral (a, b)
+ Clash.Core.TermLiteral: instance Clash.Core.TermLiteral.TermLiteral Clash.Core.Term.Term
+ Clash.Core.TermLiteral: instance Clash.Core.TermLiteral.TermLiteral Data.Text.Internal.Text
+ Clash.Core.TermLiteral: instance Clash.Core.TermLiteral.TermLiteral GHC.Base.String
+ Clash.Core.TermLiteral: instance Clash.Core.TermLiteral.TermLiteral GHC.Integer.Type.Integer
+ Clash.Core.TermLiteral: instance Clash.Core.TermLiteral.TermLiteral GHC.Natural.Natural
+ Clash.Core.TermLiteral: instance Clash.Core.TermLiteral.TermLiteral GHC.Types.Bool
+ Clash.Core.TermLiteral: instance Clash.Core.TermLiteral.TermLiteral GHC.Types.Char
+ Clash.Core.TermLiteral: instance Clash.Core.TermLiteral.TermLiteral GHC.Types.Int
+ Clash.Core.TermLiteral: instance Clash.Core.TermLiteral.TermLiteral a => Clash.Core.TermLiteral.TermLiteral (GHC.Maybe.Maybe a)
+ Clash.Core.TermLiteral: termToData :: (TermLiteral a, HasCallStack) => Term -> Either Term a
+ Clash.Core.TermLiteral: termToDataError :: TermLiteral a => Term -> Either String a
+ Clash.Core.TermLiteral: uncheckedTermToData :: TermLiteral a => Term -> a
+ Clash.Core.TermLiteral.TH: deriveTermToData :: Name -> Q Exp
+ Clash.Core.Type: mkPolyFunTy :: Type -> [Either TyVar Type] -> Type
+ Clash.Core.Util: inverseTopSortLetBindings :: HasCallStack => Term -> Term
+ Clash.Core.Util: isEnable :: TyConMap -> Type -> Bool
+ Clash.Core.Util: shouldSplit :: TyConMap -> Type -> Maybe (Term, [Type])
+ Clash.Core.Util: shouldSplit0 :: TyConMap -> TypeView -> Maybe (Term, [Type])
+ Clash.Core.Util: splitShouldSplit :: TyConMap -> [Type] -> [Type]
+ Clash.Core.Util: stripIP :: Type -> Type
+ Clash.Core.Util: tyLitShow :: TyConMap -> Type -> Except String String
+ Clash.Core.VarEnv: eltsVarSet :: VarSet -> [Var Any]
+ Clash.Core.VarEnv: foldlWithUniqueVarEnv' :: (a -> Unique -> b -> a) -> a -> VarEnv b -> a
+ Clash.Core.VarEnv: lookupVarEnvDirectly :: Unique -> VarEnv a -> Maybe a
+ Clash.Driver: knownBlackBoxFunctions :: HashMap String BlackBoxFunction
+ Clash.Driver: knownTemplateFunctions :: HashMap String TemplateFunction
+ Clash.Driver: sortTop :: BindingMap -> [TopEntityT] -> [TopEntityT]
+ Clash.Driver: splitTopAnn :: TyConMap -> SrcSpan -> Type -> TopEntity -> TopEntity
+ Clash.Driver: splitTopEntityT :: HasCallStack => TyConMap -> BindingMap -> TopEntityT -> TopEntityT
+ Clash.Driver.Types: Binding :: Id -> SrcSpan -> InlineSpec -> Term -> Binding
+ Clash.Driver.Types: DebugTry :: DebugLevel
+ Clash.Driver.Types: [bindingId] :: Binding -> Id
+ Clash.Driver.Types: [bindingLoc] :: Binding -> SrcSpan
+ Clash.Driver.Types: [bindingSpec] :: Binding -> InlineSpec
+ Clash.Driver.Types: [bindingTerm] :: Binding -> Term
+ Clash.Driver.Types: [opt_aggressiveXOpt] :: ClashOpts -> Bool
+ Clash.Driver.Types: [opt_dbgTransformations] :: ClashOpts -> Set String
+ Clash.Driver.Types: [opt_inlineWFCacheLimit] :: ClashOpts -> Word
+ Clash.Driver.Types: [succesFlags] :: Manifest -> (Int, Int, Bool)
+ Clash.Driver.Types: data Binding
+ Clash.Driver.Types: instance Control.DeepSeq.NFData Clash.Driver.Types.Binding
+ Clash.Driver.Types: instance Data.Binary.Class.Binary Clash.Driver.Types.Binding
+ Clash.Driver.Types: instance Data.Hashable.Class.Hashable Clash.Driver.Types.ClashOpts
+ Clash.Driver.Types: instance Data.Hashable.Class.Hashable Clash.Driver.Types.DebugLevel
+ Clash.Driver.Types: instance GHC.Enum.Enum Clash.Driver.Types.DebugLevel
+ Clash.Driver.Types: instance GHC.Generics.Generic Clash.Driver.Types.Binding
+ Clash.Driver.Types: instance GHC.Generics.Generic Clash.Driver.Types.DebugLevel
+ Clash.Driver.Types: instance GHC.Show.Show Clash.Driver.Types.Binding
+ Clash.Netlist.BlackBox: collectAppIO :: NetlistId -> [Term] -> [Term] -> NetlistMonad (Expr, [Declaration])
+ Clash.Netlist.BlackBox: collectBindIO :: NetlistId -> [Term] -> NetlistMonad (Expr, [Declaration])
+ Clash.Netlist.BlackBox: collectMealy :: HasCallStack => Identifier -> NetlistId -> TyConMap -> [Term] -> NetlistMonad [Declaration]
+ Clash.Netlist.BlackBox: unSimIO :: TyConMap -> Term -> Term
+ Clash.Netlist.BlackBox.Types: CtxName :: Element
+ Clash.Netlist.BlackBox.Types: NoRenderVoid :: RenderVoid
+ Clash.Netlist.BlackBox.Types: RenderVoid :: RenderVoid
+ Clash.Netlist.BlackBox.Types: ToVar :: [Element] -> !Int -> Element
+ Clash.Netlist.BlackBox.Types: [bbFunctionPlurality] :: BlackBoxMeta -> [(Int, Int)]
+ Clash.Netlist.BlackBox.Types: [bbRenderVoid] :: BlackBoxMeta -> RenderVoid
+ Clash.Netlist.BlackBox.Types: data RenderVoid
+ Clash.Netlist.BlackBox.Types: instance Control.DeepSeq.NFData Clash.Netlist.BlackBox.Types.RenderVoid
+ Clash.Netlist.BlackBox.Types: instance Data.Aeson.Types.FromJSON.FromJSON Clash.Netlist.BlackBox.Types.RenderVoid
+ Clash.Netlist.BlackBox.Types: instance Data.Binary.Class.Binary Clash.Netlist.BlackBox.Types.RenderVoid
+ Clash.Netlist.BlackBox.Types: instance Data.Hashable.Class.Hashable Clash.Netlist.BlackBox.Types.RenderVoid
+ Clash.Netlist.BlackBox.Types: instance GHC.Generics.Generic Clash.Netlist.BlackBox.Types.RenderVoid
+ Clash.Netlist.BlackBox.Types: instance GHC.Show.Show Clash.Netlist.BlackBox.Types.RenderVoid
+ Clash.Netlist.BlackBox.Util: getUsedArguments :: BlackBox -> [Int]
+ Clash.Netlist.Types: AlwaysClocked :: ActiveEdge -> Expr -> [Seq] -> Seq
+ Clash.Netlist.Types: AlwaysComb :: [Seq] -> Seq
+ Clash.Netlist.Types: Branch :: !Expr -> !HWType -> [(Maybe Literal, [Seq])] -> Seq
+ Clash.Netlist.Types: ComponentPrefix :: Maybe Identifier -> Maybe Identifier -> ComponentPrefix
+ Clash.Netlist.Types: Concurrent :: DeclarationType
+ Clash.Netlist.Types: CoreId :: Id -> NetlistId
+ Clash.Netlist.Types: CustomProduct :: !Identifier -> !DataRepr' -> !Size -> Maybe [Text] -> [(FieldAnn, HWType)] -> HWType
+ Clash.Netlist.Types: FileType :: HWType
+ Clash.Netlist.Types: Initial :: [Seq] -> Seq
+ Clash.Netlist.Types: MultiId :: [Id] -> NetlistId
+ Clash.Netlist.Types: NetlistId :: Identifier -> Type -> NetlistId
+ Clash.Netlist.Types: Noop :: Expr
+ Clash.Netlist.Types: Seq :: [Seq] -> Declaration
+ Clash.Netlist.Types: SeqDecl :: Declaration -> Seq
+ Clash.Netlist.Types: Sequential :: DeclarationType
+ Clash.Netlist.Types: TopEntityT :: Id -> Maybe TopEntity -> Maybe Id -> TopEntityT
+ Clash.Netlist.Types: [SomeBackend] :: Backend backend => backend -> SomeBackend
+ Clash.Netlist.Types: [_backend] :: NetlistState -> SomeBackend
+ Clash.Netlist.Types: [associatedTestbench] :: TopEntityT -> Maybe Id
+ Clash.Netlist.Types: [bbCtxName] :: BlackBoxContext -> Maybe Identifier
+ Clash.Netlist.Types: [componentPrefixOther] :: ComponentPrefix -> Maybe Identifier
+ Clash.Netlist.Types: [componentPrefixTop] :: ComponentPrefix -> Maybe Identifier
+ Clash.Netlist.Types: [topAnnotation] :: TopEntityT -> Maybe TopEntity
+ Clash.Netlist.Types: [topId] :: TopEntityT -> Id
+ Clash.Netlist.Types: backend :: Lens' NetlistState SomeBackend
+ Clash.Netlist.Types: data ComponentPrefix
+ Clash.Netlist.Types: data DeclarationType
+ Clash.Netlist.Types: data NetlistId
+ Clash.Netlist.Types: data Seq
+ Clash.Netlist.Types: data SomeBackend
+ Clash.Netlist.Types: data TopEntityT
+ Clash.Netlist.Types: instance Control.DeepSeq.NFData Clash.Netlist.Types.Expr
+ Clash.Netlist.Types: instance GHC.Generics.Generic Clash.Netlist.Types.TopEntityT
+ Clash.Netlist.Types: instance GHC.Show.Show Clash.Netlist.Types.ComponentPrefix
+ Clash.Netlist.Types: instance GHC.Show.Show Clash.Netlist.Types.NetlistId
+ Clash.Netlist.Types: instance GHC.Show.Show Clash.Netlist.Types.Seq
+ Clash.Netlist.Types: netlistId :: (Identifier -> r) -> (Id -> r) -> NetlistId -> [r]
+ Clash.Netlist.Types: netlistId1 :: HasCallStack => (Identifier -> r) -> (Id -> r) -> NetlistId -> r
+ Clash.Netlist.Types: netlistTypes :: NetlistId -> [Type]
+ Clash.Netlist.Types: netlistTypes1 :: HasCallStack => NetlistId -> Type
+ Clash.Netlist.Util: convertToCustomRepr :: HasCallStack => CustomReprs -> DataRepr' -> HWType -> HWType
+ Clash.Netlist.Util: isVoidMaybe :: Bool -> Maybe HWType -> Bool
+ Clash.Netlist.Util: maybeConvertToCustomRepr :: CustomReprs -> Type -> HWType -> HWType
+ Clash.Netlist.Util: preserveState :: NetlistMonad a -> NetlistMonad a
+ Clash.Netlist.Util: setBinderName :: Subst -> Id -> Bool -> (Id, Subst, [(Id, Term)]) -> (Id, Term) -> NetlistMonad ((Id, Subst, [(Id, Term)]), Id)
+ Clash.Netlist.Util: stripVoid :: HWType -> HWType
+ Clash.Netlist.Util: unexpectedProjectionErrorMsg :: DataRepr' -> Int -> Int -> String
+ Clash.Normalize.Transformations: separateArguments :: HasCallStack => NormRewrite
+ Clash.Normalize.Transformations: separateLambda :: TyConMap -> TransformContext -> Id -> Term -> Maybe Term
+ Clash.Normalize.Transformations: xOptimize :: HasCallStack => NormRewrite
+ Clash.Normalize.Types: [_inlineWFCacheLimit] :: NormalizeState -> !Word
+ Clash.Normalize.Types: inlineWFCacheLimit :: Lens' NormalizeState Word
+ Clash.Normalize.Util: mkInlineTick :: Id -> TickInfo
+ Clash.Normalize.Util: substWithTyEq :: Term -> Term
+ Clash.Normalize.Util: tvSubstWithTyEq :: Type -> Type
+ Clash.Primitives.Intel.ClockGen: alteraPllQsysTF :: TemplateFunction
+ Clash.Primitives.Intel.ClockGen: alteraPllQsysTemplate :: Backend s => BlackBoxContext -> State s Doc
+ Clash.Primitives.Intel.ClockGen: altpllQsysTF :: TemplateFunction
+ Clash.Primitives.Intel.ClockGen: altpllQsysTemplate :: Backend s => BlackBoxContext -> State s Doc
+ Clash.Primitives.Sized.ToInteger: bvToIntegerVHDL :: BlackBoxFunction
+ Clash.Primitives.Sized.ToInteger: bvToIntegerVerilog :: BlackBoxFunction
+ Clash.Primitives.Sized.ToInteger: indexToIntegerVHDL :: BlackBoxFunction
+ Clash.Primitives.Sized.ToInteger: indexToIntegerVerilog :: BlackBoxFunction
+ Clash.Primitives.Sized.ToInteger: signedToIntegerVHDL :: BlackBoxFunction
+ Clash.Primitives.Sized.ToInteger: signedToIntegerVerilog :: BlackBoxFunction
+ Clash.Primitives.Sized.ToInteger: unsignedToIntegerVHDL :: BlackBoxFunction
+ Clash.Primitives.Sized.ToInteger: unsignedToIntegerVerilog :: BlackBoxFunction
+ Clash.Primitives.Sized.Vector: FCall :: Identifier -> Identifier -> Identifier -> FCall
+ Clash.Primitives.Sized.Vector: data FCall
+ Clash.Primitives.Sized.Vector: foldBBF :: HasCallStack => BlackBoxFunction
+ Clash.Primitives.Sized.Vector: foldFunctionPlurality :: HasCallStack => Int -> Int
+ Clash.Primitives.Sized.Vector: foldTF :: TemplateFunction
+ Clash.Primitives.Sized.Vector: foldTF' :: forall s. (HasCallStack, Backend s) => BlackBoxContext -> State s Doc
+ Clash.Primitives.Types: IgnoredArguments :: [Int] -> UsedArguments
+ Clash.Primitives.Types: UsedArguments :: [Int] -> UsedArguments
+ Clash.Primitives.Types: [functionPlurality] :: Primitive a b c d -> [(Int, Int)]
+ Clash.Primitives.Types: [renderVoid] :: Primitive a b c d -> RenderVoid
+ Clash.Primitives.Types: [resultInit] :: Primitive a b c d -> Maybe b
+ Clash.Primitives.Types: [resultName] :: Primitive a b c d -> Maybe b
+ Clash.Primitives.Types: [usedArguments] :: Primitive a b c d -> UsedArguments
+ Clash.Primitives.Types: data UsedArguments
+ Clash.Primitives.Types: instance Control.DeepSeq.NFData Clash.Primitives.Types.UsedArguments
+ Clash.Primitives.Types: instance Data.Binary.Class.Binary Clash.Primitives.Types.UsedArguments
+ Clash.Primitives.Types: instance Data.Hashable.Class.Hashable Clash.Primitives.Types.UsedArguments
+ Clash.Primitives.Types: instance GHC.Generics.Generic Clash.Primitives.Types.UsedArguments
+ Clash.Primitives.Types: instance GHC.Show.Show Clash.Primitives.Types.UsedArguments
+ Clash.Primitives.Util: getFunctionPlurality :: HasCallStack => CompiledPrimitive -> [Either Term Type] -> Type -> Int -> NetlistMonad Int
+ Clash.Rewrite.Types: [_aggressiveXOpt] :: RewriteEnv -> Bool
+ Clash.Rewrite.Types: [_dbgTransformations] :: RewriteEnv -> Set String
+ Clash.Rewrite.Types: aggressiveXOpt :: Lens' RewriteEnv Bool
+ Clash.Rewrite.Types: dbgTransformations :: Lens' RewriteEnv (Set String)
+ Clash.Rewrite.Util: bindPureHeap :: TyConMap -> PureHeap -> Rewrite extra -> Rewrite extra
+ Clash.Rewrite.Util: isWorkFreeClockOrResetOrEnable :: TyConMap -> Term -> Maybe Bool
+ Clash.Rewrite.Util: liftAndSubsituteBinders :: InScopeSet -> [LetBinding] -> [LetBinding] -> Term -> RewriteMonad extra ([LetBinding], Term)
+ Clash.Rewrite.Util: whnfRW :: Bool -> TransformContext -> Term -> Rewrite extra -> RewriteMonad extra Term
+ Clash.Unique: foldlWithUnique' :: (a -> Unique -> b -> a) -> a -> UniqMap b -> a
+ Clash.Util: concatMapM :: Monad m => (a -> m [b]) -> [a] -> m [b]
+ Clash.Util: filterOnFst :: (a -> Bool) -> [(a, b)] -> [b]
+ Clash.Util: filterOnSnd :: (b -> Bool) -> [(a, b)] -> [a]
+ Clash.Util: indexNote' :: HasCallStack => String -> Int -> [a] -> a
+ Clash.Util: orM :: Monad m => [m Bool] -> m Bool
+ Clash.Util: traceShowWith :: Show b => (a -> b) -> a -> a
+ Clash.Util: traceWith :: (a -> String) -> a -> a
+ Clash.Util: unwantedLanguageExtensions :: [Extension]
+ Clash.Util: wantedLanguageExtensions :: [Extension]
+ Clash.Util.Interpolate: i :: QuasiQuoter
+ Clash.Util.Interpolate: instance GHC.Show.Show Clash.Util.Interpolate.Line
+ Clash.Util.Interpolate: instance GHC.Show.Show Clash.Util.Interpolate.Node
+ Data.Text.Prettyprint.Doc.Extra: comment :: Applicative f => Text -> Text -> f Doc
+ Data.Text.Prettyprint.Doc.Extra: squote :: Applicative f => f Doc
- Clash.Core.Evaluator: allocate :: Heap -> Stack -> [LetBinding] -> Term -> State
+ Clash.Core.Evaluator: allocate :: [LetBinding] -> Term -> Machine -> Machine
- Clash.Core.Evaluator: apply :: Heap -> Stack -> Value -> Id -> State
+ Clash.Core.Evaluator: apply :: Value -> Id -> Machine -> Machine
- Clash.Core.Evaluator: instantiate :: Heap -> Stack -> Value -> Type -> State
+ Clash.Core.Evaluator: instantiate :: Value -> Type -> Machine -> Machine
- Clash.Core.Evaluator: newLetBinding :: TyConMap -> Heap -> Term -> (Heap, Id)
+ Clash.Core.Evaluator: newLetBinding :: TyConMap -> Machine -> Term -> (Machine, Id)
- Clash.Core.Evaluator: scrutinise :: Heap -> Stack -> Value -> [Alt] -> State
+ Clash.Core.Evaluator: scrutinise :: Value -> [Alt] -> Machine -> Machine
- Clash.Core.Evaluator: step :: PrimEvaluator -> TyConMap -> State -> Maybe State
+ Clash.Core.Evaluator: step :: Step
- Clash.Core.Evaluator: unwind :: PrimEvaluator -> TyConMap -> Heap -> Stack -> Value -> Maybe State
+ Clash.Core.Evaluator: unwind :: TyConMap -> Machine -> Value -> Maybe Machine
- Clash.Core.Evaluator: unwindStack :: State -> Maybe State
+ Clash.Core.Evaluator: unwindStack :: Machine -> Maybe Machine
- Clash.Core.Evaluator: update :: Heap -> Stack -> Id -> Value -> State
+ Clash.Core.Evaluator: update :: IdScope -> Id -> Value -> Machine -> Machine
- Clash.Core.Evaluator: whnf :: PrimEvaluator -> TyConMap -> Bool -> State -> State
+ Clash.Core.Evaluator: whnf :: TyConMap -> Bool -> Machine -> Machine
- Clash.Core.Evaluator: whnf' :: PrimEvaluator -> BindingMap -> TyConMap -> GlobalHeap -> Supply -> InScopeSet -> Bool -> Term -> (GlobalHeap, PureHeap, Term)
+ Clash.Core.Evaluator: whnf' :: PrimStep -> PrimUnwind -> BindingMap -> TyConMap -> PrimHeap -> Supply -> InScopeSet -> Bool -> Term -> (PrimHeap, PureHeap, Term)
- Clash.Core.Term: Prim :: !Text -> !PrimInfo -> Term
+ Clash.Core.Term: Prim :: !PrimInfo -> Term
- Clash.Core.Term: PrimInfo :: !Type -> !WorkInfo -> PrimInfo
+ Clash.Core.Term: PrimInfo :: !Text -> !Type -> !WorkInfo -> PrimInfo
- Clash.Core.Util: piResultTy :: TyConMap -> Type -> Type -> Type
+ Clash.Core.Util: piResultTy :: HasCallStack => TyConMap -> Type -> Type -> Type
- Clash.Core.Util: piResultTyMaybe :: TyConMap -> Type -> Type -> Maybe Type
+ Clash.Core.Util: piResultTyMaybe :: HasCallStack => TyConMap -> Type -> Type -> Maybe Type
- Clash.Core.Util: piResultTys :: TyConMap -> Type -> [Type] -> Type
+ Clash.Core.Util: piResultTys :: HasCallStack => TyConMap -> Type -> [Type] -> Type
- Clash.Driver: generateHDL :: forall backend. Backend backend => CustomReprs -> BindingMap -> Maybe backend -> CompiledPrimMap -> TyConMap -> IntMap TyConName -> (CustomReprs -> TyConMap -> Type -> State HWMap (Maybe (Either String FilteredHWType))) -> PrimEvaluator -> [(Id, Maybe TopEntity, Maybe Id)] -> ClashOpts -> (UTCTime, UTCTime) -> IO ()
+ Clash.Driver: generateHDL :: forall backend. Backend backend => CustomReprs -> BindingMap -> Maybe backend -> CompiledPrimMap -> TyConMap -> IntMap TyConName -> (CustomReprs -> TyConMap -> Type -> State HWMap (Maybe (Either String FilteredHWType))) -> (PrimStep, PrimUnwind) -> [TopEntityT] -> ClashOpts -> (UTCTime, UTCTime) -> IO ()
- Clash.Driver: normalizeEntity :: CustomReprs -> BindingMap -> CompiledPrimMap -> TyConMap -> IntMap TyConName -> (CustomReprs -> TyConMap -> Type -> State HWMap (Maybe (Either String FilteredHWType))) -> PrimEvaluator -> [Id] -> ClashOpts -> Supply -> Id -> BindingMap
+ Clash.Driver: normalizeEntity :: CustomReprs -> BindingMap -> CompiledPrimMap -> TyConMap -> IntMap TyConName -> (CustomReprs -> TyConMap -> Type -> State HWMap (Maybe (Either String FilteredHWType))) -> (PrimStep, PrimUnwind) -> [Id] -> ClashOpts -> Supply -> Id -> BindingMap
- Clash.Driver.Types: ClashOpts :: Int -> Int -> Word -> Word -> DebugLevel -> Bool -> Bool -> Bool -> OverridingBool -> Int -> Maybe String -> HdlSyn -> Bool -> Bool -> [FilePath] -> Maybe String -> Bool -> Bool -> Bool -> Maybe (Maybe Int) -> Bool -> ClashOpts
+ Clash.Driver.Types: ClashOpts :: Int -> Int -> Word -> Word -> DebugLevel -> Set String -> Bool -> Bool -> Bool -> OverridingBool -> Int -> Maybe String -> HdlSyn -> Bool -> Bool -> [FilePath] -> Maybe String -> Bool -> Bool -> Bool -> Maybe (Maybe Int) -> Bool -> Bool -> Word -> ClashOpts
- Clash.Driver.Types: Manifest :: (Int, Maybe Int) -> [Text] -> [Text] -> [Text] -> [Text] -> [Text] -> Manifest
+ Clash.Driver.Types: Manifest :: (Int, Maybe Int) -> (Int, Int, Bool) -> [Text] -> [Text] -> [Text] -> [Text] -> [Text] -> Manifest
- Clash.Driver.Types: type BindingMap = VarEnv (Id, SrcSpan, InlineSpec, Term)
+ Clash.Driver.Types: type BindingMap = VarEnv Binding
- Clash.Netlist: genNames :: Bool -> (IdType -> Identifier -> Identifier) -> (Maybe Identifier, Maybe Identifier) -> HashMap Identifier Word -> VarEnv Identifier -> BindingMap -> (HashMap Identifier Word, VarEnv Identifier)
+ Clash.Netlist: genNames :: Bool -> (IdType -> Identifier -> Identifier) -> ComponentPrefix -> HashMap Identifier Word -> VarEnv Identifier -> BindingMap -> (HashMap Identifier Word, VarEnv Identifier)
- Clash.Netlist: genNetlist :: Bool -> ClashOpts -> CustomReprs -> BindingMap -> [(Id, Maybe TopEntity, Maybe Id)] -> CompiledPrimMap -> TyConMap -> (CustomReprs -> TyConMap -> Type -> State HWMap (Maybe (Either String FilteredHWType))) -> Int -> (IdType -> Identifier -> Identifier) -> (IdType -> Identifier -> Identifier -> Identifier) -> Bool -> HashMap Identifier Word -> FilePath -> (Maybe Identifier, Maybe Identifier) -> Id -> IO ([([Bool], SrcSpan, HashMap Identifier Word, Component)], HashMap Identifier Word)
+ Clash.Netlist: genNetlist :: Bool -> ClashOpts -> CustomReprs -> BindingMap -> [TopEntityT] -> CompiledPrimMap -> TyConMap -> (CustomReprs -> TyConMap -> Type -> State HWMap (Maybe (Either String FilteredHWType))) -> Int -> (IdType -> Identifier -> Identifier) -> (IdType -> Identifier -> Identifier -> Identifier) -> Bool -> SomeBackend -> HashMap Identifier Word -> FilePath -> ComponentPrefix -> Id -> IO ([([Bool], SrcSpan, HashMap Identifier Word, Component)], HashMap Identifier Word)
- Clash.Netlist: mkDcApplication :: HasCallStack => HWType -> Either Identifier Id -> DataCon -> [Term] -> NetlistMonad (Expr, [Declaration])
+ Clash.Netlist: mkDcApplication :: HasCallStack => [HWType] -> NetlistId -> DataCon -> [Term] -> NetlistMonad (Expr, [Declaration])
- Clash.Netlist: mkDeclarations' :: HasCallStack => Id -> Term -> NetlistMonad [Declaration]
+ Clash.Netlist: mkDeclarations' :: HasCallStack => DeclarationType -> Id -> Term -> NetlistMonad [Declaration]
- Clash.Netlist: mkExpr :: HasCallStack => Bool -> Either Identifier Id -> Type -> Term -> NetlistMonad (Expr, [Declaration])
+ Clash.Netlist: mkExpr :: HasCallStack => Bool -> DeclarationType -> NetlistId -> Term -> NetlistMonad (Expr, [Declaration])
- Clash.Netlist: mkProjection :: Bool -> Either Identifier Id -> Term -> Type -> Alt -> NetlistMonad (Expr, [Declaration])
+ Clash.Netlist: mkProjection :: Bool -> NetlistId -> Term -> Type -> Alt -> NetlistMonad (Expr, [Declaration])
- Clash.Netlist: mkSelection :: Either Identifier Id -> Term -> Type -> [Alt] -> [Declaration] -> NetlistMonad [Declaration]
+ Clash.Netlist: mkSelection :: DeclarationType -> NetlistId -> Term -> Type -> [Alt] -> [Declaration] -> NetlistMonad [Declaration]
- Clash.Netlist: runNetlistMonad :: Bool -> ClashOpts -> CustomReprs -> BindingMap -> VarEnv (Type, Maybe TopEntity) -> CompiledPrimMap -> TyConMap -> (CustomReprs -> TyConMap -> Type -> State HWMap (Maybe (Either String FilteredHWType))) -> Int -> (IdType -> Identifier -> Identifier) -> (IdType -> Identifier -> Identifier -> Identifier) -> Bool -> HashMap Identifier Word -> FilePath -> (Maybe Identifier, Maybe Identifier) -> NetlistMonad a -> IO (a, NetlistState)
+ Clash.Netlist: runNetlistMonad :: Bool -> ClashOpts -> CustomReprs -> BindingMap -> VarEnv TopEntityT -> CompiledPrimMap -> TyConMap -> (CustomReprs -> TyConMap -> Type -> State HWMap (Maybe (Either String FilteredHWType))) -> Int -> (IdType -> Identifier -> Identifier) -> (IdType -> Identifier -> Identifier -> Identifier) -> Bool -> SomeBackend -> HashMap Identifier Word -> FilePath -> ComponentPrefix -> NetlistMonad a -> IO (a, NetlistState)
- Clash.Netlist.BlackBox: extractPrimWarnOrFail :: Text -> NetlistMonad CompiledPrimitive
+ Clash.Netlist.BlackBox: extractPrimWarnOrFail :: HasCallStack => Text -> NetlistMonad CompiledPrimitive
- Clash.Netlist.BlackBox: mkBlackBoxContext :: Text -> Id -> [Term] -> NetlistMonad (BlackBoxContext, [Declaration])
+ Clash.Netlist.BlackBox: mkBlackBoxContext :: Text -> Id -> [Either Term Type] -> NetlistMonad (BlackBoxContext, [Declaration])
- Clash.Netlist.BlackBox: mkPrimitive :: Bool -> Bool -> Either Identifier Id -> Text -> [Either Term Type] -> Type -> [Declaration] -> NetlistMonad (Expr, [Declaration])
+ Clash.Netlist.BlackBox: mkPrimitive :: Bool -> Bool -> NetlistId -> PrimInfo -> [Either Term Type] -> [Declaration] -> NetlistMonad (Expr, [Declaration])
- Clash.Netlist.BlackBox.Types: BlackBoxMeta :: Bool -> TemplateKind -> [BlackBoxTemplate] -> [BlackBoxTemplate] -> [((Text, Text), BlackBox)] -> BlackBoxMeta
+ Clash.Netlist.BlackBox.Types: BlackBoxMeta :: Bool -> TemplateKind -> [BlackBoxTemplate] -> [BlackBoxTemplate] -> [(Int, Int)] -> [((Text, Text), BlackBox)] -> RenderVoid -> BlackBoxMeta
- Clash.Netlist.BlackBox.Types: Decl :: !Int -> [(BlackBoxTemplate, BlackBoxTemplate)] -> Decl
+ Clash.Netlist.BlackBox.Types: Decl :: !Int -> !Int -> [(BlackBoxTemplate, BlackBoxTemplate)] -> Decl
- Clash.Netlist.BlackBox.Util: prettyElem :: Monad m => Element -> Mon m Text
+ Clash.Netlist.BlackBox.Util: prettyElem :: (HasCallStack, Monad m) => Element -> Mon m Text
- Clash.Netlist.BlackBox.Util: renderElem :: Backend backend => BlackBoxContext -> Element -> State backend (Int -> Text)
+ Clash.Netlist.BlackBox.Util: renderElem :: HasCallStack => Backend backend => BlackBoxContext -> Element -> State backend (Int -> Text)
- Clash.Netlist.Types: Component :: !Identifier -> [(Identifier, HWType)] -> [(WireOrReg, (Identifier, HWType))] -> [Declaration] -> Component
+ Clash.Netlist.Types: Component :: !Identifier -> [(Identifier, HWType)] -> [(WireOrReg, (Identifier, HWType), Maybe Expr)] -> [Declaration] -> Component
- Clash.Netlist.Types: Context :: Text -> (Expr, HWType) -> [(Expr, HWType, Bool)] -> IntMap (Either BlackBox (Identifier, [Declaration]), WireOrReg, [BlackBoxTemplate], [BlackBoxTemplate], [((Text, Text), BlackBox)], BlackBoxContext) -> [Identifier] -> Int -> Identifier -> BlackBoxContext
+ Clash.Netlist.Types: Context :: Text -> (Expr, HWType) -> [(Expr, HWType, Bool)] -> IntMap [(Either BlackBox (Identifier, [Declaration]), WireOrReg, [BlackBoxTemplate], [BlackBoxTemplate], [((Text, Text), BlackBox)], BlackBoxContext)] -> [Identifier] -> Int -> Identifier -> Maybe Identifier -> BlackBoxContext
- Clash.Netlist.Types: NetDecl' :: Maybe Comment -> WireOrReg -> !Identifier -> Either Identifier HWType -> Declaration
+ Clash.Netlist.Types: NetDecl' :: Maybe Comment -> WireOrReg -> !Identifier -> Either Identifier HWType -> Maybe Expr -> Declaration
- Clash.Netlist.Types: NetlistState :: BindingMap -> !Int -> VarEnv ([Bool], SrcSpan, HashMap Identifier Word, Component) -> CompiledPrimMap -> (CustomReprs -> TyConMap -> Type -> State HWMap (Maybe (Either String FilteredHWType))) -> TyConMap -> !(Identifier, SrcSpan) -> Int -> (IdType -> Identifier -> Identifier) -> (IdType -> Identifier -> Identifier -> Identifier) -> HashMap Identifier Word -> HashMap Identifier Word -> Set Text -> VarEnv Identifier -> VarEnv (Type, Maybe TopEntity) -> FilePath -> Int -> (Maybe Identifier, Maybe Identifier) -> CustomReprs -> ClashOpts -> Bool -> Bool -> HWMap -> NetlistState
+ Clash.Netlist.Types: NetlistState :: BindingMap -> !Int -> VarEnv ([Bool], SrcSpan, HashMap Identifier Word, Component) -> CompiledPrimMap -> (CustomReprs -> TyConMap -> Type -> State HWMap (Maybe (Either String FilteredHWType))) -> TyConMap -> !(Identifier, SrcSpan) -> Int -> (IdType -> Identifier -> Identifier) -> (IdType -> Identifier -> Identifier -> Identifier) -> HashMap Identifier Word -> HashMap Identifier Word -> Set Text -> VarEnv Identifier -> VarEnv TopEntityT -> FilePath -> Int -> ComponentPrefix -> CustomReprs -> ClashOpts -> Bool -> Bool -> SomeBackend -> HWMap -> NetlistState
- Clash.Netlist.Types: [_componentPrefix] :: NetlistState -> (Maybe Identifier, Maybe Identifier)
+ Clash.Netlist.Types: [_componentPrefix] :: NetlistState -> ComponentPrefix
- Clash.Netlist.Types: [_topEntityAnns] :: NetlistState -> VarEnv (Type, Maybe TopEntity)
+ Clash.Netlist.Types: [_topEntityAnns] :: NetlistState -> VarEnv TopEntityT
- Clash.Netlist.Types: [bbFunctions] :: BlackBoxContext -> IntMap (Either BlackBox (Identifier, [Declaration]), WireOrReg, [BlackBoxTemplate], [BlackBoxTemplate], [((Text, Text), BlackBox)], BlackBoxContext)
+ Clash.Netlist.Types: [bbFunctions] :: BlackBoxContext -> IntMap [(Either BlackBox (Identifier, [Declaration]), WireOrReg, [BlackBoxTemplate], [BlackBoxTemplate], [((Text, Text), BlackBox)], BlackBoxContext)]
- Clash.Netlist.Types: [outputs] :: Component -> [(WireOrReg, (Identifier, HWType))]
+ Clash.Netlist.Types: [outputs] :: Component -> [(WireOrReg, (Identifier, HWType), Maybe Expr)]
- Clash.Netlist.Types: componentPrefix :: Lens' NetlistState (Maybe Identifier, Maybe Identifier)
+ Clash.Netlist.Types: componentPrefix :: Lens' NetlistState ComponentPrefix
- Clash.Netlist.Types: topEntityAnns :: Lens' NetlistState (VarEnv (Type, Maybe TopEntity))
+ Clash.Netlist.Types: topEntityAnns :: Lens' NetlistState (VarEnv TopEntityT)
- Clash.Netlist.Util: genComponentName :: Bool -> HashMap Identifier Word -> (IdType -> Identifier -> Identifier) -> (Maybe Identifier, Maybe Identifier) -> Id -> Identifier
+ Clash.Netlist.Util: genComponentName :: Bool -> HashMap Identifier Word -> (IdType -> Identifier -> Identifier) -> ComponentPrefix -> Id -> Identifier
- Clash.Netlist.Util: genTopComponentName :: Bool -> (IdType -> Identifier -> Identifier) -> (Maybe Identifier, Maybe Identifier) -> Maybe TopEntity -> Id -> Identifier
+ Clash.Netlist.Util: genTopComponentName :: Bool -> (IdType -> Identifier -> Identifier) -> ComponentPrefix -> Maybe TopEntity -> Id -> Identifier
- Clash.Netlist.Util: mkUniqueNormalized :: InScopeSet -> Maybe (Maybe TopEntity) -> ([Id], [LetBinding], Id) -> NetlistMonad ([Bool], [(Identifier, HWType)], [Declaration], [(Identifier, HWType)], [Declaration], [LetBinding], Maybe Id)
+ Clash.Netlist.Util: mkUniqueNormalized :: HasCallStack => InScopeSet -> Maybe (Maybe TopEntity) -> ([Id], [LetBinding], Id) -> NetlistMonad ([Bool], [(Identifier, HWType)], [Declaration], [(Identifier, HWType)], [Declaration], [LetBinding], Maybe Id)
- Clash.Normalize: CBranch :: (Id, (Id, SrcSpan, InlineSpec, Term)) -> [CallTree] -> CallTree
+ Clash.Normalize: CBranch :: (Id, Binding) -> [CallTree] -> CallTree
- Clash.Normalize: CLeaf :: (Id, (Id, SrcSpan, InlineSpec, Term)) -> CallTree
+ Clash.Normalize: CLeaf :: (Id, Binding) -> CallTree
- Clash.Normalize: callTreeToList :: [Id] -> CallTree -> ([Id], [(Id, (Id, SrcSpan, InlineSpec, Term))])
+ Clash.Normalize: callTreeToList :: [Id] -> CallTree -> ([Id], [(Id, Binding)])
- Clash.Normalize: normalize' :: Id -> NormalizeSession ([Id], (Id, (Id, SrcSpan, InlineSpec, Term)))
+ Clash.Normalize: normalize' :: Id -> NormalizeSession ([Id], (Id, Binding))
- Clash.Normalize: runNormalization :: ClashOpts -> Supply -> BindingMap -> (CustomReprs -> TyConMap -> Type -> State HWMap (Maybe (Either String FilteredHWType))) -> CustomReprs -> TyConMap -> IntMap TyConName -> PrimEvaluator -> CompiledPrimMap -> VarEnv Bool -> [Id] -> NormalizeSession a -> a
+ Clash.Normalize: runNormalization :: ClashOpts -> Supply -> BindingMap -> (CustomReprs -> TyConMap -> Type -> State HWMap (Maybe (Either String FilteredHWType))) -> CustomReprs -> TyConMap -> IntMap TyConName -> (PrimStep, PrimUnwind) -> CompiledPrimMap -> VarEnv Bool -> [Id] -> NormalizeSession a -> a
- Clash.Normalize.Types: NormalizeState :: BindingMap -> Map (Id, Int, Either Term Type) Id -> VarEnv Int -> !Int -> VarEnv (VarEnv Int) -> !Int -> !Word -> !Word -> CompiledPrimMap -> Map Text (Set Int) -> VarEnv Bool -> Bool -> Bool -> NormalizeState
+ Clash.Normalize.Types: NormalizeState :: BindingMap -> Map (Id, Int, Either Term Type) Id -> VarEnv Int -> !Int -> VarEnv (VarEnv Int) -> !Int -> !Word -> !Word -> CompiledPrimMap -> Map Text (Set Int) -> VarEnv Bool -> Bool -> Bool -> !Word -> NormalizeState
- Clash.Normalize.Util: normalizeTopLvlBndr :: Id -> (Id, SrcSpan, InlineSpec, Term) -> NormalizeSession (Id, SrcSpan, InlineSpec, Term)
+ Clash.Normalize.Util: normalizeTopLvlBndr :: Bool -> Id -> Binding -> NormalizeSession Binding
- Clash.Primitives.Types: BlackBox :: !Text -> WorkInfo -> TemplateKind -> c -> Bool -> [a] -> [a] -> [((Text, Text), b)] -> b -> Primitive a b c d
+ Clash.Primitives.Types: BlackBox :: !Text -> WorkInfo -> RenderVoid -> TemplateKind -> c -> Bool -> [a] -> [a] -> [(Int, Int)] -> [((Text, Text), b)] -> Maybe b -> Maybe b -> b -> Primitive a b c d
- Clash.Primitives.Types: BlackBoxHaskell :: !Text -> WorkInfo -> BlackBoxFunctionName -> d -> Primitive a b c d
+ Clash.Primitives.Types: BlackBoxHaskell :: !Text -> WorkInfo -> UsedArguments -> BlackBoxFunctionName -> d -> Primitive a b c d
- Clash.Rewrite.Types: RewriteEnv :: DebugLevel -> (CustomReprs -> TyConMap -> Type -> State HWMap (Maybe (Either String FilteredHWType))) -> TyConMap -> IntMap TyConName -> PrimEvaluator -> VarSet -> CustomReprs -> RewriteEnv
+ Clash.Rewrite.Types: RewriteEnv :: DebugLevel -> Set String -> Bool -> (CustomReprs -> TyConMap -> Type -> State HWMap (Maybe (Either String FilteredHWType))) -> TyConMap -> IntMap TyConName -> (PrimStep, PrimUnwind) -> VarSet -> CustomReprs -> RewriteEnv
- Clash.Rewrite.Types: RewriteState :: {-# UNPACK #-} !Int -> !BindingMap -> !Supply -> (Id, SrcSpan) -> {-# UNPACK #-} !Int -> GlobalHeap -> !extra -> RewriteState extra
+ Clash.Rewrite.Types: RewriteState :: {-# UNPACK #-} !Int -> !BindingMap -> !Supply -> (Id, SrcSpan) -> {-# UNPACK #-} !Int -> PrimHeap -> !extra -> RewriteState extra
- Clash.Rewrite.Types: [_evaluator] :: RewriteEnv -> PrimEvaluator
+ Clash.Rewrite.Types: [_evaluator] :: RewriteEnv -> (PrimStep, PrimUnwind)
- Clash.Rewrite.Types: [_globalHeap] :: RewriteState extra -> GlobalHeap
+ Clash.Rewrite.Types: [_globalHeap] :: RewriteState extra -> PrimHeap
- Clash.Rewrite.Types: bindings :: forall extra_a3alq. Lens' (RewriteState extra_a3alq) BindingMap
+ Clash.Rewrite.Types: bindings :: forall extra_a3wDl. Lens' (RewriteState extra_a3wDl) BindingMap
- Clash.Rewrite.Types: curFun :: forall extra_a3alq. Lens' (RewriteState extra_a3alq) (Id, SrcSpan)
+ Clash.Rewrite.Types: curFun :: forall extra_a3wDl. Lens' (RewriteState extra_a3wDl) (Id, SrcSpan)
- Clash.Rewrite.Types: evaluator :: Lens' RewriteEnv PrimEvaluator
+ Clash.Rewrite.Types: evaluator :: Lens' RewriteEnv (PrimStep, PrimUnwind)
- Clash.Rewrite.Types: extra :: forall extra_a3alq extra_a3atN. Lens (RewriteState extra_a3alq) (RewriteState extra_a3atN) extra_a3alq extra_a3atN
+ Clash.Rewrite.Types: extra :: forall extra_a3wDl extra_a3wLI. Lens (RewriteState extra_a3wDl) (RewriteState extra_a3wLI) extra_a3wDl extra_a3wLI
- Clash.Rewrite.Types: globalHeap :: forall extra_a3alq. Lens' (RewriteState extra_a3alq) GlobalHeap
+ Clash.Rewrite.Types: globalHeap :: forall extra_a3wDl. Lens' (RewriteState extra_a3wDl) PrimHeap
- Clash.Rewrite.Types: nameCounter :: forall extra_a3alq. Lens' (RewriteState extra_a3alq) Int
+ Clash.Rewrite.Types: nameCounter :: forall extra_a3wDl. Lens' (RewriteState extra_a3wDl) Int
- Clash.Rewrite.Types: transformCounter :: forall extra_a3alq. Lens' (RewriteState extra_a3alq) Int
+ Clash.Rewrite.Types: transformCounter :: forall extra_a3wDl. Lens' (RewriteState extra_a3wDl) Int
- Clash.Rewrite.Types: uniqSupply :: forall extra_a3alq. Lens' (RewriteState extra_a3alq) Supply
+ Clash.Rewrite.Types: uniqSupply :: forall extra_a3wDl. Lens' (RewriteState extra_a3wDl) Supply
- Clash.Rewrite.Util: applyDebug :: DebugLevel -> String -> Term -> Bool -> Term -> RewriteMonad extra Term
+ Clash.Rewrite.Util: applyDebug :: DebugLevel -> Set String -> String -> Term -> Bool -> Term -> RewriteMonad extra Term
- Clash.Rewrite.Util: cloneNameWithBindingMap :: (Monad m, MonadUnique m) => BindingMap -> Name a -> m (Name a)
+ Clash.Rewrite.Util: cloneNameWithBindingMap :: MonadUnique m => BindingMap -> Name a -> m (Name a)
- Clash.Rewrite.Util: cloneNameWithInScopeSet :: (Monad m, MonadUnique m) => InScopeSet -> Name a -> m (Name a)
+ Clash.Rewrite.Util: cloneNameWithInScopeSet :: MonadUnique m => InScopeSet -> Name a -> m (Name a)
- Clash.Rewrite.Util: inlineOrLiftBinders :: (LetBinding -> RewriteMonad extra Bool) -> (Term -> LetBinding -> RewriteMonad extra Bool) -> Rewrite extra
+ Clash.Rewrite.Util: inlineOrLiftBinders :: (LetBinding -> RewriteMonad extra Bool) -> (Term -> LetBinding -> Bool) -> Rewrite extra
- Clash.Rewrite.Util: mkBinderFor :: (Monad m, MonadUnique m, MonadFail m) => InScopeSet -> TyConMap -> Name a -> Either Term Type -> m (Either Id TyVar)
+ Clash.Rewrite.Util: mkBinderFor :: (MonadUnique m, MonadFail m) => InScopeSet -> TyConMap -> Name a -> Either Term Type -> m (Either Id TyVar)
- Clash.Rewrite.Util: mkInternalVar :: (Monad m, MonadUnique m) => InScopeSet -> OccName -> KindOrType -> m Id
+ Clash.Rewrite.Util: mkInternalVar :: MonadUnique m => InScopeSet -> OccName -> KindOrType -> m Id
- Clash.Rewrite.Util: mkSelectorCase :: HasCallStack => (Functor m, Monad m, MonadUnique m) => String -> InScopeSet -> TyConMap -> Term -> Int -> Int -> m Term
+ Clash.Rewrite.Util: mkSelectorCase :: HasCallStack => (Functor m, MonadUnique m) => String -> InScopeSet -> TyConMap -> Term -> Int -> Int -> m Term
- Clash.Rewrite.Util: mkTmBinderFor :: (Monad m, MonadUnique m, MonadFail m) => InScopeSet -> TyConMap -> Name a -> Term -> m Id
+ Clash.Rewrite.Util: mkTmBinderFor :: (MonadUnique m, MonadFail m) => InScopeSet -> TyConMap -> Name a -> Term -> m Id
- Clash.Rewrite.Util: mkWildValBinder :: (Monad m, MonadUnique m) => InScopeSet -> Type -> m Id
+ Clash.Rewrite.Util: mkWildValBinder :: MonadUnique m => InScopeSet -> Type -> m Id
- Clash.Rewrite.Util: substituteBinders :: InScopeSet -> [LetBinding] -> [LetBinding] -> Term -> ([LetBinding], Term)
+ Clash.Rewrite.Util: substituteBinders :: InScopeSet -> [LetBinding] -> [LetBinding] -> Term -> ([LetBinding], ([LetBinding], Term))
- Clash.Util: class MonadUnique m
+ Clash.Util: class Monad m => MonadUnique m
- Clash.Util: indexNote :: String -> [a] -> Int -> a
+ Clash.Util: indexNote :: HasCallStack => String -> [a] -> Int -> a
Files
- CHANGELOG.md +265/−379
- clash-lib.cabal +58/−21
- prims/common/Clash_Intel_ClockGen.json +12/−0
- prims/common/Clash_Magic.json +15/−0
- prims/common/Clash_XException.json +8/−0
- prims/common/GHC_Natural.json +7/−0
- prims/commonverilog/Clash_Explicit_SimIO.json +127/−0
- prims/commonverilog/Clash_Sized_Internal_BitVector.json +5/−7
- prims/commonverilog/Clash_Sized_Internal_Index.json +2/−4
- prims/commonverilog/Clash_Sized_Internal_Signed.json +2/−4
- prims/commonverilog/Clash_Sized_Internal_Unsigned.json +2/−4
- prims/systemverilog/Clash_Signal_BiSignal.json +1/−0
- prims/systemverilog/Clash_Signal_Internal.json +12/−12
- prims/systemverilog/Clash_Sized_Internal_Signed.json +19/−7
- prims/systemverilog/Clash_Sized_Vector.json +0/−52
- prims/systemverilog/GHC_Classes.json +18/−6
- prims/systemverilog/GHC_Integer_Type.json +70/−6
- prims/systemverilog/GHC_Prim.json +6/−6
- prims/verilog/Clash_Signal_BiSignal.json +1/−0
- prims/verilog/Clash_Signal_Internal.json +17/−13
- prims/verilog/Clash_Sized_Internal_Signed.json +19/−7
- prims/verilog/Clash_Sized_Vector.json +0/−75
- prims/verilog/GHC_Classes.json +18/−6
- prims/verilog/GHC_Integer_Type.json +72/−6
- prims/verilog/GHC_Prim.json +6/−6
- prims/vhdl/Clash_Explicit_BlockRam.json +6/−30
- prims/vhdl/Clash_Explicit_BlockRam_File.json +4/−20
- prims/vhdl/Clash_Explicit_ROM.json +2/−10
- prims/vhdl/Clash_Explicit_ROM_File.json +2/−10
- prims/vhdl/Clash_Explicit_SimIO.json +31/−0
- prims/vhdl/Clash_Signal_BiSignal.json +1/−0
- prims/vhdl/Clash_Signal_Internal.json +47/−99
- prims/vhdl/Clash_Sized_Internal_BitVector.json +2/−4
- prims/vhdl/Clash_Sized_Internal_Index.json +2/−4
- prims/vhdl/Clash_Sized_Internal_Signed.json +13/−8
- prims/vhdl/Clash_Sized_Internal_Unsigned.json +2/−4
- prims/vhdl/Clash_Sized_Vector.json +0/−46
- prims/vhdl/GHC_Classes.json +13/−6
- prims/vhdl/GHC_Integer_Type.json +43/−4
- prims/vhdl/GHC_Prim.json +4/−4
- src/Clash/Annotations/TopEntity/Extra.hs +4/−0
- src/Clash/Backend.hs +22/−0
- src/Clash/Backend/SystemVerilog.hs +363/−125
- src/Clash/Backend/VHDL.hs +176/−101
- src/Clash/Backend/Verilog.hs +214/−106
- src/Clash/Core/DataCon.hs +4/−5
- src/Clash/Core/Evaluator.hs +346/−490
- src/Clash/Core/Evaluator/Types.hs +249/−0
- src/Clash/Core/FreeVars.hs +13/−18
- src/Clash/Core/Literal.hs +2/−3
- src/Clash/Core/Name.hs +4/−5
- src/Clash/Core/Pretty.hs +25/−9
- src/Clash/Core/Subst.hs +73/−6
- src/Clash/Core/Term.hs +65/−16
- src/Clash/Core/TermLiteral.hs +96/−0
- src/Clash/Core/TermLiteral/TH.hs +93/−0
- src/Clash/Core/TyCon.hs +3/−4
- src/Clash/Core/Type.hs +79/−28
- src/Clash/Core/TysPrim.hs +1/−1
- src/Clash/Core/Util.hs +198/−33
- src/Clash/Core/Var.hs +4/−6
- src/Clash/Core/VarEnv.hs +29/−5
- src/Clash/Driver.hs +303/−63
- src/Clash/Driver/Types.hs +85/−12
- src/Clash/Netlist.hs +404/−219
- src/Clash/Netlist.hs-boot +10/−7
- src/Clash/Netlist/BlackBox.hs +485/−94
- src/Clash/Netlist/BlackBox.hs-boot +29/−0
- src/Clash/Netlist/BlackBox/Parser.hs +4/−3
- src/Clash/Netlist/BlackBox/Types.hs +43/−9
- src/Clash/Netlist/BlackBox/Util.hs +95/−63
- src/Clash/Netlist/BlackBox/Util.hs-boot +19/−0
- src/Clash/Netlist/Id.hs +1/−2
- src/Clash/Netlist/Types.hs +144/−20
- src/Clash/Netlist/Util.hs +280/−164
- src/Clash/Normalize.hs +108/−78
- src/Clash/Normalize/DEC.hs +27/−22
- src/Clash/Normalize/PrimitiveReductions.hs +30/−24
- src/Clash/Normalize/Strategy.hs +9/−4
- src/Clash/Normalize/Transformations.hs +2864/−2305
- src/Clash/Normalize/Types.hs +3/−1
- src/Clash/Normalize/Util.hs +123/−30
- src/Clash/Primitives/GHC/Int.hs +1/−2
- src/Clash/Primitives/Intel/ClockGen.hs +127/−17
- src/Clash/Primitives/Sized/ToInteger.hs +109/−0
- src/Clash/Primitives/Sized/Vector.hs +192/−29
- src/Clash/Primitives/Types.hs +58/−9
- src/Clash/Primitives/Util.hs +63/−14
- src/Clash/Rewrite/Combinators.hs +0/−4
- src/Clash/Rewrite/Types.hs +13/−10
- src/Clash/Rewrite/Util.hs +264/−102
- src/Clash/Unique.hs +11/−3
- src/Clash/Util.hs +102/−11
- src/Clash/Util/Graph.hs +1/−0
- src/Clash/Util/Interpolate.hs +369/−0
- src/Data/Aeson/Extra.hs +9/−1
- src/Data/Set/Ordered/Extra.hs +36/−0
- src/Data/Text/Prettyprint/Doc/Extra.hs +8/−1
- src/GHC/BasicTypes/Extra.hs +0/−3
- src/GHC/SrcLoc/Extra.hs +2/−4
- tests/Clash/Tests/Core/Subst.hs +53/−0
- tests/Clash/Tests/Util/Interpolate.hs +72/−0
- tests/unittests.hs +4/−0
CHANGELOG.md view
@@ -1,22 +1,155 @@-# Changelog for the [`clash-lib`](http://hackage.haskell.org/package/clash-lib) package+# Changelog for the Clash project +## 1.2.0 *March 5th 2020*+As promised when releasing 1.0, we've tried our best to keep the API stable. We+think most designs will continue to compile with this new version, although special+care needs to be taken when using:++ * Use inline blackboxes. Instead of taking a single HDL, inline primitives now+ take multiple. For example, `InlinePrimitive VHDL ".."` must now be written+ as `InlinePrimitive [VHDL] ".."`.++ * Use the `Enum` instance for `BitVector`, `Index`, `Signed`, or `Unsigned`, as+ they now respect their `maxBound`. See [#1089](https://github.com/clash-lang/clash-compiler/issues/1089).++On top of that, we've added a number of new features:++ * `makeTopEntity`: Template Haskell function for generating TopEntity annotations. See [the documentation on Haddock](http://hackage.haskell.org/package/clash-prelude-1.2.0/docs/Clash-Annotations-TopEntity.html) for more information.++ * `Clash.Explicit.SimIO`: ((System)Verilog only) I/O actions that can be translated to HDL I/O. See [the documentation on Haddock](http://hackage.haskell.org/package/clash-prelude-1.2.0/docs/Clash-Explicit-SimIO.html) for more information.++ * `Clash.Class.AutoReg`: A smart register that improves the chances of synthesis tools inferring clock-gated registers, when used. See [the documentation on Haddock](http://hackage.haskell.org/package/clash-prelude-1.2.0/docs/Clash-Class-AutoReg.html) for more information.++The full list of changes follows. Happy hacking!++* New features (API):+ * `Clash.Class.Parity` type class replaces Prelude `odd` and `even` functions due to assumptions that don't hold for Clash specific numerical types, see [#970](https://github.com/clash-lang/clash-compiler/pull/970).+ * `NFDataX.ensureSpine`, see [#748](https://github.com/clash-lang/clash-compiler/pull/803)+ * `makeTopEntity` Template Haskell function for generating TopEntity annotations+ intended to cover the majority of use cases. Generation failures should either+ result in an explicit error, or a valid annotation of an empty `PortProduct`.+ Any discrepancy between the _shape_ of generated annotations and the _shape_+ of the Clash compiler is a bug. See [#795](https://github.com/clash-lang/clash-compiler/pull/795).+ Known limitations:+ * Type application (excluding `Signal`s and `:::`) is best effort:+ * Data types with type parameters will work if the generator can discover a single relevant constructor after attempting type application.+ * Arbitrary explicit clock/reset/enables are supported, but only a single `HiddenClockResetEnable` constraint is supported.+ * Data/type family support is best effort.+ * Added `Bundle ((f :*: g) a)` instance+ * Added `NFDataX CUShort` instance+ * Clash's internal type family solver now recognizes `AppendSymbol` and `CmpSymbol`+ * Added `Clash.Magic.suffixNameFromNat`: can be used in cases where `suffixName` is too slow+ * Added `Clash.Class.AutoReg`. Improves the chances of synthesis tools inferring clock-gated registers, when used. See [#873](https://github.com/clash-lang/clash-compiler/pull/873).+ * `Clash.Magic.suffixNameP`, `Clash.Magic.suffixNameFromNatP`: enable prefixing of name suffixes+ * Added `Clash.Magic.noDeDup`: can be used to instruct Clash to /not/ share a function between multiple branches+ * A `BitPack a` constraint now implies a `KnownNat (BitSize a)` constraint, so you won't have to add it manually anymore. See [#942](https://github.com/clash-lang/clash-compiler/pull/942).+ * `Clash.Explicit.SimIO`: ((System)Verilog only) I/O actions that can be translated to HDL I/O; useful for generated test benches.+ * Export `Clash.Explicit.Testbench.assertBitVector` [#888](https://github.com/clash-lang/clash-compiler/pull/888/files)+ * Add `Clash.Prelude.Testbench.assertBitVector` to achieve feature parity with `Clash.Explicit.Testbench`. [#891](https://github.com/clash-lang/clash-compiler/pull/891/files)+ * Add `Clash.XException.NFDataX.ensureSpine` [#803](https://github.com/clash-lang/clash-compiler/pull/803)+ * Add `Clash.Class.BitPack.bitCoerceMap` [#798](https://github.com/clash-lang/clash-compiler/pull/798)+ * Add `Clash.Magic.deDup`: instruct Clash to force sharing an operator between multiple branches of a case-expression+ * `InlinePrimitive` can now support multiple backends simultaneously [#425](https://github.com/clash-lang/clash-compiler/issues/425)+ * Add `Clash.XException.hwSeqX`: render declarations of an argument, but don't assign it to a result signal+ * Add `Clash.Signal.Bundle.TaggedEmptyTuple`: allows users to emulate the pre-1.0 behavior of "Bundle ()". See [#1100](https://github.com/clash-lang/clash-compiler/pull/1100)++* New features (Compiler):+ * [#961](https://github.com/clash-lang/clash-compiler/pull/961): Show `-fclash-*` Options in `clash --show-options`++* New internal features:+ * [#918](https://github.com/clash-lang/clash-compiler/pull/935): Add X-Optimization to normalization passes (-fclash-aggressive-x-optimization)+ * [#821](https://github.com/clash-lang/clash-compiler/pull/821): Add `DebugTry`: print name of all tried transformations, even if they didn't succeed+ * [#856](https://github.com/clash-lang/clash-compiler/pull/856): Add `-fclash-debug-transformations`: only print debug info for specific transformations+ * [#911](https://github.com/clash-lang/clash-compiler/pull/911): Add 'RenderVoid' option to blackboxes+ * [#958](https://github.com/clash-lang/clash-compiler/pull/958): Prefix names of inlined functions+ * [#947](https://github.com/clash-lang/clash-compiler/pull/947): Add "Clash.Core.TermLiteral"+ * [#887](https://github.com/clash-lang/clash-compiler/pull/887): Show nicer error messages when failing in TH code+ * [#884](https://github.com/clash-lang/clash-compiler/pull/884): Teach reduceTypeFamily about AppendSymbol and CmpSymbol+ * [#784](https://github.com/clash-lang/clash-compiler/pull/784): Print whether `Id` is global or local in ppr output+ * [#781](https://github.com/clash-lang/clash-compiler/pull/781): Use naming contexts in register names+ * [#1061](https://github.com/clash-lang/clash-compiler/pull/1061): Add 'usedArguments' to BlackBoxHaskell blackboxes++* Fixes issues:+ * [#974](https://github.com/clash-lang/clash-compiler/issues/974): Fix indirect shadowing in `reduceNonRepPrim`+ * [#964](https://github.com/clash-lang/clash-compiler/issues/964): SaturatingNum instance of `Index` now behaves correctly when the size of the index overflows+ an `Int`.+ * [#810](https://github.com/clash-lang/clash-compiler/issues/810): Verilog backend now correctly specifies type of `BitVector 1`+ * [#811](https://github.com/clash-lang/clash-compiler/issues/811): Improve module load behavior in clashi+ * [#439](https://github.com/clash-lang/clash-compiler/issues/439): Template Haskell splices and TopEntity annotations can now be used in clashi+ * [#662](https://github.com/clash-lang/clash-compiler/issues/662): Clash will now constant specialize partially constant constructs+ * [#700](https://github.com/clash-lang/clash-compiler/issues/700): Check work content of expression in cast before warning users. Should eliminate a lot of (superfluous) warnings about "specializing on non work-free cast"s.+ * [#837](https://github.com/clash-lang/clash-compiler/issues/837): Blackboxes will now report clearer error messages if they're given unexpected arguments.+ * [#869](https://github.com/clash-lang/clash-compiler/issues/869): PLL is no longer duplicated in Blinker.hs example+ * [#749](https://github.com/clash-lang/clash-compiler/issues/749): Clash's dependencies now all work with GHC 8.8, allowing `clash-{prelude,lib,ghc}` to be compiled from Hackage soon.+ * [#871](https://github.com/clash-lang/clash-compiler/issues/871): RTree Bundle instance is now properly lazy+ * [#895](https://github.com/clash-lang/clash-compiler/issues/895): VHDL type error when generating `Maybe (Vec 2 (Signed 8), Index 1)`+ * [#880](https://github.com/clash-lang/clash-compiler/issues/880): Custom bit representations can now be used on product types too+ * [#976](https://github.com/clash-lang/clash-compiler/issues/976): Prevent shadowing in Clash's core evaluator+ * [#1007](https://github.com/clash-lang/clash-compiler/issues/1007): Can't translate domain tagType.Errors.IfStuck...+ * [#967](https://github.com/clash-lang/clash-compiler/issues/967): Naming registers disconnects their output+ * [#990](https://github.com/clash-lang/clash-compiler/issues/990): Internal shadowing bug results in incorrect HDL+ * [#945](https://github.com/clash-lang/clash-compiler/issues/945): Rewrite rules for Vec Applicative Functor+ * [#919](https://github.com/clash-lang/clash-compiler/issues/919): Clash generating invalid Verilog after Vec operations #919+ * [#996](https://github.com/clash-lang/clash-compiler/issues/996): Ambiguous clock when using `ClearOnReset` and `resetGen` together+ * [#701](https://github.com/clash-lang/clash-compiler/issues/701): Unexpected behaviour with the `Synthesize` annotation+ * [#694](https://github.com/clash-lang/clash-compiler/issues/694): Custom bit representation error only with VHDL+ * [#347](https://github.com/clash-lang/clash-compiler/issues/347): topEntity synthesis fails due to insufficient type-level normalisation+ * [#626](https://github.com/clash-lang/clash-compiler/issues/626): Missing Clash.Explicit.Prelude definitions+ * [#960](https://github.com/clash-lang/clash-compiler/issues/626): Blackbox Error Caused by Simple map+ * [#1012](https://github.com/clash-lang/clash-compiler/issues/1012): Case-let doesn't look through ticks+ * [#430](https://github.com/clash-lang/clash-compiler/issues/430): Issue warning when not compiled with `executable-dynamic: True`+ * [#374](https://github.com/clash-lang/clash-compiler/issues/1012): Clash.Sized.Fixed: fromInteger and fromRational don't saturate correctly+ * [#836](https://github.com/clash-lang/clash-compiler/issues/836): Generate warning when `toInteger` blackbox drops MSBs+ * [#1019](https://github.com/clash-lang/clash-compiler/issues/1019): Clash breaks on constants defined in terms of `GHC.Natural.gcdNatural`+ * [#1025](https://github.com/clash-lang/clash-compiler/issues/1025): `inlineCleanup`will not produce empty letrecs anymore+ * [#1030](https://github.com/clash-lang/clash-compiler/issues/1030): `bindConstantVar` will bind (workfree) constructs+ * [#1034](https://github.com/clash-lang/clash-compiler/issues/1034): Error (10137): object "pllLock" on lhs must have a variable data type+ * [#1046](https://github.com/clash-lang/clash-compiler/issues/1046): Don't confuse term/type namespaces in 'lookupIdSubst'+ * [#1041](https://github.com/clash-lang/clash-compiler/issues/1041): Nested product types incorrectly decomposed into ports+ * [#1058](https://github.com/clash-lang/clash-compiler/issues/1058): Prevent substitution warning when using type equalities in top entities+ * [#1033](https://github.com/clash-lang/clash-compiler/issues/1033): Fix issue where Clash breaks when using Clock/Reset/Enable in product types in combination with Synthesize annotations+ * [#1075](https://github.com/clash-lang/clash-compiler/issues/1075): Removed superfluous constraints on 'maybeX' and 'maybeIsX'+ * [#1085](https://github.com/clash-lang/clash-compiler/issues/1085): Suggest exporting topentities if they can't be found in a module+ * [#1065](https://github.com/clash-lang/clash-compiler/pull/1065): Report polymorphic topEntities as errors+ * [#1089](https://github.com/clash-lang/clash-compiler/issues/1089): Respect maxBound in Enum instances for BitVector,Index,Signed,Unsigned++* Fixes without issue reports:+ * Fix bug in `rnfX` defined for `Down` ([baef30e](https://github.com/clash-lang/clash-compiler/commit/baef30eae03dc02ba847ffbb8fae7f365c5287c2))+ * Render numbers inside gensym ([bc76f0f](https://github.com/clash-lang/clash-compiler/commit/bc76f0f1934fd6e6ed9c33bcf950dae21e2f7903))+ * Report blackbox name when encountering an error in 'setSym' ([#858](https://github.com/clash-lang/clash-compiler/pull/858))+ * Fix blackbox issues causing Clash to generate invalid HDL ([#865](https://github.com/clash-lang/clash-compiler/pull/865))+ * Treat types with a zero-width custom bit representation like other zero-width constructs ([#874](https://github.com/clash-lang/clash-compiler/pull/874))+ * TH code for auto deriving bit representations now produces nicer error messages ([7190793](https://github.com/clash-lang/clash-compiler/commit/7190793928545f85157f9b8d4b8ec2edb2cd8a26))+ * Adds '--enable-shared-executables' for nix builds; this should make Clash run _much_ faster ([#894](https://github.com/clash-lang/clash-compiler/pull/894))+ * Custom bit representations can now mark fields as zero-width without crashing the compiler ([#898](https://github.com/clash-lang/clash-compiler/pull/898))+ * Throw an error if there's data left to parse after successfully parsing a valid JSON construct ([#904](https://github.com/clash-lang/clash-compiler/pull/904))+ * `Data.gfoldl` is now manually implemented, in turn fixing issues with `gshow` ([#933](https://github.com/clash-lang/clash-compiler/pull/933))+ * Fix a number of issues with blackbox implementations ([#934](https://github.com/clash-lang/clash-compiler/pull/934))+ * Don't inline registers with non-constant clock and reset ([#998](https://github.com/clash-lang/clash-compiler/pull/998))+ * Inline let-binders called [dsN | N <- [1..]] ([#992](https://github.com/clash-lang/clash-compiler/pull/992))+ * ClockGens use their name at the Haskell level [#827](https://github.com/clash-lang/clash-compiler/pull/827)+ * Render numbers inside gensym [#809](https://github.com/clash-lang/clash-compiler/pull/809)+ * Don't overwrite existing binders when specializing [#790](https://github.com/clash-lang/clash-compiler/pull/790)+ * Deshadow in 'caseCase' [#1067](https://github.com/clash-lang/clash-compiler/pull/1067)+ * Deshadow in 'caseLet' and 'nonRepANF' [#1071](https://github.com/clash-lang/clash-compiler/pull/1071)++* Deprecations & removals:+ * Removed support for GHC 8.2 ([#842](https://github.com/clash-lang/clash-compiler/pull/842))+ * Removed support for older cabal versions, only Cabal >=2.2 supported ([#851](https://github.com/clash-lang/clash-compiler/pull/851))+ * Reset and Enable constructors are now only exported from Clash.Signal.Internal+ * [#986](https://github.com/clash-lang/clash-compiler/issues/986) Remove -fclash-allow-zero-width flag+ ## 1.0.0 *September 3rd 2019* * 10x - 50x faster compile times * New features: * API changes: check the migration guide at the end of `Clash.Tutorial`- * All memory elements now have an (implicit) enable line; "Gated" clocks have- been removed as the clock wasn't actually gated, but implemented as an- enable line.+ * All memory elements now have an (implicit) enable line; "Gated" clocks have been removed as the clock wasn't actually gated, but implemented as an enable line. * Circuit domains are now configurable in: * (old) The clock period- * (new) Clock edge on which memory elements latch their inputs- (rising edge or falling edge)- * (new) Whether the reset port of a memory element is level sensitive- (asynchronous reset) or edge sensitive (synchronous reset)- * (new) Whether the reset port of a memory element is active-high or- active-low (negated reset)- * (new) Whether memory element power on in a configurable/defined state- (common on FPGAs) or in an undefined state (ASICs)+ * (new) Clock edge on which memory elements latch their inputs (rising edge or falling edge)+ * (new) Whether the reset port of a memory element is level sensitive asynchronous reset) or edge sensitive (synchronous reset)+ * (new) Whether the reset port of a memory element is active-high or active-low (negated reset)+ * (new) Whether memory element power on in a configurable/defined state (common on FPGAs) or in an undefined state (ASICs) * See the [blog post](https://clash-lang.org/blog/0005-synthesis-domain/) on this new feature * Data types can now be given custom bit-representations: http://hackage.haskell.org/package/clash-prelude/docs/Clash-Annotations-BitRepresentation.html@@ -36,95 +169,95 @@ See for example: http://hackage.haskell.org/package/clash-lib/docs/Clash-Primitives-Intel-ClockGen.html * Fixes issues:- * [#316](https://github.com/clash-lang/clash-prelude/issues/316)- * [#319](https://github.com/clash-lang/clash-prelude/issues/319)- * [#323](https://github.com/clash-lang/clash-prelude/issues/323)- * [#324](https://github.com/clash-lang/clash-prelude/issues/324)- * [#329](https://github.com/clash-lang/clash-prelude/issues/329)- * [#331](https://github.com/clash-lang/clash-prelude/issues/331)- * [#332](https://github.com/clash-lang/clash-prelude/issues/332)- * [#335](https://github.com/clash-lang/clash-prelude/issues/335)- * [#348](https://github.com/clash-lang/clash-prelude/issues/348)- * [#349](https://github.com/clash-lang/clash-prelude/issues/349)- * [#350](https://github.com/clash-lang/clash-prelude/issues/350)- * [#351](https://github.com/clash-lang/clash-prelude/issues/351)- * [#352](https://github.com/clash-lang/clash-prelude/issues/352)- * [#353](https://github.com/clash-lang/clash-prelude/issues/353)- * [#358](https://github.com/clash-lang/clash-prelude/issues/358)- * [#359](https://github.com/clash-lang/clash-prelude/issues/359)- * [#363](https://github.com/clash-lang/clash-prelude/issues/363)- * [#364](https://github.com/clash-lang/clash-prelude/issues/364)- * [#365](https://github.com/clash-lang/clash-prelude/issues/365)- * [#371](https://github.com/clash-lang/clash-prelude/issues/371)- * [#372](https://github.com/clash-lang/clash-prelude/issues/372)- * [#373](https://github.com/clash-lang/clash-prelude/issues/373)- * [#378](https://github.com/clash-lang/clash-prelude/issues/378)- * [#380](https://github.com/clash-lang/clash-prelude/issues/380)- * [#381](https://github.com/clash-lang/clash-prelude/issues/381)- * [#382](https://github.com/clash-lang/clash-prelude/issues/382)- * [#383](https://github.com/clash-lang/clash-prelude/issues/383)- * [#387](https://github.com/clash-lang/clash-prelude/issues/387)- * [#393](https://github.com/clash-lang/clash-prelude/issues/393)- * [#396](https://github.com/clash-lang/clash-prelude/issues/396)- * [#398](https://github.com/clash-lang/clash-prelude/issues/398)- * [#399](https://github.com/clash-lang/clash-prelude/issues/399)- * [#401](https://github.com/clash-lang/clash-prelude/issues/401)- * [#403](https://github.com/clash-lang/clash-prelude/issues/403)- * [#407](https://github.com/clash-lang/clash-prelude/issues/407)- * [#412](https://github.com/clash-lang/clash-prelude/issues/412)- * [#413](https://github.com/clash-lang/clash-prelude/issues/413)- * [#420](https://github.com/clash-lang/clash-prelude/issues/420)- * [#422](https://github.com/clash-lang/clash-prelude/issues/422)- * [#423](https://github.com/clash-lang/clash-prelude/issues/423)- * [#424](https://github.com/clash-lang/clash-prelude/issues/424)- * [#438](https://github.com/clash-lang/clash-prelude/issues/438)- * [#450](https://github.com/clash-lang/clash-prelude/issues/450)- * [#452](https://github.com/clash-lang/clash-prelude/issues/452)- * [#455](https://github.com/clash-lang/clash-prelude/issues/455)- * [#460](https://github.com/clash-lang/clash-prelude/issues/460)- * [#461](https://github.com/clash-lang/clash-prelude/issues/461)- * [#463](https://github.com/clash-lang/clash-prelude/issues/463)- * [#468](https://github.com/clash-lang/clash-prelude/issues/468)- * [#475](https://github.com/clash-lang/clash-prelude/issues/475)- * [#476](https://github.com/clash-lang/clash-prelude/issues/476)- * [#500](https://github.com/clash-lang/clash-prelude/issues/500)- * [#507](https://github.com/clash-lang/clash-prelude/issues/507)- * [#512](https://github.com/clash-lang/clash-prelude/issues/512)- * [#516](https://github.com/clash-lang/clash-prelude/issues/516)- * [#517](https://github.com/clash-lang/clash-prelude/issues/517)- * [#526](https://github.com/clash-lang/clash-prelude/issues/526)- * [#556](https://github.com/clash-lang/clash-prelude/issues/556)- * [#560](https://github.com/clash-lang/clash-prelude/issues/560)- * [#566](https://github.com/clash-lang/clash-prelude/issues/566)- * [#567](https://github.com/clash-lang/clash-prelude/issues/567)- * [#569](https://github.com/clash-lang/clash-prelude/issues/569)- * [#573](https://github.com/clash-lang/clash-prelude/issues/573)- * [#575](https://github.com/clash-lang/clash-prelude/issues/575)- * [#581](https://github.com/clash-lang/clash-prelude/issues/581)- * [#582](https://github.com/clash-lang/clash-prelude/issues/582)- * [#586](https://github.com/clash-lang/clash-prelude/issues/586)- * [#588](https://github.com/clash-lang/clash-prelude/issues/588)- * [#591](https://github.com/clash-lang/clash-prelude/issues/591)- * [#596](https://github.com/clash-lang/clash-prelude/issues/596)- * [#601](https://github.com/clash-lang/clash-prelude/issues/601)- * [#607](https://github.com/clash-lang/clash-prelude/issues/607)- * [#629](https://github.com/clash-lang/clash-prelude/issues/629)- * [#637](https://github.com/clash-lang/clash-prelude/issues/637)- * [#644](https://github.com/clash-lang/clash-prelude/issues/644)- * [#647](https://github.com/clash-lang/clash-prelude/issues/647)- * [#661](https://github.com/clash-lang/clash-prelude/issues/661)- * [#668](https://github.com/clash-lang/clash-prelude/issues/668)- * [#677](https://github.com/clash-lang/clash-prelude/issues/677)- * [#678](https://github.com/clash-lang/clash-prelude/issues/678)- * [#682](https://github.com/clash-lang/clash-prelude/issues/682)- * [#691](https://github.com/clash-lang/clash-prelude/issues/691)- * [#703](https://github.com/clash-lang/clash-prelude/issues/703)- * [#713](https://github.com/clash-lang/clash-prelude/issues/713)- * [#715](https://github.com/clash-lang/clash-prelude/issues/715)- * [#727](https://github.com/clash-lang/clash-prelude/issues/727)- * [#730](https://github.com/clash-lang/clash-prelude/issues/730)- * [#736](https://github.com/clash-lang/clash-prelude/issues/736)- * [#738](https://github.com/clash-lang/clash-prelude/issues/738)+ * [#316](https://github.com/clash-lang/clash-compiler/issues/316)+ * [#319](https://github.com/clash-lang/clash-compiler/issues/319)+ * [#323](https://github.com/clash-lang/clash-compiler/issues/323)+ * [#324](https://github.com/clash-lang/clash-compiler/issues/324)+ * [#329](https://github.com/clash-lang/clash-compiler/issues/329)+ * [#331](https://github.com/clash-lang/clash-compiler/issues/331)+ * [#332](https://github.com/clash-lang/clash-compiler/issues/332)+ * [#335](https://github.com/clash-lang/clash-compiler/issues/335)+ * [#348](https://github.com/clash-lang/clash-compiler/issues/348)+ * [#349](https://github.com/clash-lang/clash-compiler/issues/349)+ * [#350](https://github.com/clash-lang/clash-compiler/issues/350)+ * [#351](https://github.com/clash-lang/clash-compiler/issues/351)+ * [#352](https://github.com/clash-lang/clash-compiler/issues/352)+ * [#353](https://github.com/clash-lang/clash-compiler/issues/353)+ * [#358](https://github.com/clash-lang/clash-compiler/issues/358)+ * [#359](https://github.com/clash-lang/clash-compiler/issues/359)+ * [#363](https://github.com/clash-lang/clash-compiler/issues/363)+ * [#364](https://github.com/clash-lang/clash-compiler/issues/364)+ * [#365](https://github.com/clash-lang/clash-compiler/issues/365)+ * [#371](https://github.com/clash-lang/clash-compiler/issues/371)+ * [#372](https://github.com/clash-lang/clash-compiler/issues/372)+ * [#373](https://github.com/clash-lang/clash-compiler/issues/373)+ * [#378](https://github.com/clash-lang/clash-compiler/issues/378)+ * [#380](https://github.com/clash-lang/clash-compiler/issues/380)+ * [#381](https://github.com/clash-lang/clash-compiler/issues/381)+ * [#382](https://github.com/clash-lang/clash-compiler/issues/382)+ * [#383](https://github.com/clash-lang/clash-compiler/issues/383)+ * [#387](https://github.com/clash-lang/clash-compiler/issues/387)+ * [#393](https://github.com/clash-lang/clash-compiler/issues/393)+ * [#396](https://github.com/clash-lang/clash-compiler/issues/396)+ * [#398](https://github.com/clash-lang/clash-compiler/issues/398)+ * [#399](https://github.com/clash-lang/clash-compiler/issues/399)+ * [#401](https://github.com/clash-lang/clash-compiler/issues/401)+ * [#403](https://github.com/clash-lang/clash-compiler/issues/403)+ * [#407](https://github.com/clash-lang/clash-compiler/issues/407)+ * [#412](https://github.com/clash-lang/clash-compiler/issues/412)+ * [#413](https://github.com/clash-lang/clash-compiler/issues/413)+ * [#420](https://github.com/clash-lang/clash-compiler/issues/420)+ * [#422](https://github.com/clash-lang/clash-compiler/issues/422)+ * [#423](https://github.com/clash-lang/clash-compiler/issues/423)+ * [#424](https://github.com/clash-lang/clash-compiler/issues/424)+ * [#438](https://github.com/clash-lang/clash-compiler/issues/438)+ * [#450](https://github.com/clash-lang/clash-compiler/issues/450)+ * [#452](https://github.com/clash-lang/clash-compiler/issues/452)+ * [#455](https://github.com/clash-lang/clash-compiler/issues/455)+ * [#460](https://github.com/clash-lang/clash-compiler/issues/460)+ * [#461](https://github.com/clash-lang/clash-compiler/issues/461)+ * [#463](https://github.com/clash-lang/clash-compiler/issues/463)+ * [#468](https://github.com/clash-lang/clash-compiler/issues/468)+ * [#475](https://github.com/clash-lang/clash-compiler/issues/475)+ * [#476](https://github.com/clash-lang/clash-compiler/issues/476)+ * [#500](https://github.com/clash-lang/clash-compiler/issues/500)+ * [#507](https://github.com/clash-lang/clash-compiler/issues/507)+ * [#512](https://github.com/clash-lang/clash-compiler/issues/512)+ * [#516](https://github.com/clash-lang/clash-compiler/issues/516)+ * [#517](https://github.com/clash-lang/clash-compiler/issues/517)+ * [#526](https://github.com/clash-lang/clash-compiler/issues/526)+ * [#556](https://github.com/clash-lang/clash-compiler/issues/556)+ * [#560](https://github.com/clash-lang/clash-compiler/issues/560)+ * [#566](https://github.com/clash-lang/clash-compiler/issues/566)+ * [#567](https://github.com/clash-lang/clash-compiler/issues/567)+ * [#569](https://github.com/clash-lang/clash-compiler/issues/569)+ * [#573](https://github.com/clash-lang/clash-compiler/issues/573)+ * [#575](https://github.com/clash-lang/clash-compiler/issues/575)+ * [#581](https://github.com/clash-lang/clash-compiler/issues/581)+ * [#582](https://github.com/clash-lang/clash-compiler/issues/582)+ * [#586](https://github.com/clash-lang/clash-compiler/issues/586)+ * [#588](https://github.com/clash-lang/clash-compiler/issues/588)+ * [#591](https://github.com/clash-lang/clash-compiler/issues/591)+ * [#596](https://github.com/clash-lang/clash-compiler/issues/596)+ * [#601](https://github.com/clash-lang/clash-compiler/issues/601)+ * [#607](https://github.com/clash-lang/clash-compiler/issues/607)+ * [#629](https://github.com/clash-lang/clash-compiler/issues/629)+ * [#637](https://github.com/clash-lang/clash-compiler/issues/637)+ * [#644](https://github.com/clash-lang/clash-compiler/issues/644)+ * [#647](https://github.com/clash-lang/clash-compiler/issues/647)+ * [#661](https://github.com/clash-lang/clash-compiler/issues/661)+ * [#668](https://github.com/clash-lang/clash-compiler/issues/668)+ * [#677](https://github.com/clash-lang/clash-compiler/issues/677)+ * [#678](https://github.com/clash-lang/clash-compiler/issues/678)+ * [#682](https://github.com/clash-lang/clash-compiler/issues/682)+ * [#691](https://github.com/clash-lang/clash-compiler/issues/691)+ * [#703](https://github.com/clash-lang/clash-compiler/issues/703)+ * [#713](https://github.com/clash-lang/clash-compiler/issues/713)+ * [#715](https://github.com/clash-lang/clash-compiler/issues/715)+ * [#727](https://github.com/clash-lang/clash-compiler/issues/727)+ * [#730](https://github.com/clash-lang/clash-compiler/issues/730)+ * [#736](https://github.com/clash-lang/clash-compiler/issues/736)+ * [#738](https://github.com/clash-lang/clash-compiler/issues/738) ## 0.99.3 *July 28th 2018* * Fixes bugs:@@ -136,15 +269,38 @@ * Create empty component when result needs 0 bits * Evaluator performs BigNat arithmetic +* Features:+ * Bundle and BitPack instances up to and including 62-tuples+ * Handle undefined writes to RAM properly+ * Handle undefined clock enables properly++ ## 0.99.1 *May 12th 2018* * Allow `~NAME[N]` tag inside `~GENSYM[X]` * Support HDL record selector generation [#313](https://github.com/clash-lang/clash-compiler/pull/313)+* `InlinePrimitive` support: specify HDL primitives inline with Haskell code+* Support for `ghc-typelits-natnormalise-0.6.1`+* `Lift` instances for `TopEntity` and `PortName`+* `InlinePrimitive` support: specify HDL primitives inline with Haskell code ## 0.99 *March 31st 2018* * New features:- * Support for `clash-prelude-0.99`:- * Explicit clock and reset arguments- * Overhaul of `TopEntity` annotations+ * Major API overhaul: check the migration guide at the end of `Clash.Tutorial`+ * New features:+ * Explicit clock and reset arguments+ * Rename `CLaSH` to `Clash`+ * Implicit/`Hidden` clock and reset arguments using a combination of+ `reflection` and `ImplicitParams`.+ * Large overhaul of `TopEntity` annotations+ * PLL and other clock sources can now be instantiated using regular functions:+ `Clash.Intel.ClockGen` and `Clash.Xilinx.ClockGen`.+ * DDR registers:+ * Generic/ASIC: `Clash.Explicit.DDR`+ * Intel: `Clash.Intel.DDR`+ * Xilinx: `Clash.Intel.Xilinx`+ * `Bit` is now a `newtype` instead of a `type` synonym and will be mapped to+ a HDL scalar instead of an array of one (e.g `std_logic` instead of+ `std_logic_vector(0 downto 0)`) * Hierarchies with multiple synthesisable boundaries by allowing more than one function in scope to have a `Synthesize` annotation. * Local caching of functions with a `Synthesize` annotation@@ -154,280 +310,10 @@ * Improved compile-time computation * Many bug fixes -## 0.7.1 *April 11th 2017*-* New features:- * Support distribution of primitive templates with Cabal/Hackage packages [commit](https://github.com/clash-lang/clash-compiler/commit/82cd31863aafcbaf3bdbf7746d89d13859af5aaf)- * Find memory data files and primitive files relative to import dirs (`-i<DIR>`)-* Fixes bugs:- * `case (EmptyCase ty) of ty' { ... }` -> `EmptyCase ty'` [#198](https://github.com/clash-lang/clash-compiler/issues/198)- * `BitVector.split#` apply the correct type arguments+## Older versions+Check out:+ * https://github.com/clash-lang/clash-compiler/blob/3649a2962415ea8ca2d6f7f5e673b4c14de26b4f/clash-prelude/CHANGELOG.md+ * https://github.com/clash-lang/clash-compiler/blob/3649a2962415ea8ca2d6f7f5e673b4c14de26b4f/clash-lib/CHANGELOG.md+ * https://github.com/clash-lang/clash-compiler/blob/3649a2962415ea8ca2d6f7f5e673b4c14de26b4f/clash-ghc/CHANGELOG.md -## 0.7 *January 16th 2017*-* New features:- * Support for `clash-prelude` 0.11- * Primitive templates can include QSys files- * VHDL blackboxes: support additional libraries and uses keywords in generated VHDL- * Highly limited Float/Double support (literals and `Rational` conversion), hidden behind the `-clash-float-support` flag.-* Fixes bugs:- * Reduce type families inside clock period calculation [#180](https://github.com/clash-lang/clash-compiler/issues/180)- * Only output signed literals as hex when they're multiple of 4 bits [#187](https://github.com/clash-lang/clash-compiler/issues/187)- * Correctly print negative hex literals -## 0.6.21 *August 18th 2016*-* Fixes bugs:- * Type families are not currently being reduced correctly [#167](https://github.com/clash-lang/clash-compiler/issues/167)--## 0.6.20 *August 3rd 2016*-* Fixes bugs:- * Bug in DEC transformation overwrites case-alternatives- * Bug in DEC transformation creates non-representable let-binders--## 0.6.19 *July 19th 2016*-* Fixes bugs:- * Rounding error in `logBase` calculation--## 0.6.18 *July 15th 2016*-* New features:- * Better error location reporting-* Fixes bugs:- * Values of type Index 'n', where 'n' > 2^MACHINE_WIDTH, incorrectly considered non-synthesisable due to overflow--## 0.6.17 *June 9th 2016*-* Fixes bugs:- * `Eq` instance of `Vec` sometimes not synthesisable--## 0.6.16 *June 7th 2016*-* New features:- * DEC transformation also lifts HO-primitives applied to "interesting" primitives (i.e. `zipWith (*)`)-* Fixes bugs:- * replicate unfolded incorrectly [#150](https://github.com/clash-lang/clash-compiler/issues/150)- * `imap` is not unrolled [#151](https://github.com/clash-lang/clash-compiler/issues/151)--## 0.6.15 *April 7th 2016*-* New features:- * Up to 2x reduced compilation times when working with large `Vec` literals-* Fixes bugs:- * Bug in DEC transformation throws CLaSH into an endless loop [#140](https://github.com/clash-lang/clash-compiler/issues/140)--## 0.6.14 *March 21st 2016*-* New features:- * Also generate testbench for circuits without input ports [#135](https://github.com/clash-lang/clash-compiler/issues/135)-* Fixes bugs:- * `clockWizard` broken [#49](https://github.com/clash-lang/clash-prelude/issues/49)--## 0.6.13 *March 14th 2016*-* Fixes bugs:- * Not all lambda's in a function position removed--## 0.6.12 *March 14th 2016*-* Fixes bugs:- * Not all lambda's in a function position removed due to bad eta-expansion [#132](https://github.com/clash-lang/clash-compiler/issues/132)--## 0.6.11 *March 11th 2016*-* New features:- * Add support for HDL synthesis tool specific HDL generation- * Preserve more Haskell names in generated HDL [#128](https://github.com/clash-lang/clash-compiler/issues/128)--## 0.6.10 *February 10th 2016*-* New features:- * hdl files can be written to a directory other than the current working directory [#125](https://github.com/clash-lang/clash-compiler/issues/125)-* Fixes bugs:- * `caseCon` transformation does not work on non-exhaustive case-expressions [#123](https://github.com/clash-lang/clash-compiler/issues/123)- * Primitive reductions don't look through `Signal` [#126](https://github.com/clash-lang/clash-compiler/issues/126)--## 0.6.9-* Fixes bugs:- * `case undefined of ...` should reduce to `undefined` [#116](https://github.com/clash-lang/clash-compiler/issues/109)- * Type families obscure eligibility for synthesis [#114](https://github.com/clash-lang/clash-compiler/issues/114)--## 0.6.8 *January 13th 2015*-* New features:- * Support for Haskell's: `Char`, `Int8`, `Int16`, `Int32`, `Int64`, `Word`, `Word8`, `Word16`, `Word32`, `Word64`.- * Int/Word/Integer bitwidth for generated HDL is configurable using the `-clash-intwidth=N` flag, where `N` can be either 32 or 64.-* Fixes bugs:- * Cannot reduce `case error ... of ...` to `error ...` [#109](https://github.com/clash-lang/clash-compiler/issues/109)--## 0.6.7 *December 21st 2015*-* Support for `unbound-generics-0.3`-* New features:- * Only look for 'topEntity' in the root module. [#22](https://github.com/clash-lang/clash-compiler/issues/22)--## 0.6.6 *December 11th 2015*-* New features:- * Remove all existing HDL files before generating new ones. This can be disabled by the `-clash-noclean` flag. [#96](https://github.com/clash-lang/clash-compiler/issues/96)- * Support for `clash-prelude` 0.10.4--## 0.6.5 *November 17th 2015*-* Fixes bugs:- * Integer literals used as arguments not always properly annotated with their type.--## 0.6.4 *November 12th 2015*-* Fixes bugs:- * Reversing alternatives is not meaning preserving for literal patterns [#91](https://github.com/clash-lang/clash-compiler/issues/91)- * DEC: root of the case-tree must contain at least 2 alternatives [#92](https://github.com/clash-lang/clash-compiler/issues/92)--## 0.6.3 *October 24th 2015*-* New features:- * Improve DEC transformation: consider alternatives before the subject when checking for disjoint expressions.-* Fixes bugs:- * DEC: don't generate single-branch case-expressions [#90](https://github.com/clash-lang/clash-compiler/issues/90)--## 0.6.2 *October 21st 2015*-* Fixes bugs:- * DEC: Subject and alternatives are not disjoint [#88](https://github.com/clash-lang/clash-compiler/issues/88)--## 0.6.1 *October 16th 2015*-* New features:- * Support for `clash-prelude` 0.10.1- * Transformation that lifts applications of the same function out of alternatives of case-statements. e.g.-- ```haskell- case x of- A -> f 3 y- B -> f x x- C -> h x- ```-- is transformed into:-- ```haskell- let f_arg0 = case x of {A -> 3; B -> x}- f_arg1 = case x of {A -> y; B -> x}- f_out = f f_arg0 f_arg1- in case x of- A -> f_out- B -> f_out- C -> h x- ```--* Fixes bugs:- * Case-statements acting like normal decoder circuits are erroneously synthesised to priority decoder circuits.--## 0.6 *October 3rd 2015*-* New features:- * Support for `clash-prelude` 0.10- * Add `~INDEXTYPE` tag: primitives get access to the `Index` clash-prelude type- * Add `~IF` construct: primitives can do conditional templating- * Unroll "definitions" of the following primitives: `fold`, `dfold`, `foldr`--## 0.5.13 *September 21st 2015*-* Fixes bugs:- * Performance bug: top-level definitions of type "Signal" erroneously inlined.--## 0.5.12 *September 14th 2015*-* New features:- * Completely unroll "definitions" of some higher-order primitives with non-representable argument or result vectors:- It is now possible to translate e.g. `f xs ys = zipWith ($) (map (+) xs) ys :: Vec 4 Int -> Vec 4 Int -> Vec 4 Int`--* Fixes bugs:- * `topLet` transformation erroneously not performed in a top-down traversal- * Specialisation limit unchecked on types and constants- * Vector of functions cannot be translated [#25](https://github.com/clash-lang/clash-compiler/issues/25 )- * CLaSH fails to generate VHDL when map is applied [#78](https://github.com/clash-lang/clash-compiler/issues/78)--## 0.5.11 *September 7th 2015*-* Fixes bugs:- * Clash running out of memory on Simple-ish project [#70](https://github.com/clash-lang/clash-compiler/issues/70)- * `CLaSH.Sized.Vector.:>` was not allowed as a function argument to HO-primitives--## 0.5.10 *August 2nd 2015*-* Fixes bugs:- * Make testbench generation deterministic- * Compile against unbound-generics-0.2--## 0.5.9 *July 9th 2015*-* Fixes bugs:- * `coreView` didn't look through newtypes of the form: `newtype Foo a = MkFoo (Maybe a)`--## 0.5.8 *June 26th 2015*-* Fixes bugs:- * Allow text and tags in ~SIGD black box construct--## 0.5.7 *June 25th 2015*-* New features:- * Support for copying string literals from Haskell to generated code- * Collect and copy data-files--* Fixes bugs:- * Signals declared twice when not using a clock-generating component [#60](https://github.com/clash-lang/clash-compiler/issues/60)- * This piece of code eat up all CPU when generating verilog [#62](https://github.com/clash-lang/clash-compiler/issues/60)--## 0.5.6 *June 3rd 2015*-* New features:- * Support Verilog backend- * Generated component names are prefixed by the name of the module containing the `topEntity`--## 0.5.5 *May 18th 2015*-* New features:- * Make inlining and specialisation limit configurable- * Make debug message level configurable--* Fixes bugs:- * Netlist: ensure that the arguments of a component instantiation are always simple variables- * CaseCon transformation: ensure that we run the compile-time evaluator on the subject before handling the one-alternative case- * Emit a warning if a function remains recursive, instead of producing an error: compilation can still be successful if the function is an argument to a higher-order blackbox that doesn't use the function.- * Emit a warning if inlining limit is reached, instead of producing an error: compilation can still be successful if the function is an argument to a higher-order blackbox that doesn't use the function.- * Always inline terms that have a type of kind `Constraint`--## 0.5.4 *May 10th 2015*-* New features:- * Add `~COMPNAME` tag: primitives get access to the component name in which they are instantiated--## 0.5.3 *May 5th 2015*-* New features:- * `TopEntity` wrappers are now specified as `ANN` annotation pragmas-* Fixes bugs:- * Lost system1000 clock in VHDL generation... [#53](https://github.com/clash-lang/clash-compiler/issues/53)- * `flattenCallTree` sometimes introduces free variables--## 0.5.2 *May 1st 2015*-* New features:- * Generate wrappers around `topEntity` that have constant names and types--## 0.5.1 *April 20th 2015*-* GHC 7.10 support--## 0.5 *March 11th 2015*-* New features:- * Simplify BlackBox handling, and improve VHDL generation. [#47](https://github.com/clash-lang/clash-compiler/issues/47)- * Use unbound-generics. [#48](https://github.com/clash-lang/clash-compiler/issues/48)--* Fixes bugs:- * VHDL generation error: wrapper for sum-of-products type. [#44](https://github.com/clash-lang/clash-compiler/issues/44)--## 0.4.1 *February 4th 2015*-* Fixes bugs:- * Treat BlackBox expressions as declarations when DC args. [#37](https://github.com/christiaanb/clash2/issues/33)- * Don't inline recursive closed bindings--## 0.4 *November 17th 2014*-* New features:- * Support for clash-prelude 0.6--* Fixes bugs:- * Ambiguous type: 'std_logic_vector' or 'std_ulogic_vector' [#33](https://github.com/christiaanb/clash2/issues/33)--## 0.3.2 *June 5th 2014*--* Fixes bugs:- * VHDL array constant ambiguous [#18](https://github.com/christiaanb/clash2/issues/18)- * Exception: can't create selector [#24](https://github.com/christiaanb/clash2/issues/24)- * Calls to `vhdlTypeMark` don't result to inclusion of VHDL type in types.vhdl [#28](https://github.com/christiaanb/clash2/issues/28)--## 0.3.1 *May 15th 2014*--* New features:- * Make ANF lift non-representable values [#7](https://github.com/christiaanb/clash2/issues/7)- * Hardcode `fromInteger` for `Signed` and `Unsigned` [#9](https://github.com/christiaanb/clash2/issues/9)- * Replace VHDL default hole by error hole [#13](https://github.com/christiaanb/clash2/issues/13)--* Fixes bugs:- * Type families are not expanded [#3](https://github.com/christiaanb/clash2/issues/3)- * Exception: CLaSH.Netlist.VHDL(512): fromSLV: Vector 13 Bool [#5](https://github.com/christiaanb/clash2/issues/5)- * Incorrect vhdl generation for default value in blackbox [#6](https://github.com/christiaanb/clash2/issues/6)- * Duplicate type names when multiple ADTs need the same amount of bits [#8](https://github.com/christiaanb/clash2/issues/8)- * Circuit testbench generation with MAC example fails[#15](https://github.com/christiaanb/clash2/issues/15)--* Code improvements:- * Refactor Netlist/BlackBox [#10](https://github.com/christiaanb/clash2/issues/10)- * CPP special-case conversion of `Control.Exception.Base.irrefutPatError` [#11](https://github.com/christiaanb/clash2/issues/11)
clash-lib.cabal view
@@ -1,5 +1,6 @@+Cabal-version: 2.2 Name: clash-lib-Version: 1.0.1+Version: 1.2.0 Synopsis: CAES Language for Synchronous Hardware - As a Library Description: Clash is a functional hardware description language that borrows both its@@ -36,15 +37,15 @@ . Front-ends (for: parsing, typecheck, etc.) are provided by separate packages: .- * <http://hackage.haskell.org/package/clash-ghc GHC/Haskell Frontend>+ * <https://hackage.haskell.org/package/clash-ghc GHC/Haskell Frontend> . * <https://github.com/christiaanb/Idris-dev Idris Frontend> . .- Prelude library: <http://hackage.haskell.org/package/clash-prelude>-Homepage: http://www.clash-lang.org/-bug-reports: http://github.com/clash-lang/clash-compiler/issues-License: BSD2+ Prelude library: <https://hackage.haskell.org/package/clash-prelude>+Homepage: https://clash-lang.org/+bug-reports: https://github.com/clash-lang/clash-compiler/issues+License: BSD-2-Clause License-file: LICENSE Author: The Clash Authors Maintainer: QBayLogic B.V. <devops@qbaylogic.com>@@ -56,7 +57,7 @@ Extra-source-files: README.md,- CHANGELOG.md+ CHANGELOG.md, src/ClashDebug.h Data-files:@@ -66,11 +67,10 @@ prims/systemverilog/*.json, prims/vhdl/*.json -Cabal-version: >=1.10- source-repository head type: git location: https://github.com/clash-lang/clash-compiler.git+ subdir: clash-lib flag debug description:@@ -90,53 +90,79 @@ default: True manual: True +common common-options+ default-language: Haskell2010+ default-extensions: BangPatterns+ BinaryLiterals+ DataKinds+ DefaultSignatures+ DeriveDataTypeable+ DeriveFoldable+ DeriveFunctor+ DeriveGeneric+ DeriveLift+ DeriveTraversable+ DerivingStrategies+ InstanceSigs+ KindSignatures+ ScopedTypeVariables+ StandaloneDeriving+ TupleSections+ TypeApplications+ TypeOperators+ ViewPatterns++ if impl(ghc >= 8.6)+ default-extensions: NoStarIsType++ Library+ import: common-options HS-Source-Dirs: src - default-language: Haskell2010 ghc-options: -Wall CPP-Options: -DCABAL other-extensions: CPP DeriveAnyClass- DeriveGeneric- DeriveFoldable- DeriveFunctor FlexibleContexts FlexibleInstances GeneralizedNewtypeDeriving LambdaCase MultiParamTypeClasses OverloadedStrings- Rank2Types+ RankNTypes RecordWildCards- ScopedTypeVariables TemplateHaskell- TupleSections- ViewPatterns Build-depends: aeson >= 0.6.2.0 && < 1.5,- ansi-terminal >= 0.8.0.0 && < 0.10,+ ansi-terminal >= 0.8.0.0 && < 0.11, attoparsec >= 0.10.4.0 && < 0.14, base >= 4.10 && < 5, binary >= 0.8.5 && < 0.11, bytestring >= 0.10.0.2 && < 0.11,- clash-prelude >= 1.0 && < 1.1,+ clash-prelude == 1.2.0, concurrent-supply >= 0.1.7 && < 0.2, containers >= 0.5.0.0 && < 0.7, data-binary-ieee754 >= 0.4.4 && < 0.6,+ data-default >= 0.7 && < 0.8, deepseq >= 1.3.0.2 && < 1.5,+ dlist >= 0.8 && < 0.9, directory >= 1.2.0.1 && < 1.4, errors >= 1.4.2 && < 2.4, exceptions >= 0.8.3 && < 0.11.0,+ extra >= 1.6.18 && < 1.7, filepath >= 1.3.0.1 && < 1.5,- ghc >= 8.2.0 && < 8.9,+ ghc >= 8.4.0 && < 8.9,+ ghc-boot-th, hashable >= 1.2.1.0 && < 1.4,+ haskell-src-meta >= 0.8 && < 0.9, hint >= 0.7 && < 0.10, integer-gmp >= 1.0 && < 1.1, interpolate >= 0.2.0 && < 1.0,- lens >= 3.9.2 && < 4.19,+ lens >= 4.10 && < 4.20, mtl >= 2.1.2 && < 2.3,+ ordered-containers >= 0.2 && < 0.3, parsers >= 0.12.8 && < 1.0, prettyprinter >= 1.2.0.1 && < 2.0, primitive >= 0.5.0.1 && < 1.0,@@ -153,6 +179,8 @@ vector-binary-instances >= 0.2.3.5 && < 0.3, unordered-containers >= 0.2.3.3 && < 0.3 + Autogen-Modules: Paths_clash_lib+ Exposed-modules: Clash.Annotations.BitRepresentation.ClashLib Clash.Backend@@ -162,12 +190,15 @@ Clash.Core.DataCon Clash.Core.Evaluator+ Clash.Core.Evaluator.Types Clash.Core.FreeVars Clash.Core.Literal Clash.Core.Name Clash.Core.Pretty Clash.Core.Subst Clash.Core.Term+ Clash.Core.TermLiteral+ Clash.Core.TermLiteral.TH Clash.Core.TyCon Clash.Core.Type Clash.Core.TysPrim@@ -202,6 +233,7 @@ Clash.Primitives.GHC.Literal Clash.Primitives.GHC.Word Clash.Primitives.Intel.ClockGen+ Clash.Primitives.Sized.ToInteger Clash.Primitives.Sized.Vector Clash.Rewrite.Combinators@@ -211,6 +243,7 @@ Clash.Unique Clash.Util Clash.Util.Graph+ Clash.Util.Interpolate Clash.Pretty Data.Text.Prettyprint.Doc.Extra@@ -218,6 +251,7 @@ Other-Modules: Clash.Annotations.TopEntity.Extra Data.Aeson.Extra Data.Semigroup.Monad.Extra+ Data.Set.Ordered.Extra Data.Vector.Primitive.Extra GHC.BasicTypes.Extra GHC.SrcLoc.Extra@@ -230,6 +264,7 @@ cpp-options: -DHISTORY test-suite unittests+ import: common-options type: exitcode-stdio-1.0 default-language: Haskell2010 main-is: unittests.hs@@ -252,3 +287,5 @@ tasty-hunit Other-Modules: Clash.Tests.Core.FreeVars+ Clash.Tests.Core.Subst+ Clash.Tests.Util.Interpolate
prims/common/Clash_Intel_ClockGen.json view
@@ -2,6 +2,12 @@ { "name" : "Clash.Intel.ClockGen.altpll" , "workInfo" : "Always" , "kind" : "Declaration"+ , "includes" : [ {"extension": "qsys"+ ,"name": "altpll"+ ,"format": "Haskell"+ ,"templateFunction": "Clash.Primitives.Intel.ClockGen.altpllQsysTF"+ }+ ] , "format" : "Haskell" , "templateFunction" : "Clash.Primitives.Intel.ClockGen.altpllTF" }@@ -10,6 +16,12 @@ { "name" : "Clash.Intel.ClockGen.alteraPll" , "workInfo" : "Always" , "kind" : "Declaration"+ , "includes" : [ {"extension": "qsys"+ ,"name": "altera_pll"+ ,"format": "Haskell"+ ,"templateFunction": "Clash.Primitives.Intel.ClockGen.alteraPllQsysTF"+ }+ ] , "format" : "Haskell" , "templateFunction" : "Clash.Primitives.Intel.ClockGen.alteraPllTF" }
prims/common/Clash_Magic.json view
@@ -9,6 +9,21 @@ } } , { "Primitive" :+ { "name" : "Clash.Magic.suffixNameFromNat"+ , "primType" : "Function"+ }+ }+, { "Primitive" :+ { "name" : "Clash.Magic.suffixNameP"+ , "primType" : "Function"+ }+ }+, { "Primitive" :+ { "name" : "Clash.Magic.suffixNameFromNatP"+ , "primType" : "Function"+ }+ }+, { "Primitive" : { "name" : "Clash.Magic.setName" , "primType" : "Function" }
prims/common/Clash_XException.json view
@@ -22,4 +22,12 @@ , "template" : "~ARG[2]" } }+, { "BlackBox" :+ { "name" : "Clash.XException.hwSeqX"+ , "workInfo" : "Never"+ , "kind" : "Expression"+ , "type" : "hwSeqX :: a -> b -> b"+ , "template" : "~DEVNULL[~VAR[x][0]]~ARG[1]"+ }+ } ]
prims/common/GHC_Natural.json view
@@ -32,5 +32,12 @@ "template": "~ARG[0] - ~ARG[1]", "warning": "GHC.Natural.minusNatural: Naturals are dynamically sized in simulation, but fixed-length after synthesization. Use carefully." }+ },+ {+ "Primitive": {+ "name": "GHC.Natural.gcdNatural",+ "workInfo" : "Never",+ "primType": "Function"+ } } ]
+ prims/commonverilog/Clash_Explicit_SimIO.json view
@@ -0,0 +1,127 @@+[ { "Primitive" :+ { "name" : "Clash.Explicit.SimIO.mealyIO"+ , "primType" : "Function"+ }+ }+, { "BlackBox" :+ { "name" : "Clash.Explicit.SimIO.display"+ , "kind" : "Declaration"+ , "renderVoid" : "RenderVoid"+ , "template" : "$display(~ARG[0]);"+ }+ }+, { "BlackBox" :+ { "name" : "Clash.Explicit.SimIO.finish"+ , "kind" : "Declaration"+ , "renderVoid" : "RenderVoid"+ , "template" : "$finish_and_return(~LIT[0]);"+ }+ }+, { "BlackBox" :+ { "name" : "Clash.Explicit.SimIO.reg"+ , "kind" : "Expression"+ , "template" : "~ARG[0]"+ }+ }+, { "BlackBox" :+ { "name" : "Clash.Explicit.SimIO.readReg"+ , "kind" : "Expression"+ , "template" : "~ARG[0]"+ }+ }+, { "BlackBox" :+ { "name" : "Clash.Explicit.SimIO.writeReg"+ , "kind" : "Declaration"+ , "renderVoid" : "RenderVoid"+ , "template" : "~ARG[0] = ~ARG[1];"+ }+ }+, { "BlackBox" :+ { "name" : "Clash.Explicit.SimIO.openFile"+ , "kind" : "Expression"+ , "template" : "$fopen(~FILE[~LIT[0]],~LIT[1])"+ }+ }+, { "BlackBox" :+ { "name" : "Clash.Explicit.SimIO.closeFile"+ , "kind" : "Declaration"+ , "renderVoid" : "RenderVoid"+ , "template" : "$fclose(~ARG[0]);"+ }+ }+, { "BlackBox" :+ { "name" : "Clash.Explicit.SimIO.getChar"+ , "kind" : "Expression"+ , "template" : "$fgetc(~ARG[0])"+ }+ }+, { "BlackBox" :+ { "name" : "Clash.Explicit.SimIO.putChar"+ , "kind" : "Declaration"+ , "renderVoid" : "RenderVoid"+ , "template" : "$ungetc(~ARG[0],~ARG[1]);"+ }+ }+, { "BlackBox" :+ { "name" : "Clash.Explicit.SimIO.getLine"+ , "kind" : "Declaration"+ , "template" : "~RESULT = $fgets(~ARG[2],~ARG[1]);"+ }+ }+, { "BlackBox" :+ { "name" : "Clash.Explicit.SimIO.isEOF"+ , "kind" : "Expression"+ , "template" : "$feof(~ARG[0])"+ }+ }+, { "BlackBox" :+ { "name" : "Clash.Explicit.SimIO.flush"+ , "kind" : "Declaration"+ , "renderVoid" : "RenderVoid"+ , "template" : "$fflush(~ARG[0]);"+ }+ }+, { "BlackBox" :+ { "name" : "Clash.Explicit.SimIO.seek"+ , "kind" : "Declaration"+ , "template" : "~RESULT = $fseek(~ARG[0],~ARG[1],~ARG[2]);"+ }+ }+, { "BlackBox" :+ { "name" : "Clash.Explicit.SimIO.rewind"+ , "kind" : "Declaration"+ , "template" : "~RESULT = $rewind(~ARG[0]);"+ }+ }+, { "BlackBox" :+ { "name" : "Clash.Explicit.SimIO.tell"+ , "kind" : "Expression"+ , "template" : "$ftell(~ARG[0])"+ }+ }+, { "Primitive" :+ { "name" : "Clash.Explicit.SimIO.fmapSimIO#"+ , "primType" : "Function"+ }+ }+, { "Primitive" :+ { "name" : "Clash.Explicit.SimIO.pureSimIO#"+ , "primType" : "Function"+ }+ }+, { "Primitive" :+ { "name" : "Clash.Explicit.SimIO.apSimIO#"+ , "primType" : "Function"+ }+ }+, { "Primitive" :+ { "name" : "Clash.Explicit.SimIO.bindSimIO#"+ , "primType" : "Function"+ }+ }+, { "Primitive" :+ { "name" : "Clash.Explicit.SimIO.unSimIO#"+ , "primType" : "Function"+ }+ }+]
prims/commonverilog/Clash_Sized_Internal_BitVector.json view
@@ -88,7 +88,7 @@ , "workInfo" : "Never" , "kind" : "Expression" , "type" : "fromInteger## :: Integer -> Integer -> Bit"- , "template" : "~ARG[0][0] ? 1'bx : ~ARG[1][0]"+ , "template" : "~VAR[i][0][0] ? 1'bx : ~VAR[i][1][0]" } } , { "BlackBox" :@@ -161,12 +161,10 @@ , "template" : "~IF~SIZE[~TYP[1]]~THEN~ARG[1] <= ~ARG[2]~ELSE1'b1~FI" } }-, { "BlackBox" :- { "name" : "Clash.Sized.Internal.BitVector.toInteger#"- , "workInfo" : "Never"- , "kind" : "Expression"- , "type" : "toInteger# :: KnownNat n => BitVector n -> Integer"- , "template" : "~IF~SIZE[~TYP[1]]~THEN~IF~CMPLE[~SIZE[~TYPO]][~SIZE[~TYP[1]]]~THEN$signed(~VAR[bv][1][0+:~SIZE[~TYPO]])~ELSE$signed({{(~SIZE[~TYPO]-~SIZE[~TYP[1]]) {1'b0}},~VAR[bv][1]})~FI~ELSE~SIZE[~TYPO]'sd0~FI"+, { "BlackBoxHaskell" :+ { "name" : "Clash.Sized.Internal.BitVector.toInteger#"+ , "workInfo" : "Never"+ , "templateFunction" : "Clash.Primitives.Sized.ToInteger.bvToIntegerVerilog" } } , { "BlackBox" :
prims/commonverilog/Clash_Sized_Internal_Index.json view
@@ -121,12 +121,10 @@ , "template" : "~ARG[0] % ~ARG[1]" } }-, { "BlackBox" :+, { "BlackBoxHaskell" : { "name" : "Clash.Sized.Internal.Index.toInteger#" , "workInfo" : "Never"- , "kind" : "Expression"- , "type" : "toInteger# :: Index n -> Integer"- , "template" : "~IF~SIZE[~TYP[0]]~THEN~IF~CMPLE[~SIZE[~TYPO]][~SIZE[~TYP[0]]]~THEN$signed(~VAR[i][0][0+:~SIZE[~TYPO]])~ELSE$signed({{(~SIZE[~TYPO]-~SIZE[~TYP[0]]) {1'b0}},~VAR[i][0]})~FI~ELSE~SIZE[~TYPO]'sd0~FI"+ , "templateFunction" : "Clash.Primitives.Sized.ToInteger.indexToIntegerVerilog" } } , { "BlackBox" :
prims/commonverilog/Clash_Sized_Internal_Signed.json view
@@ -40,12 +40,10 @@ , "template" : "~IF~SIZE[~TYP[0]]~THEN~ARG[0] <= ~ARG[1]~ELSE1'b1~FI" } }-, { "BlackBox" :+, { "BlackBoxHaskell" : { "name" : "Clash.Sized.Internal.Signed.toInteger#" , "workInfo" : "Never"- , "kind" : "Expression"- , "type" : "toInteger# :: Signed n -> Integer"- , "template" : "~IF~SIZE[~TYP[0]]~THEN~IF~CMPLE[~SIZE[~TYPO]][~SIZE[~TYP[0]]]~THEN$signed(~VAR[i][0][0+:~SIZE[~TYPO]])~ELSE$signed({{(~SIZE[~TYPO]-~SIZE[~TYP[0]]) {1'b0}},~VAR[i][0]})~FI~ELSE~SIZE[~TYPO]'sd0~FI"+ , "templateFunction" : "Clash.Primitives.Sized.ToInteger.signedToIntegerVerilog" } } , { "BlackBox" :
prims/commonverilog/Clash_Sized_Internal_Unsigned.json view
@@ -40,12 +40,10 @@ , "template" : "~IF~SIZE[~TYP[0]]~THEN~ARG[0] <= ~ARG[1]~ELSE1'b1~FI" } }-, { "BlackBox" :+, { "BlackBoxHaskell" : { "name" : "Clash.Sized.Internal.Unsigned.toInteger#" , "workInfo" : "Never"- , "kind" : "Expression"- , "type" : "toInteger# :: Unsigned n -> Integer"- , "template" : "~IF~SIZE[~TYP[0]]~THEN~IF~CMPLE[~SIZE[~TYPO]][~SIZE[~TYP[0]]]~THEN$signed(~VAR[i][0][0+:~SIZE[~TYPO]])~ELSE$signed({{(~SIZE[~TYPO]-~SIZE[~TYP[0]]) {1'b0}},~VAR[i][0]})~FI~ELSE~SIZE[~TYPO]'sd0~FI"+ , "templateFunction" : "Clash.Primitives.Sized.ToInteger.unsignedToIntegerVerilog" } } , { "BlackBox" :
prims/systemverilog/Clash_Signal_BiSignal.json view
@@ -1,6 +1,7 @@ [ { "BlackBox" : { "name" : "Clash.Signal.BiSignal.writeToBiSignal#", "kind" : "Declaration",+ "renderVoid": "RenderVoid", "type" : "writeToBiSignal# :: HasCallStack -- ARG[0]
prims/systemverilog/Clash_Signal_Internal.json view
@@ -10,18 +10,18 @@ -> a -- ARG[4] -> Signal clk a -- ARG[5] -> Signal clk a"+ , "resultName" : { "template" : "~CTXNAME" }+ , "resultInit" : { "template" : "~IF~ISINITDEFINED[0]~THEN~CONST[4]~ELSE~FI" } , "template" :-"// delay begin,-~TYPO ~GENSYM[~RESULT_reg][0] ~IF ~ISINITDEFINED[0] ~THEN = ~CONST[4] ~ELSE ~FI;~IF ~ISACTIVEENABLE[3] ~THEN+"// delay begin~IF ~ISACTIVEENABLE[3] ~THEN always_ff @(~IF~ACTIVEEDGE[Rising][0]~THENposedge~ELSEnegedge~FI ~ARG[2]) begin : ~GENSYM[~RESULT_delay][1] if (~ARG[3]) begin- ~SYM[0] <= ~ARG[5];+ ~RESULT <= ~ARG[5]; end end~ELSE-always_ff @(~IF~ACTIVEEDGE[Rising][0]~THENposedge~ELSEnegedge~FI ~ARG[2]) begin : ~SYM[1]- ~SYM[0] <= ~ARG[5];+always @(~IF~ACTIVEEDGE[Rising][0]~THENposedge~ELSEnegedge~FI ~ARG[2]) begin : ~SYM[1]+ ~RESULT <= ~ARG[5]; end~FI-assign ~RESULT = ~SYM[0]; // delay end" } }@@ -39,17 +39,17 @@ -> a -- ARG[6] (reset value) -> Signal clk a -- ARG[7] -> Signal clk a"+ , "resultName" : { "template" : "~CTXNAME" }+ , "resultInit" : { "template" : "~IF~ISINITDEFINED[0]~THEN~CONST[5]~ELSE~FI" } , "template" : "// register begin-~TYPO ~GENSYM[~RESULT_reg][0] ~IF ~ISINITDEFINED[0] ~THEN = ~CONST[5] ~ELSE ~FI;-always_ff @(~IF~ACTIVEEDGE[Rising][0]~THENposedge~ELSEnegedge~FI ~ARG[2]~IF ~ISSYNC[0] ~THEN ~ELSE or ~IF ~ISACTIVEHIGH[0] ~THEN posedge ~ELSE negedge ~FI ~ARG[3]~FI) begin : ~GENSYM[~RESULT_register][1]- if (~IF ~ISACTIVEHIGH[0] ~THEN ~ELSE ! ~FI~ARG[3]) begin- ~SYM[0] <= ~CONST[6];+always_ff @(~IF~ACTIVEEDGE[Rising][0]~THENposedge~ELSEnegedge~FI ~ARG[2]~IF ~ISSYNC[0] ~THEN ~ELSE or ~IF ~ISACTIVEHIGH[0] ~THEN posedge ~ELSE negedge ~FI ~VAR[rst][3]~FI) begin : ~GENSYM[~RESULT_register][1]+ if (~IF ~ISACTIVEHIGH[0] ~THEN ~ELSE ! ~FI~VAR[rst][3]) begin+ ~RESULT <= ~CONST[6]; end else ~IF ~ISACTIVEENABLE[4] ~THEN if (~ARG[4]) ~ELSE ~FI begin- ~SYM[0] <= ~ARG[7];+ ~RESULT <= ~ARG[7]; end end-assign ~RESULT = ~SYM[0]; // register end" } }
prims/systemverilog/Clash_Sized_Internal_Signed.json view
@@ -1,15 +1,27 @@ [ { "BlackBox" : { "name" : "Clash.Sized.Internal.Signed.div#" , "kind" : "Declaration"- , "type" : "div# :: KnownNat n => Signed n -> Signed n -> Signed n"+ , "type" : "div# :: Signed n -> Signed n -> Signed n" , "template" : "// divSigned begin-// divide (rounds towards zero)-~SIGD[~GENSYM[quot_res][0]][1];-assign ~SYM[0] = ~VAR[dividend][1] / ~VAR[divider][2];+logic ~GENSYM[resultPos][1];+logic ~GENSYM[dividerNeg][2];+logic signed [~SIZE[~TYPO]:0] ~GENSYM[dividend2][3];+logic signed [~SIZE[~TYPO]:0] ~GENSYM[dividendE][4];+logic signed [~SIZE[~TYPO]:0] ~GENSYM[dividerE][5];+logic signed [~SIZE[~TYPO]:0] ~GENSYM[quot_res][6]; -// round toward minus infinity-assign ~RESULT = (~VAR[dividend][1][~LIT[0]-1] == ~VAR[divider][2][~LIT[0]-1]) ? ~SYM[0] : ~SYM[0] - ~LIT[0]'sd1;+assign ~SYM[1] = ~VAR[dividend][0][~SIZE[~TYPO]-1] == ~VAR[divider][1][~SIZE[~TYPO]-1];+assign ~SYM[2] = ~VAR[divider][1][~SIZE[~TYPO]-1] == 1'b1;+assign ~SYM[4] = $signed({{~VAR[dividend][0][~SIZE[~TYPO]-1]},~VAR[dividend][0]}); // sign extension+assign ~SYM[5] = $signed({{~VAR[divider][1][~SIZE[~TYPO]-1]} ,~VAR[divider][1]} ); // sign extension++assign ~SYM[3] = ~SYM[1] ? ~SYM[4]+ : (~SYM[2] ? (~SYM[4] - ~SYM[5] - ~SIZE[~TYPO]'sd1)+ : (~SYM[4] - ~SYM[5] + ~SIZE[~TYPO]'sd1));++assign ~SYM[6] = ~SYM[3] / ~SYM[5];+assign ~RESULT = $signed(~SYM[6][~SIZE[~TYPO]-1:0]); // divSigned end" } }@@ -26,7 +38,7 @@ // modulo assign ~RESULT = (~VAR[dividend][0][~SIZE[~TYPO]-1] == ~VAR[divider][1][~SIZE[~TYPO]-1]) ? ~SYM[0] :- (~VAR[dividend][0] == ~SIZE[~TYPO]'sd0 ? ~SIZE[~TYPO]'sd0 : ~SYM[0] + ~VAR[divider][1]);+ (~SYM[0] == ~SIZE[~TYPO]'sd0 ? ~SIZE[~TYPO]'sd0 : ~SYM[0] + ~VAR[divider][1]); // modSigned end" } }
prims/systemverilog/Clash_Sized_Vector.json view
@@ -249,58 +249,6 @@ } } , { "BlackBox" :- { "name" : "Clash.Sized.Vector.fold"- , "workInfo" : "Never"- , "kind" : "Declaration"- , "type" : "fold :: (a -> a -> a) -> Vec (n+1) a -> a"- , "comment" : "THIS ONLY WORKS FOR POWER OF TWO LENGTH VECTORS"- , "imports" : ["~INCLUDENAME[0].inc"]- , "includes" :- [ { "name" : "depth2Index"- , "extension" : "inc"- , "template" :-"// given a level and a depth, calculate the corresponding index into the-// intermediate array-function integer ~INCLUDENAME[0];- input integer levels;- input integer depth;-- ~INCLUDENAME[0] = (2 ** levels) - (2 ** depth);-endfunction"- }- ]- , "template" :-"// fold begin-// put flat input array into the first half of the intermediate array-~SIGDO[~GENSYM[intermediate][0]] [0:(2*~LENGTH[~TYP[1]])-2];-assign ~SYM[0][0:~LENGTH[~TYP[1]]-1] = ~ARG[1];--// calculate the depth of the tree-localparam ~GENSYM[levels][4] = $clog2(~LENGTH[~TYP[1]]);--// Create the tree of instantiated components-genvar ~GENSYM[d][5];-genvar ~GENSYM[i][6];-~GENERATE-if (~SYM[4] != 0) begin : ~GENSYM[make_tree][7]- for (~SYM[5] = ~SYM[4]; ~SYM[5] > 0; ~SYM[5]=~SYM[5]-1) begin : tree_depth- for (~SYM[6] = 0; ~SYM[6] < (2**(~SYM[5]-1)); ~SYM[6] = ~SYM[6]+1) begin : tree_depth_loop- ~INST 0- ~OUTPUT <= ~SYM[0][~INCLUDENAME[0](~SYM[4]+1,~SYM[5])+~SYM[6]]~ ~TYPO~- ~INPUT <= ~SYM[0][~INCLUDENAME[0](~SYM[4]+1,~SYM[5]+1)+(2*~SYM[6])]~ ~TYPO~- ~INPUT <= ~SYM[0][~INCLUDENAME[0](~SYM[4]+1,~SYM[5]+1)+(2*~SYM[6])+1]~ ~TYPO~- ~INST- end- end-end-~ENDGENERATE--// The last element of the intermediate array holds the result-assign ~RESULT = ~SYM[0][(2*~LENGTH[~TYP[1]])-2];-// fold end"- }- }-, { "BlackBox" : { "name" : "Clash.Sized.Vector.index_int" , "kind" : "Expression" , "type" : "index_int :: KnownNat n => Vec n a -> Int -> a"
prims/systemverilog/GHC_Classes.json view
@@ -4,12 +4,24 @@ , "type" : "divInt# :: Int# -> Int# -> Int#" , "template" : "// divInt# begin-// divide (rounds towards zero)-~SIGD[~GENSYM[quot_res][0]][0];-assign ~SYM[0] = ~VAR[dividend][0] / ~VAR[divider][1];+logic ~GENSYM[resultPos][1];+logic ~GENSYM[dividerNeg][2];+logic signed [~SIZE[~TYPO]:0] ~GENSYM[dividend2][3];+logic signed [~SIZE[~TYPO]:0] ~GENSYM[dividendE][4];+logic signed [~SIZE[~TYPO]:0] ~GENSYM[dividerE][5];+logic signed [~SIZE[~TYPO]:0] ~GENSYM[quot_res][6]; -// round toward minus infinity-assign ~RESULT = (~VAR[dividend][0][~SIZE[~TYPO]-1] == ~VAR[divider][1][~SIZE[~TYPO]-1]) ? ~SYM[0] : ~SYM[0] - ~SIZE[~TYPO]'sd1;+assign ~SYM[1] = ~VAR[dividend][0][~SIZE[~TYPO]-1] == ~VAR[divider][1][~SIZE[~TYPO]-1];+assign ~SYM[2] = ~VAR[divider][1][~SIZE[~TYPO]-1] == 1'b1;+assign ~SYM[4] = $signed({{~VAR[dividend][0][~SIZE[~TYPO]-1]},~VAR[dividend][0]}); // sign extension+assign ~SYM[5] = $signed({{~VAR[divider][1][~SIZE[~TYPO]-1]} ,~VAR[divider][1]} ); // sign extension++assign ~SYM[3] = ~SYM[1] ? ~SYM[4]+ : (~SYM[2] ? (~SYM[4] - ~SYM[5] - ~SIZE[~TYPO]'sd1)+ : (~SYM[4] - ~SYM[5] + ~SIZE[~TYPO]'sd1));++assign ~SYM[6] = ~SYM[3] / ~SYM[5];+assign ~RESULT = $signed(~SYM[6][~SIZE[~TYPO]-1:0]); // divInt# end" } }@@ -26,7 +38,7 @@ // modulo assign ~RESULT = (~VAR[dividend][0][~SIZE[~TYPO]-1] == ~VAR[divider][1][~SIZE[~TYPO]-1]) ? ~SYM[0] :- ((~VAR[dividend][0] == ~SIZE[~TYPO]'sd0) ? ~SIZE[~TYPO]'sd0 : ~SYM[0] + ~VAR[divider][1]);+ ((~SYM[0] == ~SIZE[~TYPO]'sd0) ? ~SIZE[~TYPO]'sd0 : ~SYM[0] + ~VAR[divider][1]); // modInt# end" } }
prims/systemverilog/GHC_Integer_Type.json view
@@ -4,12 +4,24 @@ , "type" : "divInteger :: Integer -> Integer -> Integer" , "template" : "// divInteger begin-// divide (rounds towards zero)-~SIGD[~GENSYM[quot_res][0]][0];-assign ~SYM[0] = ~VAR[dividend][0] / ~VAR[divider][1];+logic ~GENSYM[resultPos][1];+logic ~GENSYM[dividerNeg][2];+logic signed [~SIZE[~TYPO]:0] ~GENSYM[dividend2][3];+logic signed [~SIZE[~TYPO]:0] ~GENSYM[dividendE][4];+logic signed [~SIZE[~TYPO]:0] ~GENSYM[dividerE][5];+logic signed [~SIZE[~TYPO]:0] ~GENSYM[quot_res][6]; -// round toward minus infinity-assign ~RESULT = (~VAR[dividend][0][~SIZE[~TYPO]-1] == ~VAR[divider][1][~SIZE[~TYPO]-1]) ? ~SYM[0] : ~SYM[0] - ~SIZE[~TYPO]'sd1;+assign ~SYM[1] = ~VAR[dividend][0][~SIZE[~TYPO]-1] == ~VAR[divider][1][~SIZE[~TYPO]-1];+assign ~SYM[2] = ~VAR[divider][1][~SIZE[~TYPO]-1] == 1'b1;+assign ~SYM[4] = $signed({{~VAR[dividend][0][~SIZE[~TYPO]-1]},~VAR[dividend][0]}); // sign extension+assign ~SYM[5] = $signed({{~VAR[divider][1][~SIZE[~TYPO]-1]} ,~VAR[divider][1]} ); // sign extension++assign ~SYM[3] = ~SYM[1] ? ~SYM[4]+ : (~SYM[2] ? (~SYM[4] - ~SYM[5] - ~SIZE[~TYPO]'sd1)+ : (~SYM[4] - ~SYM[5] + ~SIZE[~TYPO]'sd1));++assign ~SYM[6] = ~SYM[3] / ~SYM[5];+assign ~RESULT = $signed(~SYM[6][~SIZE[~TYPO]-1:0]); // divInteger end" } }@@ -26,8 +38,60 @@ // modulo assign ~RESULT = (~VAR[dividend][0][~SIZE[~TYPO]-1] == ~VAR[divider][1][~SIZE[~TYPO]-1]) ? ~SYM[0] :- ((~VAR[dividend][0] == ~SIZE[~TYPO]'sd0) ? ~SIZE[~TYPO]'sd0 : ~SYM[0] + ~VAR[divider][1]);+ ((~SYM[0] == ~SIZE[~TYPO]'sd0) ? ~SIZE[~TYPO]'sd0 : ~SYM[0] + ~VAR[divider][1]); // modInteger end"+ }+ }+, { "BlackBox" :+ { "name" : "GHC.Integer.Type.divModInteger"+ , "kind" : "Declaration"+ , "type" : "divModInteger :: Integer -> Integer -> (# Integer, Integer #)"+ , "template" :+"// divModInteger begin+logic ~GENSYM[resultPos][1];+logic ~GENSYM[dividerNeg][2];+logic signed [~SIZE[~TYP[0]]:0] ~GENSYM[dividend2][3];+logic signed [~SIZE[~TYP[0]]:0] ~GENSYM[dividendE][4];+logic signed [~SIZE[~TYP[0]]:0] ~GENSYM[dividerE][5];+logic signed [~SIZE[~TYP[0]]:0] ~GENSYM[quot_res][6];+logic signed [~SIZE[~TYP[0]]-1:0] ~GENSYM[div_res][7];++assign ~SYM[1] = ~VAR[dividend][0][~SIZE[~TYP[0]]-1] == ~VAR[divider][1][~SIZE[~TYP[0]]-1];+assign ~SYM[2] = ~VAR[divider][1][~SIZE[~TYP[0]]-1] == 1'b1;+assign ~SYM[4] = $signed({{~VAR[dividend][0][~SIZE[~TYP[0]]-1]},~VAR[dividend][0]}); // sign extension+assign ~SYM[5] = $signed({{~VAR[divider][1][~SIZE[~TYP[0]]-1]} ,~VAR[divider][1]} ); // sign extension++assign ~SYM[3] = ~SYM[1] ? ~SYM[4]+ : (~SYM[2] ? (~SYM[4] - ~SYM[5] - ~SIZE[~TYP[0]]'sd1)+ : (~SYM[4] - ~SYM[5] + ~SIZE[~TYP[0]]'sd1));++assign ~SYM[6] = ~SYM[3] / ~SYM[5];+assign ~SYM[7] = $signed(~SYM[6][~SIZE[~TYP[0]]-1:0]);++logic signed [~SIZE[~TYP[0]]-1:0] ~GENSYM[rem_res][8];+logic signed [~SIZE[~TYP[0]]-1:0] ~GENSYM[mod_res][9];+assign ~SYM[8] = ~VAR[dividend][0] % ~VAR[divider][1];+assign ~SYM[9] = (~VAR[dividend][0][~SIZE[~TYP[0]]-1] == ~VAR[divider][1][~SIZE[~TYP[0]]-1]) ?+ ~SYM[8] :+ ((~SYM[8] == ~SIZE[~TYP[0]]'sd0) ? ~SIZE[~TYP[0]]'sd0 : ~SYM[8] + ~VAR[divider][1]);++assign ~RESULT = {~SYM[7],~SYM[9]};+// divModInteger end"+ }+ }+, { "BlackBox" :+ { "name" : "GHC.Integer.Type.quotRemInteger"+ , "kind" : "Declaration"+ , "type" : "quotRemInteger :: Integer -> Integer -> (# Integer, Integer #)"+ , "template" :+"// quotRemInteger begin+~SIGD[~GENSYM[quot_res][0]][0];+~SIGD[~GENSYM[rem_res][1]][0];+assign ~SYM[0] = ~ARG[0] / ~ARG[1];+assign ~SYM[1] = ~ARG[0] % ~ARG[1];++assign ~RESULT = {~SYM[0],~SYM[1]};+// quotRemInteger end" } } ]
prims/systemverilog/GHC_Prim.json view
@@ -331,7 +331,7 @@ , "template" : "// clz8 begin logic [0:7] ~GENSYM[v][1];-assign ~SYM[1] = ~ARG[0][7:0];+assign ~SYM[1] = ~VAR[i][0][7:0]; logic [0:7] ~GENSYM[e][2]; genvar ~GENSYM[n][3];@@ -399,7 +399,7 @@ , "template" : "// clz16 begin logic [0:15] ~GENSYM[v][1];-assign ~SYM[1] = ~ARG[0][15:0];+assign ~SYM[1] = ~VAR[i][0][15:0]; logic [0:15] ~GENSYM[e][2]; genvar ~GENSYM[i][3];@@ -483,7 +483,7 @@ , "template" : "// clz32 begin logic [0:31] ~GENSYM[v][1];-assign ~SYM[1] = ~ARG[0][31:0];+assign ~SYM[1] = ~VAR[i][0][31:0]; logic [0:31] ~GENSYM[e][2]; genvar ~GENSYM[i][3];@@ -583,7 +583,7 @@ , "template" : "// clz64 begin logic [0:63] ~GENSYM[v][1];-assign ~SYM[1] = ~ARG[0][63:0];+assign ~SYM[1] = ~VAR[i][0][63:0]; logic [0:63] ~GENSYM[e][2]; genvar ~GENSYM[i][3];@@ -700,7 +700,7 @@ "// clz begin ~IF ~IW64 ~THEN logic [0:63] ~GENSYM[v][1];-assign ~SYM[1] = ~ARG[0][63:0];+assign ~SYM[1] = ~VAR[i][0][63:0]; logic [0:63] ~GENSYM[e][2]; genvar ~GENSYM[i][3];@@ -790,7 +790,7 @@ ~ENDGENERATE ~ELSE logic [0:31] ~SYM[1];-assign ~SYM[1] = ~ARG[0][31:0];+assign ~SYM[1] = ~VAR[i][0][31:0]; logic [0:31] ~SYM[2]; genvar ~SYM[3];
prims/verilog/Clash_Signal_BiSignal.json view
@@ -1,6 +1,7 @@ [ { "BlackBox" : { "name" : "Clash.Signal.BiSignal.writeToBiSignal#", "kind" : "Declaration",+ "renderVoid": "RenderVoid", "type" : "writeToBiSignal# :: HasCallStack -- ARG[0]
prims/verilog/Clash_Signal_Internal.json view
@@ -10,18 +10,19 @@ -> a -- ARG[4] -> Signal clk a -- ARG[5] -> Signal clk a"+ , "outputReg" : true+ , "resultName" : { "template" : "~CTXNAME" }+ , "resultInit" : { "template" : "~IF~ISINITDEFINED[0]~THEN~CONST[4]~ELSE~FI" } , "template" :-"// delay begin,-reg ~TYPO ~GENSYM[~RESULT_reg][0] ~IF ~ISINITDEFINED[0] ~THEN = ~CONST[4] ~ELSE ~FI;~IF ~ISACTIVEENABLE[3] ~THEN+"// delay begin~IF ~ISACTIVEENABLE[3] ~THEN always @(~IF~ACTIVEEDGE[Rising][0]~THENposedge~ELSEnegedge~FI ~ARG[2]) begin : ~GENSYM[~RESULT_delay][1] if (~ARG[3]) begin- ~SYM[0] <= ~ARG[5];+ ~RESULT <= ~ARG[5]; end end~ELSE always @(~IF~ACTIVEEDGE[Rising][0]~THENposedge~ELSEnegedge~FI ~ARG[2]) begin : ~SYM[1]- ~SYM[0] <= ~ARG[5];+ ~RESULT <= ~ARG[5]; end~FI-assign ~RESULT = ~SYM[0]; // delay end" } }@@ -39,17 +40,18 @@ -> a -- ARG[6] (reset value) -> Signal clk a -- ARG[7] -> Signal clk a"+ , "outputReg" : true+ , "resultName" : { "template" : "~CTXNAME" }+ , "resultInit" : { "template" : "~IF~ISINITDEFINED[0]~THEN~CONST[5]~ELSE~FI" } , "template" : "// register begin-reg ~TYPO ~GENSYM[~RESULT_reg][0] ~IF ~ISINITDEFINED[0] ~THEN = ~CONST[5] ~ELSE ~FI;-always @(~IF~ACTIVEEDGE[Rising][0]~THENposedge~ELSEnegedge~FI ~ARG[2]~IF ~ISSYNC[0] ~THEN ~ELSE or ~IF ~ISACTIVEHIGH[0] ~THEN posedge ~ELSE negedge ~FI ~ARG[3]~FI) begin : ~GENSYM[~RESULT_register][1]- if (~IF ~ISACTIVEHIGH[0] ~THEN ~ELSE ! ~FI~ARG[3]) begin- ~SYM[0] <= ~CONST[6];+always @(~IF~ACTIVEEDGE[Rising][0]~THENposedge~ELSEnegedge~FI ~ARG[2]~IF ~ISSYNC[0] ~THEN ~ELSE or ~IF ~ISACTIVEHIGH[0] ~THEN posedge ~ELSE negedge ~FI ~VAR[rst][3]~FI) begin : ~GENSYM[~RESULT_register][1]+ if (~IF ~ISACTIVEHIGH[0] ~THEN ~ELSE ! ~FI~VAR[rst][3]) begin+ ~RESULT <= ~CONST[6]; end else ~IF ~ISACTIVEENABLE[4] ~THEN if (~ARG[4]) ~ELSE ~FI begin- ~SYM[0] <= ~ARG[7];+ ~RESULT <= ~ARG[7]; end end-assign ~RESULT = ~SYM[0]; // register end" } }@@ -69,7 +71,8 @@ // 1 = 0.1ps localparam ~GENSYM[half_period][1] = (~PERIOD[0]0 / 2); always begin- ~SYM[0] = ~IF~ACTIVEEDGE[Rising][0]~THEN 0 ~ELSE 1 ~FI;+ // Delay of 1 mitigates race conditions (https://github.com/steveicarus/iverilog/issues/160)+ #1 ~SYM[0] = ~IF~ACTIVEEDGE[Rising][0]~THEN 0 ~ELSE 1 ~FI; #30000 forever begin ~SYM[0] = ~ ~SYM[0]; #~SYM[1];@@ -99,7 +102,8 @@ // 1 = 0.1ps localparam ~GENSYM[half_period][1] = (~PERIOD[0]0 / 2); always begin- ~SYM[0] = ~IF~ACTIVEEDGE[Rising][0]~THEN 0 ~ELSE 1 ~FI;+ // Delay of 1 mitigates race conditions (https://github.com/steveicarus/iverilog/issues/160)+ #1 ~SYM[0] = ~IF~ACTIVEEDGE[Rising][0]~THEN 0 ~ELSE 1 ~FI; #30000 forever begin if (~ ~ARG[1]) begin $finish;
prims/verilog/Clash_Sized_Internal_Signed.json view
@@ -4,12 +4,24 @@ , "type" : "div# :: Signed n -> Signed n -> Signed n" , "template" : "// divSigned begin-// divide (rounds towards zero)-wire ~SIGD[~GENSYM[quot_res][0]][1];-assign ~SYM[0] = ~VAR[dividend][1] / ~VAR[divider][2];+wire ~GENSYM[resultPos][1];+wire ~GENSYM[dividerNeg][2];+wire signed [~SIZE[~TYPO]:0] ~GENSYM[dividend2][3];+wire signed [~SIZE[~TYPO]:0] ~GENSYM[dividendE][4];+wire signed [~SIZE[~TYPO]:0] ~GENSYM[dividerE][5];+wire signed [~SIZE[~TYPO]:0] ~GENSYM[quot_res][6]; -// round toward minus infinity-assign ~RESULT = (~VAR[dividend][1][~LIT[0]-1] == ~VAR[divider][2][~LIT[0]-1]) ? ~SYM[0] : ~SYM[0] - ~LIT[0]'sd1;+assign ~SYM[1] = ~VAR[dividend][0][~SIZE[~TYPO]-1] == ~VAR[divider][1][~SIZE[~TYPO]-1];+assign ~SYM[2] = ~VAR[divider][1][~SIZE[~TYPO]-1] == 1'b1;+assign ~SYM[4] = $signed({{~VAR[dividend][0][~SIZE[~TYPO]-1]},~VAR[dividend][0]}); // sign extension+assign ~SYM[5] = $signed({{~VAR[divider][1][~SIZE[~TYPO]-1]} ,~VAR[divider][1]} ); // sign extension++assign ~SYM[3] = ~SYM[1] ? ~SYM[4]+ : (~SYM[2] ? (~SYM[4] - ~SYM[5] - ~SIZE[~TYPO]'sd1)+ : (~SYM[4] - ~SYM[5] + ~SIZE[~TYPO]'sd1));++assign ~SYM[6] = ~SYM[3] / ~SYM[5];+assign ~RESULT = $signed(~SYM[6][~SIZE[~TYPO]-1:0]); // divSigned end" } }@@ -20,13 +32,13 @@ , "template" : "// modSigned begin // remainder-~SIGD[~GENSYM[rem_res][0]][0];+wire ~SIGD[~GENSYM[rem_res][0]][0]; assign ~SYM[0] = ~VAR[dividend][0] % ~VAR[divider][1]; // modulo assign ~RESULT = (~VAR[dividend][0][~SIZE[~TYPO]-1] == ~VAR[divider][1][~SIZE[~TYPO]-1]) ? ~SYM[0] :- (~VAR[dividend][0] == ~SIZE[~TYPO]'sd0 ? ~SIZE[~TYPO]'sd0 : ~SYM[0] + ~VAR[divider][1]);+ (~SYM[0] == ~SIZE[~TYPO]'sd0 ? ~SIZE[~TYPO]'sd0 : ~SYM[0] + ~VAR[divider][1]); // modSigned end" } }
prims/verilog/Clash_Sized_Vector.json view
@@ -233,81 +233,6 @@ ~FI// foldr end" } }-, { "BlackBox" :- { "name" : "Clash.Sized.Vector.fold"- , "workInfo" : "Never"- , "kind" : "Declaration"- , "type" : "fold :: (a -> a -> a) -> Vec (n+1) a -> a"- , "comment" : "THIS ONLY WORKS FOR POWER OF TWO LENGTH VECTORS"- , "imports" : ["~INCLUDENAME[0].inc"]- , "includes" :- [ { "name" : "fold"- , "extension" : "inc"- , "template" :-"// ceiling of log2-function integer ~INCLUDENAME[0]_clog2;- input integer value;- begin- value = value-1;- for (~INCLUDENAME[0]_clog2=0; value>0; ~INCLUDENAME[0]_clog2=~INCLUDENAME[0]_clog2+1)- value = value>>1;- end-endfunction--// given a level and a depth, calculate the corresponding index into the-// intermediate array-function integer ~INCLUDENAME[0]_depth2Index;- input integer levels;- input integer depth;-- ~INCLUDENAME[0]_depth2Index = (2 ** levels) - (2 ** depth);-endfunction"- }- ]- , "template" :-"// fold begin-// put flat input array into the first half of the intermediate array-wire ~TYPO ~GENSYM[intermediate][0] [0:(2*~LENGTH[~TYP[1]])-2];--genvar ~GENSYM[i][2];-~GENERATE-for (~SYM[2]=0; ~SYM[2] < ~LENGTH[~TYP[1]]; ~SYM[2]=~SYM[2]+1) begin : ~GENSYM[mk_array][3]- assign ~SYM[0][(~LENGTH[~TYP[1]]-1)-~SYM[2]] = ~VAR[vecflat][1][~SYM[2]*~SIZE[~TYPO]+:~SIZE[~TYPO]];-end-~ENDGENERATE--// calculate the depth of the tree-localparam ~GENSYM[levels][4] = ~INCLUDENAME[0]_clog2(~LENGTH[~TYP[1]]);--// Create the tree of instantiated components-genvar ~GENSYM[d][5];-genvar ~GENSYM[i][6];-~GENERATE-if (~SYM[4] != 0) begin : ~GENSYM[make_tree][7]- for (~SYM[5] = ~SYM[4]; ~SYM[5] > 0; ~SYM[5]=~SYM[5]-1) begin : ~GENSYM[tree_depth][11]- for (~SYM[6] = 0; ~SYM[6] < (2**(~SYM[5]-1)); ~SYM[6] = ~SYM[6]+1) begin : ~GENSYM[tree_depth_loop][12]- wire ~TYPO ~GENSYM[fold_in1][8];- wire ~TYPO ~GENSYM[fold_in2][9];- ~OUTPUTWIREREG[0] ~TYPO ~GENSYM[fold_out][10];-- assign ~SYM[8] = ~SYM[0][~INCLUDENAME[0]_depth2Index(~SYM[4]+1,~SYM[5]+1)+(2*~SYM[6])];- assign ~SYM[9] = ~SYM[0][~INCLUDENAME[0]_depth2Index(~SYM[4]+1,~SYM[5]+1)+(2*~SYM[6])+1];- ~INST 0- ~OUTPUT <= ~SYM[10]~ ~TYPO~- ~INPUT <= ~SYM[8]~ ~TYPO~- ~INPUT <= ~SYM[9]~ ~TYPO~- ~INST- assign ~SYM[0][~INCLUDENAME[0]_depth2Index(~SYM[4]+1,~SYM[5])+~SYM[6]] = ~SYM[10];- end- end-end-~ENDGENERATE--// The last element of the intermediate array holds the result-assign ~RESULT = ~SYM[0][(2*~LENGTH[~TYP[1]])-2];-// fold end"- }- } , { "BlackBoxHaskell" : { "name" : "Clash.Sized.Vector.index_int" , "templateFunction" : "Clash.Primitives.Sized.Vector.indexIntVerilog"
prims/verilog/GHC_Classes.json view
@@ -4,12 +4,24 @@ , "type" : "divInt# :: Int# -> Int# -> Int#" , "template" : "// divInt# begin-// divide (rounds towards zero)-wire ~SIGD[~GENSYM[quot_res][0]][0];-assign ~SYM[0] = ~VAR[dividend][0] / ~VAR[divider][1];+wire ~GENSYM[resultPos][1];+wire ~GENSYM[dividerNeg][2];+wire signed [~SIZE[~TYPO]:0] ~GENSYM[dividend2][3];+wire signed [~SIZE[~TYPO]:0] ~GENSYM[dividendE][4];+wire signed [~SIZE[~TYPO]:0] ~GENSYM[dividerE][5];+wire signed [~SIZE[~TYPO]:0] ~GENSYM[quot_res][6]; -// round toward minus infinity-assign ~RESULT = (~VAR[dividend][0][~SIZE[~TYPO]-1] == ~VAR[divider][1][~SIZE[~TYPO]-1]) ? ~SYM[0] : ~SYM[0] - ~SIZE[~TYPO]'sd1;+assign ~SYM[1] = ~VAR[dividend][0][~SIZE[~TYPO]-1] == ~VAR[divider][1][~SIZE[~TYPO]-1];+assign ~SYM[2] = ~VAR[divider][1][~SIZE[~TYPO]-1] == 1'b1;+assign ~SYM[4] = $signed({{~VAR[dividend][0][~SIZE[~TYPO]-1]},~VAR[dividend][0]}); // sign extension+assign ~SYM[5] = $signed({{~VAR[divider][1][~SIZE[~TYPO]-1]} ,~VAR[divider][1]} ); // sign extension++assign ~SYM[3] = ~SYM[1] ? ~SYM[4]+ : (~SYM[2] ? (~SYM[4] - ~SYM[5] - ~SIZE[~TYPO]'sd1)+ : (~SYM[4] - ~SYM[5] + ~SIZE[~TYPO]'sd1));++assign ~SYM[6] = ~SYM[3] / ~SYM[5];+assign ~RESULT = $signed(~SYM[6][~SIZE[~TYPO]-1:0]); // divInt# end" } }@@ -26,7 +38,7 @@ // modulo assign ~RESULT = (~VAR[dividend][0][~SIZE[~TYPO]-1] == ~VAR[divider][1][~SIZE[~TYPO]-1]) ? ~SYM[0] :- ((~VAR[dividend][0] == ~SIZE[~TYPO]'sd0) ? ~SIZE[~TYPO]'sd0 : ~SYM[0] + ~VAR[divider][1]);+ ((~SYM[0] == ~SIZE[~TYPO]'sd0) ? ~SIZE[~TYPO]'sd0 : ~SYM[0] + ~VAR[divider][1]); // modInt# end" } }
prims/verilog/GHC_Integer_Type.json view
@@ -4,12 +4,24 @@ , "type" : "divInteger :: Integer -> Integer -> Integer" , "template" : "// divInteger begin-// divide (rounds towards zero)-wire ~SIGD[~GENSYM[quot_res][0]][0];-assign ~SYM[0] = ~VAR[dividend][0] / ~VAR[divider][1];+wire ~GENSYM[resultPos][1];+wire ~GENSYM[dividerNeg][2];+wire signed [~SIZE[~TYPO]:0] ~GENSYM[dividend2][3];+wire signed [~SIZE[~TYPO]:0] ~GENSYM[dividendE][4];+wire signed [~SIZE[~TYPO]:0] ~GENSYM[dividerE][5];+wire signed [~SIZE[~TYPO]:0] ~GENSYM[quot_res][6]; -// round toward minus infinity-assign ~RESULT = (~VAR[dividend][0][~SIZE[~TYPO]-1] == ~VAR[divider][1][~SIZE[~TYPO]-1]) ? ~SYM[0] : ~SYM[0] - ~SIZE[~TYPO]'sd1;+assign ~SYM[1] = ~VAR[dividend][0][~SIZE[~TYPO]-1] == ~VAR[divider][1][~SIZE[~TYPO]-1];+assign ~SYM[2] = ~VAR[divider][1][~SIZE[~TYPO]-1] == 1'b1;+assign ~SYM[4] = $signed({{~VAR[dividend][0][~SIZE[~TYPO]-1]},~VAR[dividend][0]}); // sign extension+assign ~SYM[5] = $signed({{~VAR[divider][1][~SIZE[~TYPO]-1]} ,~VAR[divider][1]} ); // sign extension++assign ~SYM[3] = ~SYM[1] ? ~SYM[4]+ : (~SYM[2] ? (~SYM[4] - ~SYM[5] - ~SIZE[~TYPO]'sd1)+ : (~SYM[4] - ~SYM[5] + ~SIZE[~TYPO]'sd1));++assign ~SYM[6] = ~SYM[3] / ~SYM[5];+assign ~RESULT = $signed(~SYM[6][~SIZE[~TYPO]-1:0]); // divInteger end" } }@@ -26,8 +38,62 @@ // modulo assign ~RESULT = (~VAR[dividend][0][~SIZE[~TYPO]-1] == ~VAR[divider][1][~SIZE[~TYPO]-1]) ? ~SYM[0] :- ((~VAR[dividend][0] == ~SIZE[~TYPO]'sd0) ? ~SIZE[~TYPO]'sd0 : ~SYM[0] + ~VAR[divider][1]);+ ((~SYM[0] == ~SIZE[~TYPO]'sd0) ? ~SIZE[~TYPO]'sd0 : ~SYM[0] + ~VAR[divider][1]); // modInteger end"+ }+ }+, { "BlackBox" :+ { "name" : "GHC.Integer.Type.divModInteger"+ , "kind" : "Declaration"+ , "type" : "divModInteger :: Integer -> Integer -> (# Integer, Integer #)"+ , "template" :+"// divModInteger begin+wire ~GENSYM[resultPos][1];+wire ~GENSYM[dividerNeg][2];+wire signed [~SIZE[~TYP[0]]:0] ~GENSYM[dividend2][3];+wire signed [~SIZE[~TYP[0]]:0] ~GENSYM[dividendE][4];+wire signed [~SIZE[~TYP[0]]:0] ~GENSYM[dividerE][5];+wire signed [~SIZE[~TYP[0]]:0] ~GENSYM[quot_res][6];+wire signed [~SIZE[~TYP[0]]-1:0] ~GENSYM[div_res][7];++assign ~SYM[1] = ~VAR[dividend][0][~SIZE[~TYP[0]]-1] == ~VAR[divider][1][~SIZE[~TYP[0]]-1];+assign ~SYM[2] = ~VAR[divider][1][~SIZE[~TYP[0]]-1] == 1'b1;+assign ~SYM[4] = $signed({{~VAR[dividend][0][~SIZE[~TYP[0]]-1]},~VAR[dividend][0]}); // sign extension+assign ~SYM[5] = $signed({{~VAR[divider][1][~SIZE[~TYP[0]]-1]} ,~VAR[divider][1]} ); // sign extension++assign ~SYM[3] = ~SYM[1] ? ~SYM[4]+ : (~SYM[2] ? (~SYM[4] - ~SYM[5] - ~SIZE[~TYP[0]]'sd1)+ : (~SYM[4] - ~SYM[5] + ~SIZE[~TYP[0]]'sd1));++assign ~SYM[6] = ~SYM[3] / ~SYM[5];+assign ~SYM[7] = $signed(~SYM[6][~SIZE[~TYP[0]]-1:0]);++wire ~SIGD[~GENSYM[rem_res][8]][0];+wire ~SIGD[~GENSYM[mod_res][9]][0];+assign ~SYM[8] = ~VAR[dividend][0] % ~VAR[divider][1];++// modulo+assign ~SYM[9] = (~VAR[dividend][0][~SIZE[~TYP[0]]-1] == ~VAR[divider][1][~SIZE[~TYP[0]]-1]) ?+ ~SYM[8] :+ ((~SYM[8] == ~SIZE[~TYP[0]]'sd0) ? ~SIZE[~TYP[0]]'sd0 : ~SYM[8] + ~VAR[divider][1]);++assign ~RESULT = {~SYM[7],~SYM[9]};+// divModInteger end"+ }+ }+, { "BlackBox" :+ { "name" : "GHC.Integer.Type.quotRemInteger"+ , "kind" : "Declaration"+ , "type" : "quotRemInteger :: Integer -> Integer -> (# Integer, Integer #)"+ , "template" :+"// quotRemInteger begin+wire ~SIGD[~GENSYM[quot_res][0]][0];+wire ~SIGD[~GENSYM[rem_res][1]][0];+assign ~SYM[0] = ~ARG[0] / ~ARG[1];+assign ~SYM[1] = ~ARG[0] % ~ARG[1];++assign ~RESULT = {~SYM[0],~SYM[1]};+// quotRemInteger end" } } ]
prims/verilog/GHC_Prim.json view
@@ -381,7 +381,7 @@ , "template" : "// clz8 begin wire [0:7] ~GENSYM[v][1];-assign ~SYM[1] = ~ARG[0][7:0];+assign ~SYM[1] = ~VAR[i][0][7:0]; wire [0:7] ~GENSYM[e][2]; genvar ~GENSYM[n][3];@@ -449,7 +449,7 @@ , "template" : "// clz16 begin wire [0:15] ~GENSYM[v][1];-assign ~SYM[1] = ~ARG[0][15:0];+assign ~SYM[1] = ~VAR[i][0][15:0]; wire [0:15] ~GENSYM[e][2]; genvar ~GENSYM[i][3];@@ -533,7 +533,7 @@ , "template" : "// clz32 begin wire [0:31] ~GENSYM[v][1];-assign ~SYM[1] = ~ARG[0][31:0];+assign ~SYM[1] = ~VAR[i][0][31:0]; wire [0:31] ~GENSYM[e][2]; genvar ~GENSYM[i][3];@@ -633,7 +633,7 @@ , "template" : "// clz64 begin wire [0:63] ~GENSYM[v][1];-assign ~SYM[1] = ~ARG[0][63:0];+assign ~SYM[1] = ~VAR[i][0][63:0]; wire [0:63] ~GENSYM[e][2]; genvar ~GENSYM[i][3];@@ -749,7 +749,7 @@ , "template" : "// clz begin~IF ~IW64 ~THEN wire [0:63] ~GENSYM[v][1];-assign ~SYM[1] = ~ARG[0][63:0];+assign ~SYM[1] = ~VAR[i][0][63:0]; wire [0:63] ~GENSYM[e][2]; genvar ~GENSYM[i][3];@@ -839,7 +839,7 @@ ~ENDGENERATE ~ELSE wire [0:31] ~SYM[1];-assign ~SYM[1] = ~ARG[0][31:0];+assign ~SYM[1] = ~VAR[i][0][31:0]; wire [0:31] ~SYM[2]; genvar ~SYM[3];
prims/vhdl/Clash_Explicit_BlockRam.json view
@@ -39,11 +39,7 @@ if ~ARG[7] ~IF ~ISACTIVEENABLE[4] ~THEN and ~ARG[4] ~ELSE ~FI then ~SYM[2](~SYM[5]) <= ~TOBV[~ARG[9]][~TYP[9]]; end if;- ~RESULT <= fromSLV(~SYM[2](~SYM[4]))- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;+ ~RESULT <= fromSLV(~SYM[2](~SYM[4])); end if; end process; ~ELSE ~SYM[6] : process(~ARG[3])@@ -52,11 +48,7 @@ if ~ARG[7] ~IF ~ISACTIVEENABLE[4] ~THEN and ~ARG[4] ~ELSE ~FI then ~SYM[2](~SYM[5]) <= ~ARG[9]; end if;- ~RESULT <= ~SYM[2](~SYM[4])- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;+ ~RESULT <= ~SYM[2](~SYM[4]); end if; end process; ~FI end block;@@ -105,11 +97,7 @@ if ~ARG[7] ~IF ~ISACTIVEENABLE[4] ~THEN and ~ARG[4] ~ELSE ~FI then ~SYM[2](~SYM[5]) <= ~TOBV[~ARG[9]][~TYP[9]]; end if;- ~RESULT <= fromSLV(~SYM[2](~SYM[4]))- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;+ ~RESULT <= fromSLV(~SYM[2](~SYM[4])); end if; end process; ~ELSE ~SYM[6] : process(~ARG[3])@@ -118,11 +106,7 @@ if ~ARG[7] ~IF ~ISACTIVEENABLE[4] ~THEN and ~ARG[4] ~ELSE ~FI then ~SYM[2](~SYM[5]) <= ~ARG[9]; end if;- ~RESULT <= ~SYM[2](~SYM[4])- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;+ ~RESULT <= ~SYM[2](~SYM[4]); end if; end process; ~FI end block;@@ -172,11 +156,7 @@ if ~ARG[8] ~IF ~ISACTIVEENABLE[4] ~THEN and ~ARG[4] ~ELSE ~FI then ~SYM[2](~SYM[5]) <= ~TOBV[~ARG[10]][~TYP[10]]; end if;- ~RESULT <= fromSLV(~SYM[2](~SYM[4]))- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;+ ~RESULT <= fromSLV(~SYM[2](~SYM[4])); end if; end process; ~ELSE ~SYM[6] : process(~ARG[3])@@ -185,11 +165,7 @@ if ~ARG[8] ~IF ~ISACTIVEENABLE[4] ~THEN and ~ARG[4] ~ELSE ~FI then ~SYM[2](~SYM[5]) <= ~ARG[10]; end if;- ~RESULT <= ~SYM[2](~SYM[4])- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;+ ~RESULT <= ~SYM[2](~SYM[4]); end if; end process; ~FI end block;
prims/vhdl/Clash_Explicit_BlockRam_File.json view
@@ -55,11 +55,7 @@ if ~ARG[8] then ~SYM[3](~SYM[6]) <= to_bitvector(~ARG[10]); end if;- ~RESULT <= to_stdlogicvector(~SYM[3](~SYM[5]))- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;+ ~RESULT <= to_stdlogicvector(~SYM[3](~SYM[5])); end if; end if; end process;~ELSE@@ -69,11 +65,7 @@ if ~ARG[8] then ~SYM[3](~SYM[6]) <= to_bitvector(~ARG[10]); end if;- ~RESULT <= to_stdlogicvector(~SYM[3](~SYM[5]))- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;+ ~RESULT <= to_stdlogicvector(~SYM[3](~SYM[5])); end if; end process;~FI ~ELSE ~IF ~ISACTIVEENABLE[4] ~THEN ~SYM[10] : process(~ARG[3])@@ -83,11 +75,7 @@ ~SYM[3](~SYM[6]) <= to_bitvector(~ARG[10]); end if; if ~ARG[4] then- ~RESULT <= to_stdlogicvector(~SYM[3](~SYM[5]))- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;+ ~RESULT <= to_stdlogicvector(~SYM[3](~SYM[5])); end if; end if; end process;~ELSE@@ -97,11 +85,7 @@ if ~ARG[8] then ~SYM[3](~SYM[6]) <= to_bitvector(~ARG[10]); end if;- ~RESULT <= to_stdlogicvector(~SYM[3](~SYM[5]))- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;+ ~RESULT <= to_stdlogicvector(~SYM[3](~SYM[5])); end if; end process;~FI ~FI end block;
prims/vhdl/Clash_Explicit_ROM.json view
@@ -26,16 +26,8 @@ ~GENSYM[romSync][6] : process (~ARG[3]) begin if (~IF~ACTIVEEDGE[Rising][0]~THENrising_edge~ELSEfalling_edge~FI(~ARG[3])~IF ~ISACTIVEENABLE[4] ~THEN and ~ARG[4] ~ELSE ~FI) then~IF ~VIVADO ~THEN- ~RESULT <= ~FROMBV[~SYM[2](~SYM[3])][~TYPO]- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;~ELSE- ~RESULT <= ~SYM[2](~SYM[3])- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;~FI+ ~RESULT <= ~FROMBV[~SYM[2](~SYM[3])][~TYPO];~ELSE+ ~RESULT <= ~SYM[2](~SYM[3]);~FI end if; end process; end block;
prims/vhdl/Clash_Explicit_ROM_File.json view
@@ -40,22 +40,14 @@ begin if (~IF~ACTIVEEDGE[Rising][1]~THENrising_edge~ELSEfalling_edge~FI(~ARG[2])) then if ~ARG[3] then- ~RESULT <= to_stdlogicvector(~SYM[2](~SYM[3]))- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;+ ~RESULT <= to_stdlogicvector(~SYM[2](~SYM[3])); end if; end if; end process;~ELSE ~SYM[7] : process (~ARG[2]) begin if (~IF~ACTIVEEDGE[Rising][1]~THENrising_edge~ELSEfalling_edge~FI(~ARG[2])) then- ~RESULT <= to_stdlogicvector(~SYM[2](~SYM[3]))- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;+ ~RESULT <= to_stdlogicvector(~SYM[2](~SYM[3])); end if; end process;~FI end block;
+ prims/vhdl/Clash_Explicit_SimIO.json view
@@ -0,0 +1,31 @@+[ { "Primitive" :+ { "name" : "Clash.Explicit.SimIO.mealyIO"+ , "primType" : "Function"+ }+ }+, { "Primitive" :+ { "name" : "Clash.Explicit.SimIO.fmapSimIO#"+ , "primType" : "Function"+ }+ }+, { "Primitive" :+ { "name" : "Clash.Explicit.SimIO.pureSimIO#"+ , "primType" : "Function"+ }+ }+, { "Primitive" :+ { "name" : "Clash.Explicit.SimIO.apSimIO#"+ , "primType" : "Function"+ }+ }+, { "Primitive" :+ { "name" : "Clash.Explicit.SimIO.bindSimIO#"+ , "primType" : "Function"+ }+ }+, { "Primitive" :+ { "name" : "Clash.Explicit.SimIO.unSimIO#"+ , "primType" : "Function"+ }+ }+]
prims/vhdl/Clash_Signal_BiSignal.json view
@@ -1,6 +1,7 @@ [ { "BlackBox" : { "name" : "Clash.Signal.BiSignal.writeToBiSignal#", "kind" : "Declaration",+ "renderVoid": "RenderVoid", "type" : "writeToBiSignal# :: HasCallStack -- ARG[0]
prims/vhdl/Clash_Signal_Internal.json view
@@ -10,40 +10,24 @@ -> a -- ARG[4] -> Signal clk a -- ARG[5] -> Signal clk a"+ , "resultName" : { "template" : "~CTXNAME" }+ , "resultInit" : { "template" : "~IF~ISINITDEFINED[0]~THEN~CONST[4]~ELSE~FI" } , "template" : "-- delay begin~IF ~ISACTIVEENABLE[3] ~THEN-~GENSYM[~RESULT_delay][0] : block- signal ~GENSYM[~RESULT_reg][1] : ~TYPO ~IF ~ISINITDEFINED[0] ~THEN := ~CONST[4] ~ELSE ~FI;+~GENSYM[~RESULT_delay][4] : process(~ARG[2]) begin- ~RESULT <= ~SYM[1];- ~GENSYM[~RESULT_dly][4] : process(~ARG[2])- begin- if ~IF~ACTIVEEDGE[Rising][0]~THENrising_edge~ELSEfalling_edge~FI(~ARG[2]) then- if ~ARG[3] then- ~SYM[1] <= ~ARG[5]- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;- end if;+ if ~IF~ACTIVEEDGE[Rising][0]~THENrising_edge~ELSEfalling_edge~FI(~ARG[2]) then+ if ~ARG[3] then+ ~RESULT <= ~ARG[5]; end if;- end process;-end block;~ELSE-~SYM[0] : block- signal ~SYM[1] : ~TYPO := ~CONST[4];+ end if;+end process;~ELSE+~SYM[4] : process(~ARG[2]) begin- ~RESULT <= ~SYM[1];- ~SYM[4] : process(~ARG[2])- begin- if ~IF~ACTIVEEDGE[Rising][0]~THENrising_edge~ELSEfalling_edge~FI(~ARG[2]) then- ~SYM[1] <= ~ARG[5]- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;- end if;- end process;-end block;~FI+ if ~IF~ACTIVEEDGE[Rising][0]~THENrising_edge~ELSEfalling_edge~FI(~ARG[2]) then+ ~RESULT <= ~ARG[5];+ end if;+end process;~FI -- delay end" } }@@ -61,84 +45,48 @@ -> a -- ARG[6] (reset value) -> Signal clk a -- ARG[7] -> Signal clk a"+ , "resultName" : { "template" : "~CTXNAME" }+ , "resultInit" : { "template" : "~IF~ISINITDEFINED[0]~THEN~CONST[5]~ELSE~FI" } , "template" :-"-- register begin~IF ~ISACTIVEENABLE[4] ~THEN-~GENSYM[~COMPNAME_register][0] : block- signal ~GENSYM[~RESULT_reg][1] : ~TYPO ~IF ~ISINITDEFINED[0] ~THEN := ~CONST[5] ~ELSE ~FI;+"-- register begin~IF ~ISACTIVEENABLE[4] ~THEN ~IF ~ISSYNC[0] ~THEN+~GENSYM[~RESULT_register][2] : process(~ARG[2]) begin- ~RESULT <= ~SYM[1]; ~IF ~ISSYNC[0] ~THEN- ~GENSYM[~RESULT_r][2] : process(~ARG[2])- begin- if ~IF~ACTIVEEDGE[Rising][0]~THENrising_edge~ELSEfalling_edge~FI(~ARG[2]) then- if ~ARG[3] = ~IF ~ISACTIVEHIGH[0] ~THEN '1' ~ELSE '0' ~FI then- ~SYM[1] <= ~CONST[6]- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;- elsif ~ARG[4] then- ~SYM[1] <= ~ARG[7]- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;- end if;- end if;- end process;~ELSE- ~SYM[2] : process(~ARG[2],~ARG[3])- begin+ if ~IF~ACTIVEEDGE[Rising][0]~THENrising_edge~ELSEfalling_edge~FI(~ARG[2]) then if ~ARG[3] = ~IF ~ISACTIVEHIGH[0] ~THEN '1' ~ELSE '0' ~FI then- ~SYM[1] <= ~CONST[6];- elsif ~IF~ACTIVEEDGE[Rising][0]~THENrising_edge~ELSEfalling_edge~FI(~ARG[2]) then- if ~ARG[4] then- ~SYM[1] <= ~ARG[7]- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;- end if;+ ~RESULT <= ~CONST[6];+ elsif ~ARG[4] then+ ~RESULT <= ~ARG[7]; end if;- end process;~FI-end block;~ELSE-~SYM[0] : block- signal ~SYM[1] : ~TYPO := ~CONST[5];+ end if;+end process;~ELSE+~SYM[2] : process(~ARG[2],~ARG[3]) begin- ~RESULT <= ~SYM[1]; ~IF ~ISSYNC[0] ~THEN- ~SYM[2] : process(~ARG[2])- begin- if ~IF~ACTIVEEDGE[Rising][0]~THENrising_edge~ELSEfalling_edge~FI(~ARG[2]) then- if ~ARG[3] = ~IF ~ISACTIVEHIGH[0] ~THEN '1' ~ELSE '0' ~FI then- ~SYM[1] <= ~CONST[6]- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;- else- ~SYM[1] <= ~ARG[7]- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;- end if;+ if ~ARG[3] = ~IF ~ISACTIVEHIGH[0] ~THEN '1' ~ELSE '0' ~FI then+ ~RESULT <= ~CONST[6];+ elsif ~IF~ACTIVEEDGE[Rising][0]~THENrising_edge~ELSEfalling_edge~FI(~ARG[2]) then+ if ~ARG[4] then+ ~RESULT <= ~ARG[7]; end if;- end process;~ELSE- ~SYM[2] : process(~ARG[2],~ARG[3])- begin+ end if;+end process;~FI~ELSE ~IF ~ISSYNC[0] ~THEN+~SYM[2] : process(~ARG[2])+begin+ if ~IF~ACTIVEEDGE[Rising][0]~THENrising_edge~ELSEfalling_edge~FI(~ARG[2]) then if ~ARG[3] = ~IF ~ISACTIVEHIGH[0] ~THEN '1' ~ELSE '0' ~FI then- ~SYM[1] <= ~CONST[6]- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;- elsif ~IF~ACTIVEEDGE[Rising][0]~THENrising_edge~ELSEfalling_edge~FI(~ARG[2]) then- ~SYM[1] <= ~ARG[7]- -- pragma translate_off- after 1 ps- -- pragma translate_on- ;+ ~RESULT <= ~CONST[6];+ else+ ~RESULT <= ~ARG[7]; end if;- end process;~FI-end block;~FI+ end if;+end process;~ELSE+~SYM[2] : process(~ARG[2],~ARG[3])+begin+ if ~ARG[3] = ~IF ~ISACTIVEHIGH[0] ~THEN '1' ~ELSE '0' ~FI then+ ~RESULT <= ~CONST[6];+ elsif ~IF~ACTIVEEDGE[Rising][0]~THENrising_edge~ELSEfalling_edge~FI(~ARG[2]) then+ ~RESULT <= ~ARG[7];+ end if;+end process;~FI~FI -- register end" } }
prims/vhdl/Clash_Sized_Internal_BitVector.json view
@@ -491,12 +491,10 @@ , "template" : "std_logic_vector(unsigned(~ARG[1]) rem unsigned(~ARG[2]))" } }-, { "BlackBox" :+, { "BlackBoxHaskell" : { "name" : "Clash.Sized.Internal.BitVector.toInteger#" , "workInfo" : "Never"- , "kind" : "Expression"- , "type" : "toInteger# :: KnownNat n => BitVector n -> Integer"- , "template" : "~IF~SIZE[~TYP[1]]~THENsigned(std_logic_vector(resize(unsigned(~ARG[1]),~SIZE[~TYPO])))~ELSEto_signed(0,64)~FI"+ , "templateFunction" : "Clash.Primitives.Sized.ToInteger.bvToIntegerVHDL" } } , { "BlackBox" :
prims/vhdl/Clash_Sized_Internal_Index.json view
@@ -121,12 +121,10 @@ , "template" : "~ARG[0] rem ~ARG[1]" } }-, { "BlackBox" :+, { "BlackBoxHaskell" : { "name" : "Clash.Sized.Internal.Index.toInteger#" , "workInfo" : "Never"- , "kind" : "Expression"- , "type" : "toInteger# :: Index n -> Integer"- , "template" : "~IF~SIZE[~TYP[0]]~THENsigned(std_logic_vector(resize(~ARG[0],~SIZE[~TYPO])))~ELSEto_signed(0,64)~FI"+ , "templateFunction" : "Clash.Primitives.Sized.ToInteger.indexToIntegerVHDL" } } , { "BlackBox" :
prims/vhdl/Clash_Sized_Internal_Signed.json view
@@ -146,11 +146,18 @@ , "template" : "-- divSigned begin ~GENSYM[divSigned][0] : block- signal ~GENSYM[quot_res][3] : ~TYP[0];+ signal ~GENSYM[resultPos][1] : boolean;+ signal ~GENSYM[dividerNeg][2] : boolean;+ signal ~GENSYM[dividend2][3] : signed(~SIZE[~TYPO] downto 0);+ signal ~GENSYM[quot_res][4] : signed(~SIZE[~TYPO] downto 0); begin- ~SYM[3] <= ~ARG[0] / ~ARG[1];- ~RESULT <= ~SYM[3] - to_signed(1,~SIZE[~TYPO]) when ~VAR[dividend][0](~VAR[dividend][0]'high) = not (~VAR[divider][1](~VAR[divider][1]'high)) else- ~SYM[3];+ ~SYM[1] <= ~VAR[dividend][0](~VAR[dividend][0]'high) = ~VAR[divider][1](~VAR[divider][1]'high);+ ~SYM[2] <= ~VAR[divider][1](~VAR[divider][1]'high) = '1';+ ~SYM[3] <= resize(~VAR[dividend][0],~SIZE[~TYPO]+1) when ~SYM[1] else+ (resize(~VAR[dividend][0],~SIZE[~TYPO]+1) - resize(~VAR[divider][1],~SIZE[~TYPO]+1) - 1) when ~SYM[2] else+ (resize(~VAR[dividend][0],~SIZE[~TYPO]+1) - resize(~VAR[divider][1],~SIZE[~TYPO]+1) + 1);+ ~SYM[4] <= ~SYM[3] / ~VAR[divider][1];+ ~RESULT <= signed(~SYM[4](~SIZE[~TYPO]-1 downto 0)); end block; -- divSigned end" }@@ -162,12 +169,10 @@ , "template" : "~ARG[0] mod ~ARG[1]" } }-, { "BlackBox" :+, { "BlackBoxHaskell" : { "name" : "Clash.Sized.Internal.Signed.toInteger#" , "workInfo" : "Never"- , "kind" : "Expression"- , "type" : "toInteger# :: Signed n -> Integer"- , "template" : "~IF~SIZE[~TYP[0]]~THENresize(~ARG[0],~SIZE[~TYPO])~ELSEto_signed(0,64)~FI"+ , "templateFunction" : "Clash.Primitives.Sized.ToInteger.signedToIntegerVHDL" } } , { "BlackBox" :
prims/vhdl/Clash_Sized_Internal_Unsigned.json view
@@ -130,12 +130,10 @@ , "template" : "~ARG[0] rem ~ARG[1]" } }-, { "BlackBox" :+, { "BlackBoxHaskell" : { "name" : "Clash.Sized.Internal.Unsigned.toInteger#" , "workInfo" : "Never"- , "kind" : "Expression"- , "type" : "toInteger# :: Unsigned n -> Integer"- , "template" : "~IF~SIZE[~TYP[0]]~THENsigned(std_logic_vector(resize(~ARG[0],~SIZE[~TYPO])))~ELSEto_signed(0,64)~FI"+ , "templateFunction" : "Clash.Primitives.Sized.ToInteger.unsignedToIntegerVHDL" } } , { "BlackBox" :
prims/vhdl/Clash_Sized_Vector.json view
@@ -266,52 +266,6 @@ } } , { "BlackBox" :- { "name" : "Clash.Sized.Vector.fold"- , "workInfo" : "Never"- , "kind" : "Declaration"- , "type" : "fold :: (a -> a -> a) -> Vec (n+1) a -> a"- , "comment" : "THIS ONLY WORKS FOR POWER OF TWO LENGTH VECTORS"- , "template" :-"-- fold begin-~GENSYM[fold][0] : block- -- given a level and a depth, calculate the corresponding index into the- -- intermediate array- function ~GENSYM[depth2Index][1] (levels,depth : in natural) return natural is- begin- return (2 ** levels - 2 ** depth);- end function;-~IF ~VIVADO ~THEN- type ~GENSYM[fold_res_type][2] is array(natural range <>) of ~TYPO;- signal ~GENSYM[intermediate][3] : ~SYM[2](0 to (2*~LENGTH[~TYP[1]])-2);~ELSE- signal ~SYM[3] : ~TYPM[1](0 to (2*~LENGTH[~TYP[1]])-2);~FI- constant ~GENSYM[levels][5] : natural := natural (ceil (log2 (real (~LENGTH[~TYP[1]]))));-begin- -- put input array into the first half of the intermediate array~IF ~VIVADO ~THEN- ~SYM[6] : for ~SYM[7] in 0 to (~LENGTH[~TYP[1]] - 1) generate- ~SYM[3](~SYM[7]) <= fromSLV(~VAR[vec][1](~SYM[7]));- end generate;~ELSE- ~SYM[3](0 to ~LENGTH[~TYP[1]]-1) <= ~VAR[vec][1];~FI-- -- Create the tree of instantiated components- ~GENSYM[make_tree][8] : if ~SYM[5] /= 0 generate- ~GENSYM[tree_depth][9] : for ~GENSYM[d][10] in ~SYM[5]-1 downto 0 generate- ~GENSYM[tree_depth_loop][11] : for ~GENSYM[i][12] in 0 to (natural(2**~SYM[10]) - 1) generate- ~INST 0- ~OUTPUT <= ~SYM[3](~SYM[1](~SYM[5]+1,~SYM[10]+1)+~SYM[12])~ ~TYPO~- ~INPUT <= ~SYM[3](~SYM[1](~SYM[5]+1,~SYM[10]+2)+(2*~SYM[12]))~ ~TYPO~- ~INPUT <= ~SYM[3](~SYM[1](~SYM[5]+1,~SYM[10]+2)+(2*~SYM[12])+1)~ ~TYPO~- ~INST- end generate;- end generate;- end generate;-- -- The last element of the intermediate array holds the result- ~RESULT <= ~SYM[3]((2*~LENGTH[~TYP[1]])-2);-end block;--- fold end"- }- }-, { "BlackBox" : { "name" : "Clash.Sized.Vector.index_int" , "kind" : "Declaration" , "type" : "index_int :: KnownNat n => Vec n a -> Int -> a"
prims/vhdl/GHC_Classes.json view
@@ -38,15 +38,22 @@ , "kind" : "Declaration" , "type" : "divInt# :: Int# -> Int# -> Int#" , "template" :-"-- divInt begin+"-- divInt# begin ~GENSYM[divInt][0] : block- signal ~GENSYM[quot_res][1] : ~TYP[1];+ signal ~GENSYM[resultPos][1] : boolean;+ signal ~GENSYM[dividerNeg][2] : boolean;+ signal ~GENSYM[dividend2][3] : signed(~SIZE[~TYPO] downto 0);+ signal ~GENSYM[quot_res][4] : signed(~SIZE[~TYPO] downto 0); begin- ~SYM[1] <= ~ARG[0] / ~ARG[1];- ~RESULT <= ~SYM[1] - 1 when ((~ARG[0] = abs ~ARG[0]) /= (~ARG[1] = abs ~ARG[1])) else- ~SYM[1];+ ~SYM[1] <= ~VAR[dividend][0](~VAR[dividend][0]'high) = ~VAR[divider][1](~VAR[divider][1]'high);+ ~SYM[2] <= ~VAR[divider][1](~VAR[divider][1]'high) = '1';+ ~SYM[3] <= resize(~VAR[dividend][0],~SIZE[~TYPO]+1) when ~SYM[1] else+ (resize(~VAR[dividend][0],~SIZE[~TYPO]+1) - resize(~VAR[divider][1],~SIZE[~TYPO]+1) - 1) when ~SYM[2] else+ (resize(~VAR[dividend][0],~SIZE[~TYPO]+1) - resize(~VAR[divider][1],~SIZE[~TYPO]+1) + 1);+ ~SYM[4] <= ~SYM[3] / ~VAR[divider][1];+ ~RESULT <= signed(~SYM[4](~SIZE[~TYPO]-1 downto 0)); end block;--- divInt end"+-- divInt# end" } } , { "BlackBox" :
prims/vhdl/GHC_Integer_Type.json view
@@ -42,11 +42,18 @@ , "template" : "-- divInteger begin ~GENSYM[divInteger][0] : block- signal ~GENSYM[quot_res][1] : ~TYP[1];+ signal ~GENSYM[resultPos][1] : boolean;+ signal ~GENSYM[dividerNeg][2] : boolean;+ signal ~GENSYM[dividend2][3] : signed(~SIZE[~TYPO] downto 0);+ signal ~GENSYM[quot_res][4] : signed(~SIZE[~TYPO] downto 0); begin- ~SYM[1] <= ~ARG[0] / ~ARG[1];- ~RESULT <= ~SYM[1] - 1 when ((~ARG[0] = abs ~ARG[0]) /= (~ARG[1] = abs ~ARG[1])) else- ~SYM[1];+ ~SYM[1] <= ~VAR[dividend][0](~VAR[dividend][0]'high) = ~VAR[divider][1](~VAR[divider][1]'high);+ ~SYM[2] <= ~VAR[divider][1](~VAR[divider][1]'high) = '1';+ ~SYM[3] <= resize(~VAR[dividend][0],~SIZE[~TYPO]+1) when ~SYM[1] else+ (resize(~VAR[dividend][0],~SIZE[~TYPO]+1) - resize(~VAR[divider][1],~SIZE[~TYPO]+1) - 1) when ~SYM[2] else+ (resize(~VAR[dividend][0],~SIZE[~TYPO]+1) - resize(~VAR[divider][1],~SIZE[~TYPO]+1) + 1);+ ~SYM[4] <= ~SYM[3] / ~VAR[divider][1];+ ~RESULT <= signed(~SYM[4](~SIZE[~TYPO]-1 downto 0)); end block; -- divInteger end" }@@ -56,6 +63,38 @@ , "kind" : "Expression" , "type" : "modInteger :: Integer -> Integer -> Integer" , "template" : "~ARG[0] mod ~ARG[1]"+ }+ }+, { "BlackBox" :+ { "name" : "GHC.Integer.Type.divModInteger"+ , "kind" : "Declaration"+ , "type" : "divModInteger :: Integer -> Integer -> (# Integer, Integer #)"+ , "template" :+"-- divModInteger begin+~GENSYM[divModInteger][0] : block+ signal ~GENSYM[resultPos][1] : boolean;+ signal ~GENSYM[dividerNeg][2] : boolean;+ signal ~GENSYM[dividend2][3] : signed(~SIZE[~TYP[0]] downto 0);+ signal ~GENSYM[quot_res][4] : signed(~SIZE[~TYP[0]] downto 0);+ signal ~GENSYM[div_res][5] : signed(~SIZE[~TYP[0]]-1 downto 0);+begin+ ~SYM[1] <= ~VAR[dividend][0](~VAR[dividend][0]'high) = ~VAR[divider][1](~VAR[divider][1]'high);+ ~SYM[2] <= ~VAR[divider][1](~VAR[divider][1]'high) = '1';+ ~SYM[3] <= resize(~VAR[dividend][0],~SIZE[~TYP[0]]+1) when ~SYM[1] else+ (resize(~VAR[dividend][0],~SIZE[~TYP[0]]+1) - resize(~VAR[divider][1],~SIZE[~TYP[0]]+1) - 1) when ~SYM[2] else+ (resize(~VAR[dividend][0],~SIZE[~TYP[0]]+1) - resize(~VAR[divider][1],~SIZE[~TYP[0]]+1) + 1);+ ~SYM[4] <= ~SYM[3] / ~VAR[divider][1];+ ~SYM[5] <= signed(~SYM[4](~SIZE[~TYP[0]]-1 downto 0));+ ~RESULT <= (~SYM[5], ~VAR[dividend][0] mod ~VAR[divider][1]);+end block;+-- divModInteger end"+ }+ }+, { "BlackBox" :+ { "name" : "GHC.Integer.Type.quotRemInteger"+ , "kind" : "Expression"+ , "type" : "quotRemInteger :: Integer -> Integer -> (# Integer, Integer #)"+ , "template" : "(~ARG[0] / ~ARG[1], ~ARG[0] rem ~ARG[1])" } } , { "BlackBox" :
prims/vhdl/GHC_Prim.json view
@@ -549,7 +549,7 @@ , "type" : "clz16 :: Word# -> Word#" , "template" : "-- clz16 begin-clz16~SYM[0] : block+~GENSYM[clz16][0] : block function ~GENSYM[enc][1] (constant a : unsigned(1 downto 0)) return unsigned is begin case a is@@ -594,7 +594,7 @@ , "type" : "clz32 :: Word# -> Word#" , "template" : "-- clz32 begin-clz32~SYM[0] : block+~GENSYM[clz32][0] : block function ~GENSYM[enc][1] (constant a : unsigned(1 downto 0)) return unsigned is begin case a is@@ -641,7 +641,7 @@ , "type" : "clz64 :: Word# -> Word#" , "template" : "-- clz64 begin-clz64~SYM[0] : block+~GENSYM[clz64][0] : block function ~GENSYM[enc][1] (constant a : unsigned(1 downto 0)) return unsigned is begin case a is@@ -690,7 +690,7 @@ , "type" : "clz :: Word# -> Word#" , "template" : "-- clz begin-clz~SYM[0] : block+~GENSYM[clz][0] : block function ~GENSYM[enc][1] (constant a : unsigned(1 downto 0)) return unsigned is begin case a is
src/Clash/Annotations/TopEntity/Extra.hs view
@@ -9,12 +9,14 @@ module Clash.Annotations.TopEntity.Extra where import Clash.Annotations.TopEntity (TopEntity, PortName)+import Clash.Netlist.Types (TopEntityT) import Language.Haskell.TH.Syntax (ModName, Name, NameFlavour, NameSpace, PkgName, OccName) import Data.Binary (Binary) import Data.Hashable (Hashable) import Control.DeepSeq (NFData) +instance Binary TopEntityT instance Binary TopEntity instance Binary PortName @@ -25,6 +27,7 @@ instance Binary NameSpace instance Binary PkgName +instance Hashable TopEntityT instance Hashable TopEntity instance Hashable PortName @@ -35,6 +38,7 @@ instance Hashable PkgName instance Hashable OccName +instance NFData TopEntityT instance NFData TopEntity instance NFData PortName
src/Clash/Backend.hs view
@@ -5,6 +5,7 @@ Maintainer : Christiaan Baaij <christiaan.baaij@gmail.com> -} +{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} module Clash.Backend where@@ -29,6 +30,27 @@ import Clash.Netlist.BlackBox.Types import Clash.Annotations.Primitive (HDL)++#ifdef CABAL+import qualified Paths_clash_lib+import qualified Data.Version+#else+import qualified System.FilePath+#endif++primsRoot :: IO FilePath+#ifdef CABAL+primsRoot = Paths_clash_lib.getDataFileName "prims"+#else+primsRoot = return ("clash-lib" System.FilePath.</> "prims")+#endif++clashVer :: String+#ifdef CABAL+clashVer = Data.Version.showVersion Paths_clash_lib.version+#else+clashVer = "development"+#endif type ModName = Identifier
src/Clash/Backend/SystemVerilog.hs view
@@ -7,14 +7,12 @@ Generate SystemVerilog for assorted Netlist datatypes -} -{-# LANGUAGE CPP #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecursiveDo #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE RecursiveDo #-}+{-# LANGUAGE TemplateHaskell #-} module Clash.Backend.SystemVerilog (SystemVerilogState) where @@ -37,35 +35,30 @@ import qualified Data.Text.Lazy as Text import qualified Data.Text as TextS import Data.Text.Prettyprint.Doc.Extra-#ifdef CABAL-import qualified Data.Version-#endif import qualified System.FilePath import Clash.Annotations.Primitive (HDL (..)) import Clash.Annotations.BitRepresentation.Internal- (ConstrRepr'(..))+ (ConstrRepr'(..), DataRepr'(..)) import Clash.Annotations.BitRepresentation.ClashLib (bitsToBits) import Clash.Annotations.BitRepresentation.Util (BitOrigin(Lit, Field), bitOrigins, bitRanges) import Clash.Core.Var (Attr'(..)) import Clash.Backend-import Clash.Backend.Verilog (bits, bit_char, encodingNote, exprLit, include, uselibs)+import Clash.Backend.Verilog+ (bits, bit_char, encodingNote, exprLit, include, noEmptyInit, uselibs) import Clash.Netlist.BlackBox.Types (HdlSyn (..)) import Clash.Netlist.BlackBox.Util (extractLiterals, renderBlackBox, renderFilePath) import Clash.Netlist.Id (IdType (..), mkBasicId') import Clash.Netlist.Types hiding (_intWidth, intWidth) import Clash.Netlist.Util hiding (mkIdentifier, extendIdentifier)+import Clash.Signal.Internal (ActiveEdge (..)) import Clash.Util- (SrcSpan, noSrcSpan, curLoc, makeCached, (<:>), first, on, traceIf)+ (SrcSpan, noSrcSpan, curLoc, makeCached, (<:>), first, on, traceIf, indexNote) import Clash.Util.Graph (reverseTopSort) -#ifdef CABAL-import qualified Paths_clash_lib-#endif- -- | State for the 'Clash.Backend.SystemVerilog.SystemVerilogM' monad: data SystemVerilogState = SystemVerilogState@@ -95,16 +88,6 @@ makeLenses ''SystemVerilogState -squote :: Mon (State SystemVerilogState) Doc-squote = string "'"--primsRoot :: IO FilePath-#ifdef CABAL-primsRoot = Paths_clash_lib.getDataFileName "prims"-#else-primsRoot = return ("clash-lib" System.FilePath.</> "prims")-#endif- instance Backend SystemVerilogState where initBackend = SystemVerilogState HashSet.empty HashMapS.empty HashMap.empty 0 "" HashMapS.empty [] noSrcSpan [] [] []@@ -174,10 +157,10 @@ blockDecl _ ds = do decs <- decls ds if isEmpty decs- then indent 2 (insts ds)+ then insts ds else pure decs <> line <>- indent 2 (insts ds)+ insts ds unextend = return rmSlash addIncludes inc = includes %= (inc++) addLibraries libs = libraries %= (libs ++)@@ -256,11 +239,6 @@ verilog = commentHeader <> line <> timescale <> line <> module_ c-#ifdef CABAL- clashVer = Data.Version.showVersion Paths_clash_lib.version-#else- clashVer = "development"-#endif commentHeader = "/* AUTOMATICALLY GENERATED SYSTEMVERILOG-2005 SOURCE CODE." <> line <> "** GENERATED BY CLASH " <> string (Text.pack clashVer) <> ". DO NOT MODIFY."@@ -562,8 +540,8 @@ modBody = indent 2 (decls (declarations c)) <> line <> line <> indent 2 (insts (declarations c)) modEnding = "endmodule" - inPorts = sequence [ sigPort (Nothing,isBiSignalIn ty) (i,ty) | (i,ty) <- inputs c ]- outPorts = sequence [ sigPort (Just wr,False) p | (wr, p) <- outputs c ]+ inPorts = sequence [ sigPort (Nothing,isBiSignalIn ty) (i,ty) Nothing | (i,ty) <- inputs c ]+ outPorts = sequence [ sigPort (Just wr,False) p iEM | (wr, p, iEM) <- outputs c ] wr2ty (Nothing,isBidirectional) | isBidirectional@@ -574,9 +552,11 @@ = "output" -- map a port to its verilog type, port name, and any encoding notes- sigPort (wr2ty -> portTy) (nm, hwTy)+ sigPort (wr2ty -> portTy) (nm, hwTy) iEM = addAttrs (hwTypeAttrs hwTy)- (portTy <+> sigDecl (stringS nm) hwTy <+> encodingNote hwTy)+ (portTy <+> sigDecl (stringS nm) hwTy <> iE <+> encodingNote hwTy)+ where+ iE = maybe emptyDoc (noEmptyInit . expr_ False) iEM -- slightly more readable than 'tupled', makes the output Haskell-y-er commafy v = (comma <> space) <> pure v @@ -599,8 +579,8 @@ addSeen :: Component -> SystemVerilogM () addSeen c = do let iport = map fst (inputs c)- oport = map (fst.snd) $ outputs c- nets = mapMaybe (\case {NetDecl' _ _ i _ -> Just i; _ -> Nothing}) $ declarations c+ oport = map (fst . (\(_,x,_)->x)) $ outputs c+ nets = mapMaybe (\case {NetDecl' _ _ i _ _ -> Just i; _ -> Nothing}) $ declarations c Mon (idSeen %= (HashMapS.unionWith max (HashMapS.fromList (concatMap (map (,0)) [iport,oport,nets])))) Mon (oports .= oport) @@ -644,6 +624,7 @@ Bit -> "logic" Bool -> "logic" String -> "string"+ FileType -> "integer" _ -> logicOrWire <+> brackets (int (typeSize t -1) <> colon <> int 0) sigDecl :: SystemVerilogM Doc -> HWType -> SystemVerilogM Doc@@ -742,13 +723,14 @@ _ -> punctuate' semi (A.pure dsDoc) decl :: Declaration -> SystemVerilogM (Maybe Doc)-decl (NetDecl' noteM _ id_ tyE) =+decl (NetDecl' noteM _ id_ tyE iEM) = Just A.<$> maybe id addNote noteM (addAttrs attrs (typ tyE)) where- typ (Left ty) = stringS ty <+> stringS id_- typ (Right ty) = sigDecl (stringS id_) ty+ typ (Left ty) = stringS ty <+> stringS id_ <> iE+ typ (Right ty) = sigDecl (stringS id_) ty <> iE addNote n = mappend ("//" <+> stringS n <> line) attrs = fromMaybe [] (hwTypeAttrs A.<$> either (const Nothing) Just tyE)+ iE = maybe emptyDoc (noEmptyInit . expr_ False) iEM decl _ = return Nothing @@ -773,7 +755,7 @@ insts :: [Declaration] -> SystemVerilogM Doc insts [] = emptyDoc-insts (TickDecl id_:ds) = "//" <+> stringS id_ <> line <> insts ds+insts (TickDecl id_:ds) = comment "//" id_ <> line <> insts ds insts (d:ds) = do docM <- inst_ d case docM of@@ -818,8 +800,8 @@ patLitCustom' size cRepr patLitCustom x y = error $ $(curLoc) ++ unwords- [ "You can only pass CustomSP / CustomSum and a NumLit to this function,"- , "not", show x, "and", show y]+ [ "You can only pass CustomSP / CustomSum / CustomProduct and a NumLit to"+ , "this function, not", show x, "and", show y] patMod :: HWType -> Literal -> Literal patMod hwTy (NumLit i) = NumLit (i `mod` (2 ^ typeSize hwTy))@@ -880,12 +862,15 @@ where (t,f) = if b then (l,r) else (r,l) -inst_ (CondAssignment id_ _ scrut scrutTy@(CustomSP _ _ _ _) es) =+inst_ (CondAssignment id_ _ scrut scrutTy@(CustomSP {}) es) = inst_' id_ scrut scrutTy es -inst_ (CondAssignment id_ _ scrut scrutTy@(CustomSum _ _ _ _) es) =+inst_ (CondAssignment id_ _ scrut scrutTy@(CustomSum {}) es) = inst_' id_ scrut scrutTy es +inst_ (CondAssignment id_ _ scrut scrutTy@(CustomProduct {}) es) =+ inst_' id_ scrut scrutTy es+ inst_ (CondAssignment id_ ty scrut scrutTy es) = fmap Just $ do { syn <- Mon hdlSyn ; p <- Mon $ use oports@@ -924,21 +909,140 @@ inst_ (BlackBoxD _ libs imps inc bs bbCtx) = fmap Just (Mon (column (renderBlackBox libs imps inc bs bbCtx))) -inst_ (NetDecl' _ _ _ _) = return Nothing+inst_ (Seq ds) = Just <$> seqs ds +inst_ (NetDecl' {}) = return Nothing++-- | Render a data constructor application for data constructors having a+-- custom bit representation.+customReprDataCon+ :: DataRepr'+ -- ^ Custom representation of data type+ -> ConstrRepr'+ -- ^ Custom representation of a specific constructor of @dataRepr@+ -> [(HWType, Expr)]+ -- ^ Arguments applied to constructor+ -> SystemVerilogM Doc+customReprDataCon dataRepr constrRepr args =+ braces $ hcat $ punctuate ", " $ mapM range' origins+ where+ size = drSize dataRepr++ -- Build bit representations for all constructor arguments+ argExprs = map (uncurry toSLV) args :: [SystemVerilogM Doc]++ -- Spread bits of constructor arguments using masks+ origins = bitOrigins dataRepr constrRepr :: [BitOrigin]++ range'+ :: BitOrigin+ -> SystemVerilogM Doc+ range' (Lit (bitsToBits -> ns)) =+ int (length ns) <> squote <> "b" <> hcat (mapM (bit_char undefValue) ns)+ range' (Field n start end) =+ -- We want to select the bits starting from 'start' downto and including+ -- 'end'. We cannot use slice notation in Verilog, as the preceding+ -- expression might not be an identifier.+ let fsize = start - end + 1 in+ let expr' = argExprs !! n in++ if | fsize == size ->+ -- If sizes are equal, rotating / resizing amounts to doing nothing+ expr'+ | end == 0 ->+ -- Rotating is not necessary if relevant bits are already at the end+ int fsize <> squote <> parens expr'+ | otherwise ->+ -- Select bits 'start' downto and including 'end'+ let rotated = parens expr' <+> ">>" <+> int end in+ int fsize <> squote <> parens rotated++seq_ :: Seq -> SystemVerilogM Doc+seq_ (AlwaysClocked edge clk ds) =+ "always @" <>+ parens (case edge of {Rising -> "posedge"; _ -> "negedge"} <+>+ expr_ False clk) <+> "begin" <> line <>+ indent 2 (seqs ds) <> line <>+ "end"++seq_ (Initial ds) =+ "initial begin" <> line <>+ indent 2 (seqs ds) <> line <>+ "end"++seq_ (AlwaysComb ds) =+ "always @* begin" <> line <>+ indent 2 (seqs ds) <> line <>+ "end"++seq_ (Branch scrut scrutTy es) =+ "case" <> parens (expr_ True scrut) <> line <>+ (indent 2 $ vcat $ conds es) <> line <>+ "endcase"+ where+ conds :: [(Maybe Literal,[Seq])] -> SystemVerilogM [Doc]+ conds [] =+ return []+ conds [(_,sq)] =+ ("default" <+> colon <+> "begin" <> line <>+ indent 2 (seqs sq) <> line <>+ "end") <:> return []+ conds ((Nothing,sq):_) =+ ("default" <+> colon <+> "begin" <> line <>+ indent 2 (seqs sq) <> line <>+ "end") <:> return []+ conds ((Just c ,sq):es') =+ (exprLitSV (Just (scrutTy,conSize scrutTy)) c <+> colon <+> "begin" <> line <>+ indent 2 (seqs sq) <> line <>+ "end") <:> conds es'++seq_ (SeqDecl sd) = case sd of+ Assignment id_ e ->+ stringS id_ <+> equals <+> expr_ False e <> semi++ BlackBoxD {} ->+ fromMaybe <$> emptyDoc <*> inst_ sd++ Seq ds ->+ seqs ds++ _ -> error (show sd)++seqs :: [Seq] -> SystemVerilogM Doc+seqs [] = emptyDoc+seqs (SeqDecl (TickDecl id_):ds) = "//" <+> stringS id_ <> line <> seqs ds+seqs (d:ds) = seq_ d <> line <> line <> seqs ds+ -- | Turn a Netlist expression into a SystemVerilog expression expr_ :: Bool -- ^ Enclose in parentheses? -> Expr -- ^ Expr to convert -> SystemVerilogM Doc-expr_ _ (Literal sizeM lit) = exprLitSV sizeM lit-expr_ _ (Identifier id_ Nothing) = stringS id_-expr_ _ (Identifier id_ (Just (Indexed (CustomSP _id _dataRepr _size args,dcI,fI)))) =- expFromSLV resultType (braces $ hcat $ punctuate ", " $ sequence ranges)- where- (ConstrRepr' _name _n _mask _value anns, _, argTys) = args !! dcI- resultType = argTys !! fI- ranges = map range' $ bitRanges (anns !! fI)- range' (start, end) = stringS id_ <> brackets (int start <> ":" <> int end)+expr_ _ (Literal sizeM lit) = exprLitSV sizeM lit+expr_ _ (Identifier id_ Nothing) = stringS id_+expr_ _ (Identifier id_ (Just (Indexed (CustomSP _id dataRepr _size args,dcI,fI)))) =+ case fieldTy of+ Void {} ->+ error (unexpectedProjectionErrorMsg dataRepr dcI fI)+ _ ->+ expFromSLV fieldTy (braces $ hcat $ punctuate ", " $ sequence ranges)+ where+ (ConstrRepr' _name _n _mask _value anns, _, fieldTypes) = args !! dcI+ ranges = map range' $ bitRanges (anns !! fI)+ range' (start, end) = stringS id_ <> brackets (int start <> ":" <> int end)+ fieldTy = indexNote ($(curLoc) ++ "panic") fieldTypes fI++expr_ _ (Identifier id_ (Just (Indexed (CustomProduct _id dataRepr _size _maybeFieldNames args,dcI,fI)))) =+ case fieldTy of+ Void {} ->+ error (unexpectedProjectionErrorMsg dataRepr dcI fI)+ _ ->+ expFromSLV fieldTy (braces $ hcat $ punctuate ", " $ sequence ranges)+ where+ (anns, fieldTypes) = unzip args+ ranges = map range' $ bitRanges (anns !! fI)+ range' (start, end) = stringS id_ <> brackets (int start <> ":" <> int end)+ fieldTy = indexNote ($(curLoc) ++ "panic") fieldTypes fI+ expr_ _ (Identifier id_ (Just (Indexed (ty@(SP _ args),dcI,fI)))) = fromSLV argTy id_ start end where argTys = snd $ args !! dcI@@ -996,40 +1100,31 @@ start = typeSize ty - 1 end = typeSize ty - conSize ty -expr_ b (Identifier id_ (Just (Nested m1 m2))) = case nestM m1 m2 of- Just m3 -> expr_ b (Identifier id_ (Just m3))- _ -> case nestSP 0 m1 m2 of- Just (argTy,start,end) -> fromSLV argTy id_ start end- _ -> do- k <- expr_ b (Identifier id_ (Just m1))- expr_ b (Identifier (Text.toStrict (renderOneLine k)) (Just m2))+expr_ _ (Identifier id_ (Just m@Nested {})) = case modifier 0 [] m of+ Nothing -> stringS id_+ Just (mods,resTy) -> do+ nm <- Mon $ use modNm+ pkgCtx <- Mon $ use tyPkgCtx+ let prefix = if pkgCtx then emptyDoc else stringS nm <> "_types::"+ let e = stringS id_ <> hcat (mapM (either bracketNMod bracketNMod) (reverse mods))+ case resTy of+ Signed _ -> "$signed" <> parens e+ Vector {}+ | Left (NRange {}):_ <- mods+ -> e+ | otherwise -> do+ Mon (tyCache %= HashSet.insert resTy)+ prefix <> tyName resTy <> "_from_lv" <> parens e+ RTree {}+ | Left (NRange {}):_ <- mods+ -> e+ | otherwise -> do+ Mon (tyCache %= HashSet.insert resTy)+ prefix <> tyName resTy <> "_from_lv" <> parens e+ _ -> e where- nestSP offset k1 k2 = case goSP offset k1 of- Just (_,_,e) -> goSP e k2- _ -> Nothing-- goSP offset (Indexed (ty@(SP _ args),dcI,fI)) = Just (argTy,start+offset,end+offset)- where- argTys = snd $ args !! dcI- argTy = argTys !! fI- argSize = typeSize argTy- other = otherSize argTys (fI-1)- start = typeSize ty - 1 - conSize ty - other- end = start - argSize + 1--- goSP offset (Indexed (CustomSP _id _dataRepr _size args,dcI,fI)) =- case bitRanges (anns !! fI) of- [(start,end)] -> Just (argTy,start+offset,end+offset)- _ -> Nothing-- where- (ConstrRepr' _name _n _mask _value anns, _, argTys) = args !! dcI- argTy = argTys !! fI-- goSP offset (Nested k1 k2) = nestSP offset k1 k2-- goSP _ _ = Nothing+ bracketNMod (NElem i) = brackets (int i)+ bracketNMod (NRange s e) = brackets (int s <> colon <> int e) -- See [Note] integer projection expr_ _ (Identifier id_ (Just (Indexed ((Signed w),_,_)))) = do@@ -1096,39 +1191,13 @@ expr_ _ (DataCon ty@(CustomSum _ _ _ tys) (DC (_,i)) []) = let (ConstrRepr' _ _ _ value _) = fst $ tys !! i in int (typeSize ty) <> squote <> "d" <> int (fromIntegral value)-expr_ _ (DataCon (CustomSP _ dataRepr size args) (DC (_,i)) es) =- braces $ hcat $ punctuate ", " $ mapM range' origins- where- (cRepr, _, argTys) = args !! i-- -- Build bit representations for all constructor arguments- argExprs = zipWith toSLV argTys es :: [SystemVerilogM Doc]-- -- Spread bits of constructor arguments using masks- origins = bitOrigins dataRepr cRepr :: [BitOrigin]-- range'- :: BitOrigin- -> SystemVerilogM Doc- range' (Lit (bitsToBits -> ns)) =- int (length ns) <> squote <> "b" <> hcat (mapM (bit_char undefValue) ns)- range' (Field n start end) =- -- We want to select the bits starting from 'start' downto and including- -- 'end'. We cannot use slice notation in Verilog, as the preceding- -- expression might not be an identifier.- let fsize = start - end + 1 in- let expr' = argExprs !! n in+expr_ _ (DataCon (CustomSP _ dataRepr _size args) (DC (_,i)) es) =+ let (cRepr, _, argTys) = args !! i in+ customReprDataCon dataRepr cRepr (zip argTys es)+expr_ _ (DataCon (CustomProduct _ dataRepr _size _labels tys) _ es) |+ DataRepr' _typ _size [cRepr] <- dataRepr =+ customReprDataCon dataRepr cRepr (zip (map snd tys) es) - if | fsize == size ->- -- If sizes are equal, rotating / resizing amounts to doing nothing- expr'- | end == 0 ->- -- Rotating is not necessary if relevant bits are already at the end- int fsize <> squote <> parens expr'- | otherwise ->- -- Select bits 'start' downto and including 'end'- let rotated = parens expr' <+> ">>" <+> int end in- int fsize <> squote <> parens rotated expr_ _ (DataCon (Product _ _ tys) _ es) = listBraces (zipWithM toSLV tys es) expr_ _ (BlackBoxE pNm _ _ _ _ bbCtx _)@@ -1197,7 +1266,7 @@ expr_ b (ConvBV topM t True e) = do nm <- Mon $ use modNm pkgCtx <- Mon $ use tyPkgCtx- let prefix = if pkgCtx then stringS nm <> "_types::" else emptyDoc+ let prefix = if pkgCtx then emptyDoc else stringS nm <> "_types::" case t of Vector {} -> do Mon (tyCache %= HashSet.insert t)@@ -1212,7 +1281,7 @@ expr_ b (ConvBV topM t False e) = do nm <- Mon $ use modNm pkgCtx <- Mon $ use tyPkgCtx- let prefix = if pkgCtx then stringS nm <> "_types::" else emptyDoc+ let prefix = if pkgCtx then emptyDoc else stringS nm <> "_types::" case t of Vector {} -> do Mon (tyCache %= HashSet.insert t)@@ -1285,3 +1354,172 @@ punctuate' :: Monad m => Mon m Doc -> Mon m [Doc] -> Mon m Doc punctuate' s d = vcat (punctuate s d) <> s+++data NMod+ = NRange Int Int+ | NElem Int++-- | Calculate the beginning and end index into a variable, to get the+-- desired field. Also returns the HWType of the result.+--+-- NB: returns a list of slices and indices when selections are into vectors and+-- rtrees. Left -> index/slice from an unpacked array; Right -> slice from a+-- packed type+modifier+ :: Int+ -- ^ Offset, only used when we have nested modifiers+ -> [Either NMod NMod]+ -- ^ Ranges selected so far+ -> Modifier+ -> Maybe ([Either NMod NMod],HWType)+modifier offset mods (Sliced (BitVector _,start,end)) =+ let m = Right (NRange (start+offset) (end+offset)) in+ case mods of+ Right {}:rest -> Just (m:rest, BitVector (start-end+1))+ _ -> Just (m:mods, BitVector (start-end+1))++modifier offset mods (Indexed (ty@(SP _ args),dcI,fI)) =+ Just (Right (NRange (start+offset) (end+offset)):mods, argTy)+ where+ argTys = snd $ args !! dcI+ argTy = argTys !! fI+ argSize = typeSize argTy+ other = otherSize argTys (fI-1)+ start = typeSize ty - 1 - conSize ty - other+ end = start - argSize + 1++modifier offset mods (Indexed (ty@(Product _ _ argTys),_,fI)) =+ let m = Right (NRange (start+offset) (end+offset)) in+ case mods of+ Right {}:rest -> Just (m:rest, argTy)+ _ -> Just (m:mods,argTy)+ where+ argTy = argTys !! fI+ argSize = typeSize argTy+ otherSz = otherSize argTys (fI - 1)+ start = typeSize ty - 1 - otherSz+ end = start - argSize + 1++modifier offset mods (Indexed (ty@(Vector _ argTy),1,0)) = case mods of+ Right {}:rest -> Just (Right (NRange (start+offset) (end+offset)):rest, argTy)+ Left (NRange b _):rest -> Just (Left (NElem b):rest,argTy)+ _ -> Just (Left (NElem 0):mods,argTy)+ where+ argSize = typeSize argTy+ start = typeSize ty - 1+ end = start - argSize + 1++modifier offset mods (Indexed (ty@(Vector n argTy),1,1)) = case mods of+ Right {}:rest -> Just (Right (NRange (start+offset) offset):rest, Vector (n-1) argTy)+ Left (NRange b e):rest -> Just (Left (NRange (b+1) e):rest, Vector (n-1) argTy)+ _ -> Just (Left (NRange 1 (n-1)):mods, Vector (n-1) argTy)+ where+ argSize = typeSize argTy+ start = typeSize ty - argSize - 1++modifier offset mods (Indexed (ty@(RTree 0 argTy),0,0)) = case mods of+ Right {}:rest -> Just (Right (NRange (start+offset) offset):rest, argTy)+ Left (NRange b _):rest -> Just (Left (NElem b):rest,argTy)+ _ -> Just (Left (NElem 0):mods,argTy)+ where+ start = typeSize ty - 1++modifier offset mods (Indexed (ty@(RTree d argTy),1,0)) = case mods of+ Right {}:rest -> Just (Right (NRange (start+offset) (end+offset)):rest, RTree (d-1) argTy)+ Left (NRange b _):rest -> Just (Left (NRange b (b+lhsSz-1)):rest,RTree (d-1) argTy)+ _ -> Just (Left (NRange 0 (lhsSz-1)):mods,RTree (d-1) argTy)+ where+ start = typeSize ty - 1+ end = typeSize ty `div` 2+ lhsSz = (d-1)^(2 :: Int)++modifier offset mods (Indexed (ty@(RTree d argTy),1,1)) = case mods of+ Right {}:rest -> Just (Right (NRange (start+offset) offset):rest, RTree (d-1) argTy)+ Left (NRange _ e):rest -> Just (Left (NRange (e+1-rhsS) e):rest,RTree (d-1) argTy)+ _ -> Just (Left (NRange rhsS rhsE):mods,RTree (d-1) argTy)+ where+ start = (typeSize ty `div` 2) - 1+ rhsS = (d-1)^(2 :: Int)+ rhsE = d^(2 :: Int)-1++-- This is a HACK for Clash.Driver.TopWrapper.mkOutput+-- Vector's don't have a 10'th constructor, this is just so that we can+-- recognize the particular case+modifier offset mods (Indexed (ty@(Vector _ argTy),10,fI)) = case mods of+ Right {}:rest -> Just (Right (NRange (start+offset) (end+offset)):rest, argTy)+ Left (NRange b _):rest -> Just (Left (NElem (fI+b)):rest, argTy)+ _ -> Just (Left (NElem fI):mods,argTy)+ where+ argSize = typeSize argTy+ start = typeSize ty - (fI * argSize) - 1+ end = start - argSize + 1++-- This is a HACK for Clash.Driver.TopWrapper.mkOutput+-- RTree's don't have a 10'th constructor, this is just so that we can+-- recognize the particular case+modifier offset mods (Indexed (ty@(RTree _ argTy),10,fI)) = case mods of+ Right {}:rest -> Just (Right (NRange (start+offset) (end+offset)):rest, argTy)+ Left (NRange b _):rest -> Just (Left (NElem (b+fI)):rest, argTy)+ _ -> Just (Left (NElem fI):mods, argTy)+ where+ argSize = typeSize argTy+ start = typeSize ty - (fI * argSize) - 1+ end = start - argSize + 1++modifier offset mods (Indexed (CustomSP typName _dataRepr _size args,dcI,fI)) =+ case bitRanges (anns !! fI) of+ [(start,end)] ->+ let m = Right (NRange (start+offset) (end+offset)) in+ case mods of+ Right {}:rest -> Just (m:rest, argTy)+ _ -> Just (m:mods, argTy)+ _ ->+ error $ $(curLoc) ++ "Cannot handle projection out of a "+ ++ "non-contiguously or zero-width encoded field. Tried to project "+ ++ "field " ++ show fI ++ " of constructor " ++ show dcI ++ " of "+ ++ "data type " ++ show typName ++ "."+ where+ (ConstrRepr' _name _n _mask _value anns, _, argTys) = args !! dcI+ argTy = argTys !! fI++modifier offset mods (Indexed (CustomProduct typName dataRepr _size _maybeFieldNames args,dcI,fI))+ | DataRepr' _typ _size [cRepr] <- dataRepr+ , ConstrRepr' _cName _pos _mask _val fieldAnns <- cRepr =+ case bitRanges (fieldAnns !! fI) of+ [(start,end)] ->+ let m = Right (NRange (start+offset) (end+offset)) in+ case mods of+ Right {}:rest -> Just (m:rest, argTy)+ _ -> Just (m:mods,argTy)+ _ ->+ error $ $(curLoc) ++ "Cannot handle projection out of a "+ ++ "non-contiguously or zero-width encoded field. Tried to project "+ ++ "field " ++ show fI ++ " of constructor " ++ show dcI ++ " of "+ ++ "data type " ++ show typName ++ "."+ where+ argTy = map snd args !! fI++modifier offset mods (DC (ty@(SP _ _),_)) =+ let m = Right (NRange (start+offset) (end+offset)) in+ case mods of+ Right {}:rest -> Just (m:rest, ty)+ _ -> Just (m:mods,ty)+ where+ start = typeSize ty - 1+ end = typeSize ty - conSize ty++modifier offset mods (Nested m1 m2) = do+ case modifier offset mods m1 of+ Nothing -> modifier offset mods m2+ Just (mods1,argTy) ->+ let m3 = case mods1 of+ Right (NRange _ e):_ -> modifier e mods1 m2+ _ -> modifier 0 mods1 m2+ in case m3 of+ -- In case the second modifier is `Nothing` that means we want the entire+ -- thing calculated by the first modifier+ Nothing -> Just (mods1,argTy)+ m -> m++modifier _ _ _ = Nothing
src/Clash/Backend/VHDL.hs view
@@ -7,19 +7,17 @@ Generate VHDL for assorted Netlist datatypes -} -{-# LANGUAGE CPP #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE MultiWayIf #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecursiveDo #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecursiveDo #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-} module Clash.Backend.VHDL (VHDLState) where +import Control.Arrow (second) import Control.Applicative (liftA2) import Control.Lens hiding (Indexed, Empty) import Control.Monad (forM,join,zipWithM)@@ -49,7 +47,7 @@ import Clash.Annotations.Primitive (HDL (..)) import Clash.Annotations.BitRepresentation.Internal- (ConstrRepr'(..))+ (ConstrRepr'(..), DataRepr'(..)) import Clash.Annotations.BitRepresentation.ClashLib (bitsToBits) import Clash.Annotations.BitRepresentation.Util@@ -63,13 +61,10 @@ import Clash.Netlist.Types hiding (_intWidth, intWidth) import Clash.Netlist.Util hiding (mkIdentifier) import Clash.Util- (SrcSpan, noSrcSpan, clogBase, curLoc, first, makeCached, on, traceIf, (<:>))+ (SrcSpan, noSrcSpan, clogBase, curLoc, first, makeCached, on, traceIf, (<:>),+ indexNote) import Clash.Util.Graph (reverseTopSort) -#ifdef CABAL-import qualified Paths_clash_lib-#endif- -- | State for the 'Clash.Netlist.VHDL.VHDLM' monad: data VHDLState = VHDLState@@ -101,16 +96,6 @@ makeLenses ''VHDLState -squote :: Mon (State VHDLState) Doc-squote = string "'"--primsRoot :: IO FilePath-#ifdef CABAL-primsRoot = Paths_clash_lib.getDataFileName "prims"-#else-primsRoot = return ("clash-lib" System.FilePath.</> "prims")-#endif- instance Backend VHDLState where initBackend = VHDLState HashSet.empty HashMap.empty HashMap.empty "" noSrcSpan [] [] [] [] [] HashMapS.empty@@ -183,7 +168,7 @@ blockDecl nm ds = do decs <- decls ds let attrs = [ (id_, attr)- | NetDecl' _ _ id_ (Right hwtype) <- ds+ | NetDecl' _ _ id_ (Right hwtype) _ <- ds , attr <- hwTypeAttrs hwtype] if isEmpty decs then insts ds@@ -401,6 +386,8 @@ SP _ elTys -> concatMap mkUsedTys (concatMap snd elTys) BiDirectional _ elTy -> mkUsedTys elTy Annotated _ elTy -> mkUsedTys elTy+ CustomProduct _ _ _ _ tys0 ->+ concatMap mkUsedTys (map snd tys0) CustomSP _ _ _ tys0 -> let tys1 = concat [tys | (_repr, _id, tys) <- tys0] in concatMap mkUsedTys tys1@@ -453,6 +440,10 @@ let tys2 = [HashMap.lookup (mkVecZ ty) nodesI | ty <- tys1] in map (nodesI HashMap.! t,) (catMaybes tys2) + edge t@(CustomProduct _ _ _ _ (map snd -> tys0)) =+ let tys1 = [HashMap.lookup (mkVecZ ty) nodesI | ty <- tys0] in+ map (nodesI HashMap.! t,) (catMaybes tys1)+ edge _ = [] mkVecZ :: HWType -> HWType@@ -516,6 +507,7 @@ SP _ _ -> typAliasDec hwty Sum _ _ -> typAliasDec hwty CustomSum _ _ _ _ -> typAliasDec hwty+ CustomProduct {} -> typAliasDec hwty -- VHDL builtin types: BitVector _ -> emptyDoc@@ -781,13 +773,17 @@ -> HWType -> VHDLM Doc -> Int+ -> Maybe Expr -> VHDLM (Doc, t)-port elName hwType portDirection fillToN =- (,fromIntegral $ TextS.length elName) <$> (encodingNote hwType <> fill fillToN (pretty elName) <+> colon <+> direction <+> sizedQualTyName hwType)+port elName hwType portDirection fillToN iEM =+ (,fromIntegral $ TextS.length elName) <$>+ (encodingNote hwType <> fill fillToN (pretty elName) <+> colon <+> direction+ <+> sizedQualTyName hwType <> iE) where direction | isBiSignalIn hwType = "inout" | otherwise = portDirection + iE = maybe emptyDoc (noEmptyInit . expr_ False) iEM -- [Note] Hack entity attributes in architecture --@@ -818,15 +814,15 @@ _ -> indent 2 (rports p) <> "end" <> semi ) where- ports l = sequence $ [port iName hwType "in" l | (iName, hwType) <- inputs c]- ++ [port oName hwType "out" l | (_, (oName, hwType)) <- outputs c]+ ports l = sequence $ [port iName hwType "in" l Nothing | (iName, hwType) <- inputs c]+ ++ [port oName hwType "out" l iEM | (_, (oName, hwType), iEM) <- outputs c] rports p = "port" <> (parens (align (vcat (punctuate semi (pure p))))) <> semi rattrs = renderAttrs attrs attrs = inputAttrs ++ outputAttrs inputAttrs = [(id_, attr) | (id_, hwtype) <- inputs c, attr <- hwTypeAttrs hwtype]- outputAttrs = [(id_, attr) | (_wireOrReg, (id_, hwtype)) <- outputs c, attr <- hwTypeAttrs hwtype]+ outputAttrs = [(id_, attr) | (_wireOrReg, (id_, hwtype), _) <- outputs c, attr <- hwTypeAttrs hwtype] architecture :: Component -> VHDLM Doc@@ -847,13 +843,13 @@ } where netdecls = filter isNetDecl (declarations c)- declAttrs = [(id_, attr) | NetDecl' _ _ id_ (Right hwtype) <- netdecls, attr <- hwTypeAttrs hwtype]+ declAttrs = [(id_, attr) | NetDecl' _ _ id_ (Right hwtype) _ <- netdecls, attr <- hwTypeAttrs hwtype] inputAttrs = [(id_, attr) | (id_, hwtype) <- inputs c, attr <- hwTypeAttrs hwtype]- outputAttrs = [(id_, attr) | (_wireOrReg, (id_, hwtype)) <- outputs c, attr <- hwTypeAttrs hwtype]+ outputAttrs = [(id_, attr) | (_wireOrReg, (id_, hwtype), _) <- outputs c, attr <- hwTypeAttrs hwtype] isNetDecl :: Declaration -> Bool- isNetDecl (NetDecl' _ _ _ (Right _)) = True- isNetDecl _ = False+ isNetDecl (NetDecl' _ _ _ (Right _) _) = True+ isNetDecl _ = False attrType :: t ~ HashMap T.Text T.Text@@ -1006,7 +1002,8 @@ -- Some type names do not have specific names, but are instead basic types -- in VHDL. tyName'- :: Bool+ :: HasCallStack+ => Bool -- ^ Include length information in first part of name. For example, say we -- want to generate a name for a vector<signed>, where the vector is of length -- 5, and signed has 64 bits. When given `True`, this function would@@ -1023,7 +1020,7 @@ KnownDomain {} -> return (error ($(curLoc) ++ "Forced to print KnownDomain tyName")) Void _ ->- return (error ($(curLoc) ++ "Forced to print Void tyName"))+ return (error ($(curLoc) ++ "Forced to print Void tyName: " ++ show t)) Bool -> return "boolean" Signed n -> let app = if rec0 then ["_", showt n] else [] in@@ -1068,10 +1065,13 @@ Mon $ makeCached (t, False) nameCache (userTyName "sp" nm t) Product nm _ _ -> Mon $ makeCached (t, False) nameCache (userTyName "product" nm t)+ CustomProduct nm _ _ _ _ ->+ Mon $ makeCached (t, False) nameCache (userTyName "product" nm t) Annotated _ hwTy -> tyName' rec0 hwTy BiDirectional _ hwTy -> tyName' rec0 hwTy+ FileType -> return "file" -- | Returns underlying type of given HWType. That is, the type by which it -- eventually will be represented in VHDL.@@ -1088,6 +1088,7 @@ String -> hwty Integer -> hwty Bit -> hwty+ FileType -> hwty -- Complex types, for which a user defined type is made in VHDL: Vector _ _ -> hwty@@ -1102,6 +1103,7 @@ SP _ _ -> BitVector (typeSize hwty) Sum _ _ -> BitVector (typeSize hwty) CustomSum _ _ _ _ -> BitVector (typeSize hwty)+ CustomProduct {} -> BitVector (typeSize hwty) -- Transparent types: Annotated _ elTy -> normaliseType elTy@@ -1122,6 +1124,7 @@ Index _ -> hwty Sum _ _ -> hwty CustomSum _ _ _ _ -> hwty+ FileType -> hwty Vector n elTy -> Vector n (filterTransparent elTy) RTree n elTy -> RTree n (filterTransparent elTy)@@ -1136,6 +1139,10 @@ CustomSP nm0 drepr size (map (\(repr, nm1, tys) -> (repr, nm1, map filterTransparent tys)) constrs) + CustomProduct nm0 drepr size maybeFieldNames constrs ->+ CustomProduct nm0 drepr size maybeFieldNames+ (map (second filterTransparent) constrs)+ -- Transparent types: Annotated _ elTy -> elTy BiDirectional _ elTy -> elTy@@ -1173,7 +1180,11 @@ -- | Convert a Netlist HWType to an error VHDL value for that type sizedQualTyNameErrValue :: HWType -> VHDLM Doc-sizedQualTyNameErrValue Bool = "true"+sizedQualTyNameErrValue Bool = do+ udf <- Mon (use undefValue)+ case udf of+ Just (Just 0) -> "false"+ _ -> "true" sizedQualTyNameErrValue Bit = singularErrValue sizedQualTyNameErrValue t@(Vector n elTy) = do syn <-Mon hdlSyn@@ -1225,10 +1236,11 @@ _ -> punctuate' semi (pure dsDoc) decl :: Int -> Declaration -> VHDLM (Maybe (Doc,Int))-decl l (NetDecl' noteM _ id_ ty) = Just <$> (,fromIntegral (TextS.length id_)) <$>- maybe id addNote noteM ("signal" <+> fill l (pretty id_) <+> colon <+> either pretty sizedQualTyName ty)+decl l (NetDecl' noteM _ id_ ty iEM) = Just <$> (,fromIntegral (TextS.length id_)) <$>+ maybe id addNote noteM ("signal" <+> fill l (pretty id_) <+> colon <+> either pretty sizedQualTyName ty <> iE) where addNote n = mappend ("--" <+> pretty n <> line)+ iE = maybe emptyDoc (noEmptyInit . expr_ False) iEM decl _ (InstDecl Comp _ nm _ gens pms) = fmap (Just . (,0)) $ do { rec (p,ls) <- fmap unzip $ sequence [ (,formalLength i) <$> fill (maximum ls) (expr_ False i) <+> colon <+> portDir dir <+> sizedQualTyName ty | (i,dir,ty,_) <- pms ]@@ -1249,6 +1261,13 @@ decl _ _ = return Nothing +noEmptyInit :: VHDLM Doc -> VHDLM Doc+noEmptyInit d = do+ d1 <- d+ if isEmpty d1+ then emptyDoc+ else (space <> ":=" <+> d)+ stdMatch :: Bits a => Int@@ -1300,7 +1319,7 @@ insts :: [Declaration] -> VHDLM Doc insts [] = emptyDoc-insts (TickDecl id_:ds) = "--" <+> stringS id_ <> line <> insts ds+insts (TickDecl id_:ds) = comment "--" id_ <> line <> insts ds insts (d:ds) = do d' <- inst_ d case d' of@@ -1325,7 +1344,6 @@ <+> "else" <:> conds es' - -- | Turn a Netlist Declaration to a VHDL concurrent block inst_ :: Declaration -> VHDLM (Maybe Doc) inst_ (Assignment id_ e) = fmap Just $@@ -1346,6 +1364,9 @@ inst_ (CondAssignment id_ _ scrut scrutTy@(CustomSum _ _ _ _) es) = inst_' id_ scrut scrutTy es +inst_ (CondAssignment id_ _ scrut scrutTy@(CustomProduct _ _ _ _ _) es) =+ inst_' id_ scrut scrutTy es+ inst_ (CondAssignment id_ _sig scrut scrutTy es) = fmap Just $ "with" <+> parens (expr_ True scrut) <+> "select" <> line <> indent 2 (pretty id_ <+> larrow <+> align (vcat (punctuate comma (conds esNub)) <> semi))@@ -1381,27 +1402,89 @@ inst_ _ = return Nothing +-- | Render a data constructor application for data constructors having a+-- custom bit representation.+customReprDataCon+ :: DataRepr'+ -- ^ Custom representation of data type+ -> ConstrRepr'+ -- ^ Custom representation of a specific constructor of @dataRepr@+ -> [(HWType, Expr)]+ -- ^ Arguments applied to constructor+ -> VHDLM Doc+customReprDataCon dataRepr constrRepr args =+ "std_logic_vector'" <> parens (hcat $ punctuate " & " $ mapM range origins)+ where+ DataRepr' _typ size _constrs = dataRepr++ -- Build bit representations for all constructor arguments+ argSLVs = map (uncurry toSLV) args :: [VHDLM Doc]++ -- Spread bits of constructor arguments using masks+ origins = bitOrigins dataRepr constrRepr :: [BitOrigin]++ range+ :: BitOrigin+ -> VHDLM Doc+ range (Lit (bitsToBits -> ns)) =+ dquotes $ hcat $ mapM bit_char ns+ range (Field n start end) =+ -- We want to select the bits starting from 'start' downto and including+ -- 'end'. We cannot use "(start downto end)" in VHDL, as the preceeding+ -- expression might be anything. This notation only works on identifiers+ -- unfortunately.+ let fsize = start - end + 1 in+ let expr' = argSLVs !! n in++ -- HACK: While expr' is a std_logic_vector (see call `toSLV`), it cannot+ -- be cast to unsigned in case of literals. This is fixed by explicitly+ -- casting it to std_logic_vector.+ let unsigned = "unsigned" <> parens ("std_logic_vector'" <> parens expr') in++ if | fsize == size ->+ -- If sizes are equal, rotating / resizing amounts to doing nothing+ expr'+ | end == 0 ->+ -- Rotating is not necessary if relevant bits are already at the end+ let resized = "resize" <> parens (unsigned <> comma <> int fsize) in+ "std_logic_vector" <> parens resized+ | otherwise ->+ -- Select bits 'start' downto and including 'end'+ let rotated = unsigned <+> "srl" <+> int end in+ let resized = "resize" <> parens (rotated <> comma <> int fsize) in+ "std_logic_vector" <> parens resized+ -- | Turn a Netlist expression into a VHDL expression-expr_ :: Bool -- ^ Enclose in parentheses?- -> Expr -- ^ Expr to convert- -> VHDLM Doc+expr_+ :: HasCallStack+ => Bool+ -- ^ Enclose in parentheses?+ -> Expr+ -- ^ Expr to convert+ -> VHDLM Doc expr_ _ (Literal sizeM lit) = exprLit sizeM lit expr_ _ (Identifier id_ Nothing) = pretty id_-expr_ _ (Identifier id_ (Just (Indexed (CustomSP _id _dataRepr _size args,dcI,fI)))) = do- nm <- Mon $ use modNm- let cast = qualTyName resultType <> squote- let fSLV = stringS (TextS.toLower nm) <> "_types.fromSLV"- cast <> parens (fSLV <> parens (hcat $ punctuate " & " $ ranges))- where- resultType = fieldTypes !! fI- (ConstrRepr' _name _n _mask _value anns, _, fieldTypes) = args !! dcI+expr_ _ (Identifier id_ (Just (Indexed (CustomSP _id dataRepr _size args,dcI,fI)))) =+ case fieldTy of+ Void {} ->+ error (unexpectedProjectionErrorMsg dataRepr dcI fI)+ _ -> do+ nm <- Mon $ use modNm+ let cast = qualTyName resultType <> squote+ let fSLV = stringS (TextS.toLower nm) <> "_types.fromSLV"+ cast <> parens (fSLV <> parens (hcat $ punctuate " & " $ ranges))+ where+ resultType = fieldTypes !! fI+ (ConstrRepr' _name _n _mask _value anns, _, fieldTypes) = args !! dcI - ranges =- mapM range $ bitRanges (anns !! fI)+ ranges =+ mapM range $ bitRanges (anns !! fI) - range (start, end) =- pretty id_ <> parens (int start <+> "downto" <+> int end)+ range (start, end) =+ pretty id_ <> parens (int start <+> "downto" <+> int end) + fieldTy = indexNote ($(curLoc) ++ "panic") fieldTypes fI+ expr_ b (Identifier id_ (Just (Indexed (ty@(SP _ args),dcI,fI)))) = do nm <- Mon $ use modNm case b of@@ -1419,6 +1502,20 @@ start = typeSize ty - 1 - conSize ty - other end = start - argSize + 1 +expr_ _ (Identifier id_ (Just (Indexed (CustomProduct _ dataRepr _ _ tys, dcI, fI)))) =+ case fieldTy of+ Void {} ->+ error (unexpectedProjectionErrorMsg dataRepr dcI fI)+ _ -> do+ modNm' <- Mon (use modNm)+ let cast = qualTyName fieldTy <> squote+ let fSLV = stringS (TextS.toLower modNm') <> "_types.fromSLV"+ cast <> parens (fSLV <> parens (hcat $ punctuate " & " $ ranges))+ where+ (fieldAnn, fieldTy) = indexNote ($(curLoc) ++ "panic") tys fI+ ranges = mapM range (bitRanges fieldAnn)+ range (start, end) = pretty id_ <> parens (int start <+> "downto" <+> int end)+ expr_ _ (Identifier id_ (Just (Indexed (ty@(Product _ labels tys),_,fI)))) = pretty id_ <> dot <> tyName ty <> selectProductField labels tys fI @@ -1634,47 +1731,12 @@ expr_ _ (DataCon ty@(CustomSum _ _ _ tys) (DC (_,i)) []) = let (ConstrRepr' _ _ _ value _) = fst $ tys !! i in "std_logic_vector" <> parens ("to_unsigned" <> parens (int (fromIntegral value) <> comma <> int (typeSize ty)))-expr_ _ (DataCon (CustomSP _ dataRepr size args) (DC (_,i)) es) =- "std_logic_vector'" <> parens (hcat $ punctuate " & " $ mapM range origins)- where- (cRepr, _, argTys) = args !! i-- -- Build bit representations for all constructor arguments- argExprs = zipWith toSLV argTys es :: [VHDLM Doc]-- -- Spread bits of constructor arguments using masks- origins = bitOrigins dataRepr cRepr :: [BitOrigin]-- range- :: BitOrigin- -> VHDLM Doc- range (Lit (bitsToBits -> ns)) =- dquotes $ hcat $ mapM bit_char ns- range (Field n start end) =- -- We want to select the bits starting from 'start' downto and including- -- 'end'. We cannot use "(start downto end)" in VHDL, as the preceeding- -- expression might be anything. This notation only works on identifiers- -- unfortunately.- let fsize = start - end + 1 in- let expr' = argExprs !! n in-- -- HACK: While expr' is a std_logic_vector (see call `toSLV`), it cannot- -- be cast to unsigned in case of literals. This is fixed by explicitly- -- casting it to std_logic_vector.- let unsigned = "unsigned" <> parens ("std_logic_vector'" <> parens expr') in-- if | fsize == size ->- -- If sizes are equal, rotating / resizing amounts to doing nothing- expr'- | end == 0 ->- -- Rotating is not necessary if relevant bits are already at the end- let resized = "resize" <> parens (unsigned <> comma <> int fsize) in- "std_logic_vector" <> parens resized- | otherwise ->- -- Select bits 'start' downto and including 'end'- let rotated = unsigned <+> "srl" <+> int end in- let resized = "resize" <> parens (rotated <> comma <> int fsize) in- "std_logic_vector" <> parens resized+expr_ _ (DataCon (CustomSP _ dataRepr _size args) (DC (_,i)) es) =+ let (cRepr, _, argTys) = args !! i in+ customReprDataCon dataRepr cRepr (zip argTys es)+expr_ _ (DataCon (CustomProduct _ dataRepr _size _labels tys) _ es) |+ DataRepr' _typ _size [cRepr] <- dataRepr =+ customReprDataCon dataRepr cRepr (zip (map snd tys) es) expr_ _ (DataCon ty@(Product _ labels tys) _ es) = tupled $ zipWithM (\i e' -> tyName ty <> selectProductField labels tys i <+> rarrow <+> expr_ False e') [0..] es@@ -1888,7 +1950,7 @@ Just (Just i) -> "'" <> int i <> "'" bit_char Z = char 'Z' -toSLV :: HWType -> Expr -> VHDLM Doc+toSLV :: HasCallStack => HWType -> Expr -> VHDLM Doc toSLV Bool e = do nm <- Mon $ use modNm pretty (TextS.toLower nm) <> "_types.toSLV" <> parens (expr_ False e)@@ -1910,6 +1972,7 @@ let (ConstrRepr' _ _ _ value _) = fst $ reprs !! i in let unsigned = "to_unsigned" <> parens (int (fromIntegral value) <> comma <> int size) in "std_logic_vector" <> parens unsigned+toSLV (CustomSum {}) e = "std_logic_vector" <> parens (expr_ False e) toSLV t@(Product _ labels tys) (Identifier id_ Nothing) = do selIds' <- sequence selIds encloseSep lparen rparen " & " (zipWithM toSLV tys selIds')@@ -1919,11 +1982,18 @@ selIds = map (fmap (\n -> Identifier n Nothing)) selNames toSLV (Product _ _ tys) (DataCon _ _ es) = do encloseSep lparen rparen " & " (zipWithM toSLV tys es)+toSLV (CustomProduct _ _ _ _ _) e = do+ -- Custom representations are represented as bitvectors in HDL, so we don't+ -- need to do anything.+ expr_ False e toSLV (Product _ _ _) e = do nm <- Mon $ use modNm pretty (TextS.toLower nm) <> "_types.toSLV" <> parens (expr_ False e) toSLV (SP _ _) e = expr_ False e-toSLV (CustomSP _ _ _ _) e = expr_ False e+toSLV (CustomSP _ _ _ _) e =+ -- Custom representations are represented as bitvectors in HDL, so we don't+ -- need to do anything.+ expr_ False e toSLV (Vector n elTy) (Identifier id_ Nothing) = do selIds' <- sequence selIds syn <- Mon hdlSyn@@ -1934,6 +2004,10 @@ where selNames = map (fmap (T.toStrict . renderOneLine) ) $ [pretty id_ <> parens (int i) | i <- [0 .. (n-1)]] selIds = map (fmap (`Identifier` Nothing)) selNames+-- Don't split up newtype wrappers, or void-filtered types+toSLV (Vector _ _) e@(DataCon _ (DC (Void Nothing, -1)) _) = do+ nm <- Mon $ use modNm+ pretty (TextS.toLower nm) <> "_types.toSLV" <> parens (expr_ False e) toSLV (Vector n elTy) (DataCon _ _ es) = "std_logic_vector'" <> (parens $ vcat $ punctuate " & " (zipWithM toSLV [elTy,Vector (n-1) elTy] es)) toSLV (Vector _ _) e = do@@ -1963,6 +2037,7 @@ args = zipWith3 (`fromSLV` id_) tys starts ends fromSLV (CustomSP _ _ _ _) id_ start end = pretty id_ <> parens (int start <+> "downto" <+> int end)+fromSLV (CustomProduct {}) id_ start end = pretty id_ <> parens (int start <+> "downto" <+> int end) fromSLV (SP _ _) id_ start end = pretty id_ <> parens (int start <+> "downto" <+> int end) fromSLV (Vector n elTy) id_ start _ = if n > 1 then tupled args
src/Clash/Backend/Verilog.hs view
@@ -7,15 +7,13 @@ Generate Verilog for assorted Netlist datatypes -} -{-# LANGUAGE CPP #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE RecursiveDo #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecursiveDo #-}+{-# LANGUAGE TemplateHaskell #-} module Clash.Backend.Verilog ( VerilogState@@ -25,6 +23,7 @@ , exprLit , bits , bit_char+ , noEmptyInit ) where @@ -46,17 +45,15 @@ import qualified Data.Text.Lazy as Text import qualified Data.Text as TextS import Data.Text.Prettyprint.Doc.Extra-#ifdef CABAL-import qualified Data.Version-#endif import qualified System.FilePath+import GHC.Stack (HasCallStack) import Clash.Annotations.Primitive (HDL (..)) import Clash.Annotations.BitRepresentation (BitMask) import Clash.Annotations.BitRepresentation.ClashLib (bitsToBits) import Clash.Annotations.BitRepresentation.Internal- (ConstrRepr'(..))+ (ConstrRepr'(..), DataRepr'(..), ConstrRepr'(..)) import Clash.Annotations.BitRepresentation.Util (BitOrigin(Lit, Field), bitOrigins, bitRanges, isContinuousMask) import Clash.Core.Var (Attr'(..))@@ -67,16 +64,11 @@ import Clash.Netlist.Id (IdType (..), mkBasicId') import Clash.Netlist.Types hiding (_intWidth, intWidth) import Clash.Netlist.Util hiding (mkIdentifier, extendIdentifier)+import Clash.Signal.Internal (ActiveEdge (..)) import Clash.Util- (SrcSpan, noSrcSpan, curLoc, traceIf, (<:>),on,first)--+ (SrcSpan, noSrcSpan, curLoc, traceIf, (<:>), on, first, indexNote) -#ifdef CABAL-import qualified Paths_clash_lib-#endif- -- | State for the 'Clash.Backend.Verilog.VerilogM' monad: data VerilogState = VerilogState@@ -99,16 +91,6 @@ makeLenses ''VerilogState -squote :: Mon (State VerilogState) Doc-squote = string "'"--primsRoot :: IO FilePath-#ifdef CABAL-primsRoot = Paths_clash_lib.getDataFileName "prims"-#else-primsRoot = return ("clash-lib" System.FilePath.</> "prims")-#endif- instance Backend VerilogState where initBackend = VerilogState 0 HashMap.empty noSrcSpan [] [] [] [] [] hdlKind = const Verilog@@ -176,10 +158,10 @@ blockDecl _ ds = do decs <- decls ds if isEmpty decs- then indent 2 (insts ds)+ then insts ds else pure decs <> line <>- indent 2 (insts ds)+ insts ds unextend = return rmSlash addIncludes inc = includes %= (inc++) addLibraries libs = libraries %= (libs ++)@@ -235,11 +217,6 @@ incs <- Mon $ use includes return ((TextS.unpack cName,v),incs) where-#ifdef CABAL- clashVer = Data.Version.showVersion Paths_clash_lib.version-#else- clashVer = "development"-#endif cName = componentName c commentHeader = "/* AUTOMATICALLY GENERATED VERILOG-2001 SOURCE CODE."@@ -250,16 +227,19 @@ sigPort :: Maybe WireOrReg -> TextS.Text -> HWType+ -> Maybe Expr -> VerilogM Doc-sigPort wor pName hwType =+sigPort wor pName hwType iEM = addAttrs (hwTypeAttrs hwType)- (portType <+> verilogType hwType <+> stringS pName <> encodingNote hwType)+ (portType <+> verilogType hwType <+> stringS pName <> iE <> encodingNote hwType) where portType = case wor of Nothing -> if isBiSignalIn hwType then "inout" else "input" Just Wire -> "output" <+> "wire" Just Reg -> "output" <+> "reg" + iE = maybe emptyDoc (noEmptyInit . expr_ False) iEM+ module_ :: Component -> VerilogM Doc module_ c = addSeen c *> modVerilog <* Mon (imports .= []) where@@ -274,8 +254,8 @@ modBody = indent 2 (decls (declarations c)) <> line <> line <> indent 2 (insts (declarations c)) modEnding = "endmodule" - inPorts = sequence [ sigPort Nothing id_ hwType | (id_, hwType) <- inputs c ]- outPorts = sequence [ sigPort (Just wireOrReg) id_ hwType | (wireOrReg, (id_, hwType)) <- outputs c ]+ inPorts = sequence [ sigPort Nothing id_ hwType Nothing | (id_, hwType) <- inputs c ]+ outPorts = sequence [ sigPort (Just wireOrReg) id_ hwType iEM | (wireOrReg, (id_, hwType), iEM) <- outputs c ] -- slightly more readable than 'tupled', makes the output Haskell-y-er commafy v = (comma <> space) <> pure v@@ -309,15 +289,16 @@ indent 2 (string "`uselib" <+> (hsep (mapM (\l -> ("lib=" <> string l)) xs))) <> line <> line -wireOrRegDoc :: WireOrReg -> VerilogM Doc-wireOrRegDoc Wire = "wire"-wireOrRegDoc Reg = "reg"+wireRegFileDoc :: WireOrReg -> (Either Identifier HWType) -> VerilogM Doc+wireRegFileDoc _ (Right FileType) = "integer"+wireRegFileDoc Wire _ = "wire"+wireRegFileDoc Reg _ = "reg" addSeen :: Component -> VerilogM () addSeen c = do let iport = [iName | (iName, _) <- inputs c]- oport = [oName | (_, (oName, _)) <- outputs c]- nets = mapMaybe (\case {NetDecl' _ _ i _ -> Just i; _ -> Nothing}) $ declarations c+ oport = [oName | (_, (oName, _), _) <- outputs c]+ nets = mapMaybe (\case {NetDecl' _ _ i _ _ -> Just i; _ -> Nothing}) $ declarations c Mon $ idSeen %= (HashMap.unionWith max (HashMap.fromList (concatMap (map (,0)) [iport,oport,nets]))) verilogType :: HWType -> VerilogM Doc@@ -327,6 +308,7 @@ Reset {} -> emptyDoc Bit -> emptyDoc Bool -> emptyDoc+ FileType -> emptyDoc _ -> brackets (int (typeSize t -1) <> colon <> int 0) sigDecl :: VerilogM Doc -> HWType -> VerilogM Doc@@ -381,19 +363,27 @@ renderAttr (Attr' key ) = pack $ key decl :: Declaration -> VerilogM (Maybe Doc)-decl (NetDecl' noteM wr id_ tyE) =- Just A.<$> maybe id addNote noteM (addAttrs attrs (wireOrRegDoc wr <+> tyDec tyE))+decl (NetDecl' noteM wr id_ tyE iEM) =+ Just A.<$> maybe id addNote noteM (addAttrs attrs (wireRegFileDoc wr tyE <+> tyDec tyE)) where- tyDec (Left ty) = stringS ty <+> stringS id_- tyDec (Right ty) = sigDecl (stringS id_) ty+ tyDec (Left ty) = stringS ty <+> stringS id_ <> iE+ tyDec (Right ty) = sigDecl (stringS id_) ty <> iE addNote n = mappend ("//" <+> stringS n <> line) attrs = fromMaybe [] (hwTypeAttrs A.<$> either (const Nothing) Just tyE)+ iE = maybe emptyDoc (noEmptyInit . expr_ False) iEM decl _ = return Nothing +noEmptyInit :: (Monad m, Semigroup (m Doc)) => m Doc -> m Doc+noEmptyInit d = do+ d1 <- d+ if isEmpty d1+ then emptyDoc+ else (space <> string "=" <+> d)+ insts :: [Declaration] -> VerilogM Doc insts [] = emptyDoc-insts (TickDecl id_:ds) = "//" <+> stringS id_ <> line <> insts ds+insts (TickDecl id_:ds) = comment "//" id_ <> line <> insts ds insts (d:ds) = do docM <- inst_ d case docM of@@ -437,9 +427,14 @@ let (cRepr, _id, _tys) = reprs !! i in patLitCustom' size cRepr +patLitCustom hwTy _+ | CustomProduct _name dataRepr size _maybeFieldNames _reprs <- hwTy+ , DataRepr' _typ _size [cRepr] <- dataRepr =+ patLitCustom' size cRepr+ patLitCustom x y = error $ $(curLoc) ++ unwords- [ "You can only pass CustomSP / CustomSum and a NumLit to this function,"- , "not", show x, "and", show y]+ [ "You can only pass CustomSP / CustomSum / CustomProduct and a NumLit to "+ , "this function, not", show x, "and", show y ] patMod :: HWType -> Literal -> Literal patMod hwTy (NumLit i) = NumLit (i `mod` (2 ^ typeSize hwTy))@@ -491,12 +486,15 @@ where (t,f) = if b then (l,r) else (r,l) -inst_ (CondAssignment id_ _ scrut scrutTy@(CustomSP _ _ _ _) es) =+inst_ (CondAssignment id_ _ scrut scrutTy@(CustomSP {}) es) = inst_' id_ scrut scrutTy es -inst_ (CondAssignment id_ _ scrut scrutTy@(CustomSum _ _ _ _) es) =+inst_ (CondAssignment id_ _ scrut scrutTy@(CustomSum {}) es) = inst_' id_ scrut scrutTy es +inst_ (CondAssignment id_ _ scrut scrutTy@(CustomProduct {}) es) =+ inst_' id_ scrut scrutTy es+ inst_ (CondAssignment id_ _ scrut scrutTy es) = fmap Just $ "always @(*) begin" <> line <> indent 2 ("case" <> parens (expr_ True scrut) <> line <>@@ -521,13 +519,72 @@ inst_ (BlackBoxD _ libs imps inc bs bbCtx) = fmap Just (Mon (column (renderBlackBox libs imps inc bs bbCtx))) -inst_ (NetDecl' _ _ _ _) = return Nothing+inst_ (Seq ds) = Just <$> seqs ds +inst_ (NetDecl' {}) = return Nothing++seq_ :: Seq -> VerilogM Doc+seq_ (AlwaysClocked edge clk ds) =+ "always @" <>+ parens (case edge of {Rising -> "posedge"; _ -> "negedge"} <+>+ expr_ False clk) <+> "begin" <> line <>+ indent 2 (seqs ds) <> line <>+ "end"++seq_ (Initial ds) =+ "initial begin" <> line <>+ indent 2 (seqs ds) <> line <>+ "end"++seq_ (AlwaysComb ds) =+ "always @* begin" <> line <>+ indent 2 (seqs ds) <> line <>+ "end"++seq_ (Branch scrut scrutTy es) =+ "case" <> parens (expr_ True scrut) <> line <>+ (indent 2 $ vcat $ conds es) <> line <>+ "endcase"+ where+ conds :: [(Maybe Literal,[Seq])] -> VerilogM [Doc]+ conds [] =+ return []+ conds [(_,sq)] =+ ("default" <+> colon <+> "begin" <> line <>+ indent 2 (seqs sq) <> line <>+ "end") <:> return []+ conds ((Nothing,sq):_) =+ ("default" <+> colon <+> "begin" <> line <>+ indent 2 (seqs sq) <> line <>+ "end") <:> return []+ conds ((Just c ,sq):es') =+ (exprLitV (Just (scrutTy,conSize scrutTy)) c <+> colon <+> "begin" <> line <>+ indent 2 (seqs sq) <> line <>+ "end") <:> conds es'++seq_ (SeqDecl sd) = case sd of+ Assignment id_ e ->+ stringS id_ <+> equals <+> expr_ False e <> semi++ BlackBoxD {} ->+ fromMaybe <$> emptyDoc <*> inst_ sd++ Seq ds ->+ seqs ds++ _ -> error ("seq_: " ++ show sd)++seqs :: [Seq] -> VerilogM Doc+seqs [] = emptyDoc+seqs (SeqDecl (TickDecl id_):ds) = "//" <+> stringS id_ <> line <> seqs ds+seqs (d:ds) = seq_ d <> line <> line <> seqs ds+ -- | Calculate the beginning and end index into a variable, to get the -- desired field. -- Also returns the HWType of the result. modifier- :: Int+ :: HasCallStack+ => Int -- ^ Offset, only used when we have nested modifiers -> Modifier -> Maybe (Int,Int,HWType)@@ -556,7 +613,7 @@ start = typeSize ty - 1 end = start - argSize + 1 -modifier offset (Indexed (ty@(Vector _ argTy),1,1)) = Just (start+offset,offset, argTy)+modifier offset (Indexed (ty@(Vector n argTy),1,1)) = Just (start+offset,offset,Vector (n-1) argTy) where argSize = typeSize argTy start = typeSize ty - argSize - 1@@ -565,12 +622,12 @@ where start = typeSize ty - 1 -modifier offset (Indexed (ty@(RTree _ argTy),1,0)) = Just (start+offset,end+offset, argTy)+modifier offset (Indexed (ty@(RTree d argTy),1,0)) = Just (start+offset,end+offset,RTree (d-1) argTy) where start = typeSize ty - 1 end = typeSize ty `div` 2 -modifier offset (Indexed (ty@(RTree _ argTy),1,1)) = Just (start+offset,offset, argTy)+modifier offset (Indexed (ty@(RTree d argTy),1,1)) = Just (start+offset,offset, RTree (d-1) argTy) where start = (typeSize ty `div` 2) - 1 @@ -592,14 +649,33 @@ start = typeSize ty - (fI * argSize) - 1 end = start - argSize + 1 -modifier offset (Indexed (CustomSP _id _dataRepr _size args,dcI,fI)) =+modifier offset (Indexed (CustomSP typName _dataRepr _size args,dcI,fI)) = case bitRanges (anns !! fI) of- [(start,end)] -> Just (start+offset,end+offset, argTy)- _ -> error ($(curLoc) ++ "Cannot handle projection out of a non-contiguously encoded field")+ [(start,end)] ->+ Just (start+offset,end+offset, argTy)+ _ ->+ error $ $(curLoc) ++ "Cannot handle projection out of a "+ ++ "non-contiguously or zero-width encoded field. Tried to project "+ ++ "field " ++ show fI ++ " of constructor " ++ show dcI ++ " of "+ ++ "data type " ++ show typName ++ "." where (ConstrRepr' _name _n _mask _value anns, _, argTys) = args !! dcI argTy = argTys !! fI +modifier offset (Indexed (CustomProduct typName dataRepr _size _maybeFieldNames args,dcI,fI))+ | DataRepr' _typ _size [cRepr] <- dataRepr+ , ConstrRepr' _cName _pos _mask _val fieldAnns <- cRepr =+ case bitRanges (fieldAnns !! fI) of+ [(start,end)] ->+ Just (start+offset,end+offset, argTy)+ _ ->+ error $ $(curLoc) ++ "Cannot handle projection out of a "+ ++ "non-contiguously or zero-width encoded field. Tried to project "+ ++ "field " ++ show fI ++ " of constructor " ++ show dcI ++ " of "+ ++ "data type " ++ show typName ++ "."+ where+ argTy = map snd args !! fI+ modifier offset (DC (ty@(SP _ _),_)) = Just (start+offset,end+offset, ty) where start = typeSize ty - 1@@ -616,6 +692,56 @@ modifier _ _ = Nothing +-- | Render a data constructor application for data constructors having a+-- custom bit representation.+customReprDataCon+ :: DataRepr'+ -- ^ Custom representation of data type+ -> ConstrRepr'+ -- ^ Custom representation of a specific constructor of @dataRepr@+ -> [(HWType, Expr)]+ -- ^ Arguments applied to constructor+ -> VerilogM Doc+customReprDataCon dataRepr constrRepr args =+ (flip fromMaybe) (errOnNonContinuous 0 anns) $+ braces $ hcat $ punctuate ", " $ mapM range' origins+ where+ anns = crFieldAnns constrRepr+ size = drSize dataRepr++ errOnNonContinuous :: Int -> [BitMask] -> Maybe a+ errOnNonContinuous _ [] = Nothing+ errOnNonContinuous fieldnr (ann:anns') =+ if isContinuousMask ann then+ errOnNonContinuous (fieldnr + 1) anns'+ else+ error $ $(curLoc) ++ unlines [+ "Error while processing custom bit representation:\n"+ , unwords ["Field", show fieldnr, "of constructor"+ , show (crName constrRepr), "of type\n"]+ , " " ++ show (drType dataRepr) ++ "\n"+ , "has a non-continuous fieldmask:\n"+ , " " ++ (map bit_char' $ toBits size ann) ++ "\n"+ , unwords [ "This is not supported in Verilog. Change the mask to a"+ , "continuous one, or render using VHDL or SystemVerilog."+ ]+ ]++ -- Build bit representations for all constructor arguments+ argExprs = map (expr_ False) (map snd args) :: [VerilogM Doc]++ -- Spread bits of constructor arguments using masks+ origins = bitOrigins dataRepr constrRepr :: [BitOrigin]++ range'+ :: BitOrigin+ -> VerilogM Doc+ range' (Lit (bitsToBits -> ns)) =+ int (length ns) <> squote <> "b" <> hcat (mapM (bit_char undefValue) ns)+ range' (Field n _start _end) =+ argExprs !! n++ -- | Turn a Netlist expression into a Verilog expression expr_ :: Bool -- ^ Enclose in parentheses? -> Expr -- ^ Expr to convert@@ -624,13 +750,27 @@ expr_ _ (Identifier id_ Nothing) = stringS id_ -expr_ _ (Identifier id_ (Just (Indexed (CustomSP _id _dataRepr _size args,dcI,fI)))) =- braces $ hcat $ punctuate ", " $ sequence ranges- where- (ConstrRepr' _name _n _mask _value anns, _, _argTys) = args !! dcI- ranges = map range' $ bitRanges (anns !! fI)- range' (start, end) = stringS id_ <> brackets (int start <> ":" <> int end)+expr_ _ (Identifier id_ (Just (Indexed (CustomSP _id dataRepr _size args,dcI,fI)))) =+ case fieldTy of+ Void {} -> error (unexpectedProjectionErrorMsg dataRepr dcI fI)+ _ -> braces $ hcat $ punctuate ", " $ sequence ranges+ where+ (ConstrRepr' _name _n _mask _value anns, _, fieldTypes) = args !! dcI+ ranges = map range' $ bitRanges (anns !! fI)+ range' (start, end) = stringS id_ <> brackets (int start <> ":" <> int end)+ fieldTy = indexNote ($(curLoc) ++ "panic") fieldTypes fI +expr_ _ (Identifier d_ (Just (Indexed (CustomProduct _id dataRepr _size _maybeFieldNames tys, dcI, fI))))+ | DataRepr' _typ _size [cRepr] <- dataRepr+ , ConstrRepr' _cName _pos _mask _val anns <- cRepr =+ let ranges = map range' (bitRanges (anns !! fI)) in+ case fieldTy of+ Void {} -> error (unexpectedProjectionErrorMsg dataRepr dcI fI)+ _ -> braces $ hcat $ punctuate ", " $ sequence ranges+ where+ (_fieldAnn, fieldTy) = indexNote ($(curLoc) ++ "panic") tys fI+ range' (start, end) = stringS d_ <> brackets (int start <> ":" <> int end)+ -- See [Note] integer projection expr_ _ (Identifier id_ (Just (Indexed ((Signed w),_,_)))) = do iw <- Mon $ use intWidth@@ -698,44 +838,12 @@ expr_ _ (DataCon ty@(CustomSum _ _ _ tys) (DC (_,i)) []) = let (ConstrRepr' _ _ _ value _) = fst $ tys !! i in int (typeSize ty) <> squote <> "d" <> int (fromIntegral value)-expr_ _ (DataCon (CustomSP name' dataRepr size args) (DC (_,constrNr)) es) =- (flip fromMaybe) (errOnNonContinuous 0 anns) $- braces $ hcat $ punctuate ", " $ mapM range' origins- where- (cRepr, _, _) = args !! constrNr- (ConstrRepr' _name _n _mask _value anns) = cRepr-- errOnNonContinuous :: Int -> [BitMask] -> Maybe a- errOnNonContinuous _ [] = Nothing- errOnNonContinuous fieldnr (ann:anns') =- if isContinuousMask ann then- errOnNonContinuous (fieldnr + 1) anns'- else- error $ $(curLoc) ++ unlines [- "Error while processing custom bit representation:\n"- , unwords ["Field", show fieldnr, "of constructor", show constrNr, "of type\n"]- , " " ++ show name' ++ "\n"- , "has a non-continuous fieldmask:\n"- , " " ++ (map bit_char' $ toBits size ann) ++ "\n"- , unwords [ "This is not supported in Verilog. Change the mask to a"- , "continuous one, or render using VHDL or SystemVerilog."- ]- ]-- -- Build bit representations for all constructor arguments- argExprs = map (expr_ False) es :: [VerilogM Doc]-- -- Spread bits of constructor arguments using masks- origins = bitOrigins dataRepr cRepr :: [BitOrigin]-- range'- :: BitOrigin- -> VerilogM Doc- range' (Lit (bitsToBits -> ns)) =- int (length ns) <> squote <> "b" <> hcat (mapM (bit_char undefValue) ns)- range' (Field n _start _end) =- argExprs !! n-+expr_ _ (DataCon (CustomSP _name dataRepr _size constrs) (DC (_,constrNr)) es) =+ let (cRepr, _, argTys) = constrs !! constrNr in+ customReprDataCon dataRepr cRepr (zip argTys es)+expr_ _ (DataCon (CustomProduct _ dataRepr _size _labels tys) _ es) |+ DataRepr' _typ _size [cRepr] <- dataRepr =+ customReprDataCon dataRepr cRepr (zip (map snd tys) es) expr_ _ (DataCon (Product {}) _ es) = listBraces (mapM (expr_ False) es) expr_ _ (BlackBoxE pNm _ _ _ _ bbCtx _)
src/Clash/Core/DataCon.hs view
@@ -7,12 +7,11 @@ Data Constructors in CoreHW -} -{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TemplateHaskell #-} module Clash.Core.DataCon ( DataCon (..)
src/Clash/Core/Evaluator.hs view
@@ -10,31 +10,28 @@ -} -{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE MagicHash #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MagicHash #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE TemplateHaskell #-} module Clash.Core.Evaluator where -import Control.Arrow (second)+import Prelude hiding (lookup)+ import Control.Concurrent.Supply (Supply, freshId)-import Control.Lens (view, _4)-import Data.Bits (shiftL) import Data.Either (lefts,rights)-import Data.List- (foldl',mapAccumL,uncons)-import Data.IntMap (IntMap)+import Data.List (foldl',mapAccumL)+import Data.Maybe (fromMaybe) import qualified Data.Primitive.ByteArray as BA+import qualified Data.Text as Text import qualified Data.Vector.Primitive as PV-import Data.Text (Text)-import Data.Text.Prettyprint.Doc-import Debug.Trace (trace)+import Debug.Trace import GHC.Integer.GMP.Internals (Integer (..), BigNat (..))+ import Clash.Core.DataCon+import Clash.Core.Evaluator.Types import Clash.Core.FreeVars import Clash.Core.Literal import Clash.Core.Name@@ -46,488 +43,361 @@ import Clash.Core.Util import Clash.Core.Var import Clash.Core.VarEnv-import Clash.Driver.Types (BindingMap)-import Prelude hiding (lookup)+import Clash.Driver.Types (BindingMap, Binding(..))+import Clash.Pretty import Clash.Unique import Clash.Util (curLoc)-import Clash.Pretty --- | The heap-data Heap = Heap GlobalHeap GPureHeap PureHeap Supply InScopeSet--type PureHeap = VarEnv Term-newtype GPureHeap = GPureHeap { unGPureHeap :: PureHeap }---- | Global heap-type GlobalHeap = (IntMap Term, Int)---- | The stack-type Stack = [StackFrame]--data StackFrame- = Update Id- | GUpdate Id- | Apply Id- | Instantiate Type- | PrimApply Text PrimInfo [Type] [Value] [Term]- | Scrutinise Type [Alt]- | Tickish TickInfo- deriving Show--instance ClashPretty StackFrame where- clashPretty (Update i) = hsep ["Update", fromPpr i]- clashPretty (GUpdate i) = hsep ["GUpdate", fromPpr i]- clashPretty (Apply i) = hsep ["Apply", fromPpr i]- clashPretty (Instantiate t) = hsep ["Instantiate", fromPpr t]- clashPretty (PrimApply a b c d e) = do- hsep ["PrimApply", fromPretty a, "::", fromPpr (primType b),- "; type args=", fromPpr c,- "; val args=", fromPpr (map valToTerm d),- "term args=", fromPpr e]- clashPretty (Scrutinise a b) =- hsep ["Scrutinise ", fromPpr a,- fromPpr (Case (Literal (CharLiteral '_')) a b)]- clashPretty (Tickish sp) =- hsep ["Tick", fromPpr sp]--mkTickish- :: Stack- -> [TickInfo]- -> Stack-mkTickish s sps = map Tickish sps ++ s---- Values-data Value- = Lambda Id Term- -- ^ Functions- | TyLambda TyVar Term- -- ^ Type abstractions- | DC DataCon [Either Term Type]- -- ^ Data constructors- | Lit Literal- -- ^ Literals- | PrimVal Text PrimInfo [Type] [Value]- -- ^ Clash's number types are represented by their "fromInteger#" primitive- -- function. So some primitives are values.- | Suspend Term- -- ^ Used by lazy primitives- deriving Show---- | State of the evaluator-type State = (Heap, Stack, Term)---- | Function that can evaluator primitives, i.e., perform delta-reduction-type PrimEvaluator =- Bool -> -- Force special primitives? See [Note: forcing special primitives]- TyConMap -> -- Type constructors- Heap ->- Stack ->- Text -> -- Name of the primitive- PrimInfo -> -- Type of the primitive- [Type] -> -- Type arguments of the primitive- [Value] -> -- Value arguments of the primitive- Maybe State -- Delta-reduction can get stuck, so Nothing is an option---- | Evaluate to WHNF starting with an empty Heap and Stack whnf'- :: PrimEvaluator+ :: PrimStep+ -> PrimUnwind -> BindingMap -> TyConMap- -> GlobalHeap+ -> PrimHeap -> Supply -> InScopeSet -> Bool -> Term- -> (GlobalHeap, PureHeap, Term)-whnf' eval gbl0 tcm gh ids is isSubj e- = case whnf eval tcm isSubj (Heap gh gbl1 emptyVarEnv ids is,[],e) of- (Heap gh' _ ph' _ _,_,e') -> (gh',ph',e')+ -> (PrimHeap, PureHeap, Term)+whnf' eval fu bm tcm ph ids is isSubj e =+ toResult $ whnf tcm isSubj m where- gbl1 = GPureHeap (mapVarEnv (view _4) gbl0)+ toResult x = (mHeapPrim x, mHeapLocal x, mTerm x) + m = Machine eval fu ph gh emptyVarEnv [] ids is e+ gh = mapVarEnv bindingTerm bm+ -- | Evaluate to WHNF given an existing Heap and Stack whnf- :: PrimEvaluator- -> TyConMap+ :: TyConMap -> Bool- -> State- -> State-whnf eval tcm isSubj (h,k,e) =- if isSubj- then go (h,Scrutinise ty []:k,e) -- See [Note: empty case expressions]- else go (h,k,e)+ -> Machine+ -> Machine+whnf tcm isSubj m+ | isSubj =+ -- See [Note: empty case expressions]+ let ty = termType tcm (mTerm m)+ in go (stackPush (Scrutinise ty []) m)+ | otherwise = go m where- ty = termType tcm e-- go s = case step eval tcm s of+ go s = case step s tcm of Just s' -> go s'- Nothing- | Just e' <- unwindStack s- -> e'- | otherwise- -> error $ showDoc $ ppr e+ Nothing -> fromMaybe (error . showDoc . ppr $ mTerm m) (unwindStack s) --- | Are we in a context where special primitives must be forced.+-- | Completely unwind the stack to get back the complete term+unwindStack :: Machine -> Maybe Machine+unwindStack m+ | stackNull m = Just m+ | otherwise = do+ (m', kf) <- stackPop m++ case kf of+ PrimApply p tys vs tms ->+ let term = foldl' App+ (foldl' App+ (foldl' TyApp (Prim p) tys)+ (fmap valToTerm vs))+ (mTerm m' : tms)+ in unwindStack (setTerm term m')++ Instantiate ty ->+ let term = TyApp (getTerm m') ty+ in unwindStack (setTerm term m')++ Apply n ->+ case heapLookup LocalId n m' of+ Just e ->+ let term = App (getTerm m') e+ in unwindStack (setTerm term m')++ Nothing -> error $ unlines $+ [ "Clash.Core.Evaluator.unwindStack:"+ , "Stack:"+ ] <>+ [ " " <> showDoc (clashPretty frame) | frame <- mStack m] <>+ [ ""+ , "Expression:"+ , showPpr (mTerm m)+ , ""+ , "Heap:"+ , showDoc (clashPretty $ mHeapLocal m)+ ]++ Scrutinise _ [] ->+ unwindStack m'++ Scrutinise ty alts ->+ let term = Case (getTerm m') ty alts+ in unwindStack (setTerm term m')++ Update LocalId x ->+ unwindStack (heapInsert LocalId x (mTerm m') m')++ Update GlobalId _ ->+ unwindStack m'++ Tickish sp ->+ let term = Tick sp (getTerm m')+ in unwindStack (setTerm term m')++-- | A single step in the partial evaluator. The result is the new heap and+-- stack, and the next expression to be reduced. ----- See [Note: forcing special primitives]-isScrut :: Stack -> Bool-isScrut (Scrutinise {}:_) = True-isScrut (PrimApply {} :_) = True-isScrut (Tickish {}:k) = isScrut k-isScrut _ = False+type Step = Machine -> TyConMap -> Maybe Machine --- | Completely unwind the stack to get back the complete term-unwindStack :: State -> Maybe State-unwindStack s@(_,[],_) = Just s-unwindStack (h@(Heap gh gbl h' ids is),(kf:k'),e) = case kf of- PrimApply nm ty tys vs tms ->- unwindStack- (h,k'- ,foldl' App- (foldl' App (foldl' TyApp (Prim nm ty) tys) (map valToTerm vs))- (e:tms))- Instantiate ty ->- unwindStack (h,k',TyApp e ty)- Apply id_ -> do- case lookupVarEnv id_ h' of- Just e' -> unwindStack (h,k',App e e')- Nothing -> error $ unlines- $ [ "Clash.Core.Evaluator.unwindStack:"- , "Stack:"- ] ++- [ " "++ showDoc (clashPretty frame) | frame <- kf:k'] ++- [ ""- , "Expression:"- , showPpr e- , ""- , "Heap:"- , showDoc (clashPretty h')- ]- Scrutinise _ [] ->- unwindStack (h,k',e)- Scrutinise ty alts ->- unwindStack (h,k',Case e ty alts)- Update x ->- unwindStack (Heap gh gbl (extendVarEnv x e h') ids is,k',e)- GUpdate _ ->- unwindStack (h,k',e)- Tickish sp ->- unwindStack (h,k',Tick sp e)+stepVar :: Id -> Step+stepVar i m _+ | Just e <- heapLookup LocalId i m+ = go LocalId e -{- [Note: forcing special primitives]-Clash uses the `whnf` function in two places (for now):+ | Just e <- heapLookup GlobalId i m+ , isGlobalId i+ = go GlobalId e - 1. The case-of-known-constructor transformation- 2. The reduceConstant transformation+ | otherwise+ = Nothing+ where+ go s e =+ let term = deShadowTerm (mScopeNames m) (tickExpr e)+ in Just . setTerm term . stackPush (Update s i) $ heapDelete s i m -The first transformation is needed to reach the required normal form. The-second transformation is more of cleanup transformation, so non-essential.+ -- Removing the heap-bound value on a force ensures we do not get stuck on+ -- expressions such as: "let x = x in x"+ tickExpr = Tick (NameMod PrefixName (LitTy . SymTy $ toStr i))+ unQualName = snd . Text.breakOnEnd "."+ toStr = Text.unpack . unQualName . flip Text.snoc '_' . nameOcc . varName -Normally, `whnf` would force the evaluation of all primitives, which is needed-in the `case-of-known-constructor` transformation. However, there are some-primitives which we want to leave unevaluated in the `reduceConstant`-transformation. Such primitives are:+stepData :: DataCon -> Step+stepData dc m tcm = unwind tcm m (DC dc []) - - Primitives such as `Clash.Sized.Vector.transpose`, `Clash.Sized.Vector.map`,- etc. that do not reduce to an expression in normal form. Where the- `reduceConstant` transformation is supposed to be normal-form preserving.- - Primitives such as `GHC.Int.I8#`, `GHC.Word.W32#`, etc. which seem like- wrappers around a 64-bit literal, but actually perform truncation to the- desired bit-size.+stepLiteral :: Literal -> Step+stepLiteral l m tcm = unwind tcm m (Lit l) -This is why the Primitive Evaluator gets a flag telling whether it should-evaluate these special primitives.--}+stepPrim :: PrimInfo -> Step+stepPrim pInfo m tcm+ | primName pInfo == "GHC.Prim.realWorld#" =+ unwind tcm m (PrimVal pInfo [] []) + | otherwise =+ case fst $ splitFunForallTy (primType pInfo) of+ [] -> mPrimStep m tcm (forcePrims m) pInfo [] [] m+ tys -> newBinder tys (Prim pInfo) m tcm++stepLam :: Id -> Term -> Step+stepLam x e m tcm = unwind tcm m (Lambda x e)++stepTyLam :: TyVar -> Term -> Step+stepTyLam x e m tcm = unwind tcm m (TyLambda x e)++stepApp :: Term -> Term -> Step+stepApp x y m tcm =+ case term of+ Data dc ->+ let tys = fst $ splitFunForallTy (dcType dc)+ in case compare (length args) (length tys) of+ EQ -> unwind tcm m (DC dc args)+ LT -> newBinder tys' (App x y) m tcm+ GT -> error "Overapplied DC"++ Prim p ->+ let tys = fst $ splitFunForallTy (primType p)+ in case compare (length args) (length tys) of+ EQ -> case lefts args of+ -- We make boolean conjunction and disjunction extra lazy by+ -- deferring the evaluation of the arguments during the evaluation+ -- of the primop rule.+ --+ -- This allows us to implement:+ --+ -- x && True --> x+ -- x && False --> False+ -- x || True --> True+ -- x || False --> x+ --+ -- even when that 'x' is _|_. This makes the evaluation+ -- rule lazier than the actual Haskel implementations which+ -- are strict in the first argument and lazy in the second.+ [a0, a1] | primName p `elem` ["GHC.Classes.&&","GHC.Classes.||"] ->+ let (m0,i) = newLetBinding tcm m a0+ (m1,j) = newLetBinding tcm m0 a1+ in mPrimStep m tcm (forcePrims m) p [] [Suspend (Var i), Suspend (Var j)] m1++ (e':es) ->+ Just . setTerm e' $ stackPush (PrimApply p (rights args) [] es) m++ _ -> error "internal error"++ LT -> newBinder tys' (App x y) m tcm++ GT -> let (m0, n) = newLetBinding tcm m y+ in Just . setTerm x $ stackPush (Apply n) m0++ _ -> let (m0, n) = newLetBinding tcm m y+ in Just . setTerm x $ stackPush (Apply n) m0+ where+ (term, args, _) = collectArgsTicks (App x y)+ tys' = fst . splitFunForallTy . termType tcm $ App x y++stepTyApp :: Term -> Type -> Step+stepTyApp x ty m tcm =+ case term of+ Data dc ->+ let tys = fst $ splitFunForallTy (dcType dc)+ in case compare (length args) (length tys) of+ EQ -> unwind tcm m (DC dc args)+ LT -> newBinder tys' (TyApp x ty) m tcm+ GT -> error "Overapplied DC"++ Prim p ->+ let tys = fst $ splitFunForallTy (primType p)+ in case compare (length args) (length tys) of+ EQ -> case lefts args of+ [] | primName p == "Clash.Transformations.removedArg" ->+ unwind tcm m (PrimVal p (rights args) [])++ | otherwise ->+ mPrimStep m tcm (forcePrims m) p (rights args) [] m++ (e':es) ->+ Just . setTerm e' $ stackPush (PrimApply p (rights args) [] es) m++ LT -> newBinder tys' (TyApp x ty) m tcm+ GT -> Just . setTerm x $ stackPush (Instantiate ty) m++ _ -> Just . setTerm x $ stackPush (Instantiate ty) m+ where+ (term, args, _) = collectArgsTicks (TyApp x ty)+ tys' = fst . splitFunForallTy . termType tcm $ TyApp x ty++stepLetRec :: [LetBinding] -> Term -> Step+stepLetRec bs x m _ = Just (allocate bs x m)++stepCase :: Term -> Type -> [Alt] -> Step+stepCase scrut ty alts m _ =+ Just . setTerm scrut $ stackPush (Scrutinise ty alts) m++-- TODO Support stepwise evaluation of casts.+--+stepCast :: Term -> Type -> Type -> Step+stepCast _ _ _ _ _ =+ flip trace Nothing $ unlines+ [ "WARNING: " <> $(curLoc) <> "Clash can't symbolically evaluate casts"+ , "Please file an issue at https://github.com/clash-lang/clash-compiler/issues"+ ]++stepTick :: TickInfo -> Term -> Step+stepTick tick x m _ =+ Just . setTerm x $ stackPush (Tickish tick) m+ -- | Small-step operational semantics.-step- :: PrimEvaluator- -> TyConMap- -> State- -> Maybe State-step eval tcm (h, k, e) = case e of- Var v -> force h k v- (Lam x e') -> unwind eval tcm h k (Lambda x e')- (TyLam x e') -> unwind eval tcm h k (TyLambda x e')- (Literal l) -> unwind eval tcm h k (Lit l)- (App e1 e2)- | (Data dc,args,_ticks) <- collectArgsTicks e- , (tys,_) <- splitFunForallTy (dcType dc)- -> case compare (length args) (length tys) of- EQ -> unwind eval tcm h k (DC dc args)- LT -> let (tys',_) = splitFunForallTy (termType tcm e)- (h2,e') = mkAbstr (h,e) tys'- in step eval tcm (h2,k,e')- GT -> error "Overapplied DC"- | (Prim nm pInfo,args,_ticks) <- collectArgsTicks e- , let ty = primType pInfo- , (tys,_) <- splitFunForallTy ty- -> case compare (length args) (length tys) of- EQ -> let (e':es) = lefts args- in Just (h,PrimApply nm pInfo (rights args) [] es:k,e')- LT -> let (tys',_) = splitFunForallTy (termType tcm e)- (h2,e') = mkAbstr (h,e) tys'- in step eval tcm (h2,k,e')- GT -> let (h2,id_) = newLetBinding tcm h e2- in Just (h2,Apply id_:k,e1)- (TyApp e1 ty)- | (Data dc,args,_ticks) <- collectArgsTicks e- , (tys,_) <- splitFunForallTy (dcType dc)- -> case compare (length args) (length tys) of- EQ -> unwind eval tcm h k (DC dc args)- LT -> let (tys',_) = splitFunForallTy (termType tcm e)- (h2,e') = mkAbstr (h,e) tys'- in step eval tcm (h2,k,e')- GT -> error "Overapplied DC"- | (Prim nm pInfo,args,_ticks) <- collectArgsTicks e- , let ty' = primType pInfo- , (tys,_) <- splitFunForallTy ty'- -> case compare (length args) (length tys) of- EQ -> case lefts args of- [] | nm `elem` ["Clash.Transformations.removedArg"]- -- The above primitives are actually values, and not operations.- -> unwind eval tcm h k (PrimVal nm pInfo (rights args) [])- | otherwise- -> eval (isScrut k) tcm h k nm pInfo (rights args) []- (e':es) -> Just (h,PrimApply nm pInfo (rights args) [] es:k,e')- LT -> let (tys',_) = splitFunForallTy (termType tcm e)- (h2,e') = mkAbstr (h,e) tys'- in step eval tcm (h2,k,e')- GT -> Just (h,Instantiate ty:k,e1)- (Data dc) -> unwind eval tcm h k (DC dc [])- (Prim nm pInfo)- | nm `elem` ["GHC.Prim.realWorld#"]- -> unwind eval tcm h k (PrimVal nm pInfo [] [])- | otherwise- , let ty' = primType pInfo- -> case fst (splitFunForallTy ty') of- [] -> eval (isScrut k) tcm h k nm pInfo [] []- tys -> let (h2,e') = mkAbstr (h,e) tys- in step eval tcm (h2,k,e')- (App e1 e2) -> let (h2,id_) = newLetBinding tcm h e2- in Just (h2,Apply id_:k,e1)- (TyApp e1 ty) -> Just (h,Instantiate ty:k,e1)- (Case scrut ty alts) -> Just (h,Scrutinise ty alts:k,scrut)- (Letrec bs e') -> Just (allocate h k bs e')- Tick sp e' -> Just (h,Tickish sp:k,e')- Cast _ _ _ -> trace (unlines ["WARNING: " ++ $(curLoc) ++ "Clash currently can't symbolically evaluate casts"- ,"If you have testcase that produces this message, please open an issue about it."]) Nothing+--+step :: Step+step m = case mTerm m of+ Var i -> stepVar i m+ Data dc -> stepData dc m+ Literal l -> stepLiteral l m+ Prim p -> stepPrim p m+ Lam v x -> stepLam v x m+ TyLam v x -> stepTyLam v x m+ App x y -> stepApp x y m+ TyApp x ty -> stepTyApp x ty m+ Letrec bs x -> stepLetRec bs x m+ Case s ty as -> stepCase s ty as m+ Cast x a b -> stepCast x a b m+ Tick t x -> stepTick t x m +-- | Take a list of types or type variables and create a lambda / type lambda+-- for each one around the given term.+--+newBinder :: [Either TyVar Type] -> Term -> Step+newBinder tys x m tcm =+ let (s', iss', x') = mkAbstr (mSupply m, mScopeNames m, x) tys+ m' = m { mSupply = s', mScopeNames = iss', mTerm = x' }+ in step m' tcm+ where+ mkAbstr = foldr go+ where+ go (Left tv) (s', iss', e') =+ (s', iss', TyLam tv (TyApp e' (VarTy tv)))++ go (Right ty) (s', iss', e') =+ let ((s'', _), n) = mkUniqSystemId (s', iss') ("x", ty)+ in (s'', iss' ,Lam n (App e' (Var n)))+ newLetBinding :: TyConMap- -> Heap+ -> Machine -> Term- -> (Heap,Id)-newLetBinding tcm h@(Heap gh gbl h' ids is0) e+ -> (Machine, Id)+newLetBinding tcm m e | Var v <- e- , Just _ <- lookupVarEnv v h'- = (h, v)- | otherwise- = (Heap gh gbl (extendVarEnv id_ e h') ids' is1,id_)- where- ty = termType tcm e- ((ids',is1),id_) = mkUniqSystemId (ids,is0) ("x",ty)--newLetBindings'- :: TyConMap- -> Heap- -> [Either Term Type]- -> (Heap,[Either Term Type])-newLetBindings' tcm =- (second (map (either (Left . toVar) (Right . id))) .) . mapAccumL go- where- go h (Left tm) = second Left (newLetBinding tcm h tm)- go h (Right ty) = (h,Right ty)--mkAbstr- :: (Heap,Term)- -> [Either TyVar Type]- -> (Heap,Term)-mkAbstr = foldr go- where- go (Left tv) (h,e) =- (h,TyLam tv (TyApp e (VarTy tv)))- go (Right ty) (Heap gh gbl h ids is,e) =- let ((ids',_),id_) = mkUniqSystemId (ids,is) ("x",ty)- in (Heap gh gbl h ids' is,Lam id_ (App e (Var id_)))+ , heapContains LocalId v m+ = (m, v) --- | Force the evaluation of a variable.-force :: Heap -> Stack -> Id -> Maybe State-force (Heap gh g@(GPureHeap gbl) h ids is) k x' = case lookupVarEnv x' h of- Nothing -> case lookupVarEnv x' gbl of- Just e | isGlobalId x'- -> Just (Heap gh (GPureHeap (delVarEnv gbl x')) h ids is,GUpdate x':k,e)- _ -> Nothing- Just e -> Just (Heap gh g (delVarEnv h x') ids is,Update x':k,e)- -- Removing the heap-bound value on a force ensures we do not get stuck on- -- expressions such as: "let x = x in x"+ | otherwise+ = let m' = heapInsert LocalId id_ e m+ in (m' { mSupply = ids', mScopeNames = is1 }, id_)+ where+ ty = termType tcm e+ ((ids', is1), id_) = mkUniqSystemId (mSupply m, mScopeNames m) ("x", ty) -- | Unwind the stack by 1 unwind- :: PrimEvaluator- -> TyConMap- -> Heap -> Stack -> Value -> Maybe State-unwind eval tcm h k v = do- (kf,k') <- uncons k- case kf of- Update x -> return (update h k' x v)- GUpdate x -> return (gupdate h k' x v)- Apply x -> return (apply h k' v x)- Instantiate ty -> return (instantiate h k' v ty)- PrimApply nm ty tys vals tms -> primop eval tcm h k' nm ty tys vals v tms- Scrutinise _ alts -> return (scrutinise h k' v alts)- -- Adding back the Tick constructor will make the evaluator loop- Tickish _ -> return (h,k',valToTerm v)---- | Update the Heap with the evaluated term-update :: Heap -> Stack -> Id -> Value -> State-update (Heap gh gbl h ids is) k x v = (Heap gh gbl (extendVarEnv x v' h) ids is,k,v')- where- v' = valToTerm v---- | Update the Globals with the evaluated term-gupdate :: Heap -> Stack -> Id -> Value -> State-gupdate (Heap gh (GPureHeap gbl) h ids is) k x v =- (Heap gh (GPureHeap (extendVarEnv x v' gbl)) h ids is,k,v')+ :: TyConMap+ -> Machine+ -> Value+ -> Maybe Machine+unwind tcm m v = do+ (m', kf) <- stackPop m+ go kf m' where- v' = valToTerm v--valToTerm :: Value -> Term-valToTerm v = case v of- Lambda x e -> Lam x e- TyLambda x e -> TyLam x e- DC dc pxs -> foldl' (\e a -> either (App e) (TyApp e) a)- (Data dc) pxs- Lit l -> Literal l- PrimVal nm ty tys vs -> foldl' App (foldl' TyApp (Prim nm ty) tys)- (map valToTerm vs)- Suspend e -> e--toVar :: Id -> Term-toVar x = Var x+ go (Update s x) = return . update s x v+ go (Apply x) = return . apply v x+ go (Instantiate ty) = return . instantiate v ty+ go (PrimApply p tys vs tms) = mPrimUnwind m tcm p tys vs v tms+ go (Scrutinise _ as) = return . scrutinise v as+ go (Tickish _) = return . setTerm (valToTerm v) -toType :: TyVar -> Type-toType x = VarTy x+-- | Update the Heap with the evaluated term+update :: IdScope -> Id -> Value -> Machine -> Machine+update s x (valToTerm -> term) =+ setTerm term . heapInsert s x term -- | Apply a value to a function-apply :: Heap -> Stack -> Value -> Id -> State-apply h@(Heap _ _ _ _ is0) k (Lambda x' e) x = (h,k,substTm "Evaluator.apply" subst e)+apply :: Value -> Id -> Machine -> Machine+apply (Lambda x' e) x m =+ setTerm (substTm "Evaluator.apply" subst e) m where subst = extendIdSubst subst0 x' (Var x)- subst0 = mkSubst (extendInScopeSet is0 x)-apply _ _ _ _ = error "not a lambda"+ subst0 = mkSubst $ extendInScopeSet (mScopeNames m) x +apply _ _ _ = error "Evaluator.apply: Not a lambda"+ -- | Instantiate a type-abstraction-instantiate :: Heap -> Stack -> Value -> Type -> State-instantiate h k (TyLambda x e) ty = (h,k,substTm "Evaluator.instantiate" subst e)+instantiate :: Value -> Type -> Machine -> Machine+instantiate (TyLambda x e) ty =+ setTerm (substTm "Evaluator.instantiate" subst e) where subst = extendTvSubst subst0 x ty- subst0 = mkSubst is0- is0 = mkInScopeSet (localFVsOfTerms [e] `unionUniqSet` tyFVsOfTypes [ty])-instantiate _ _ _ _ = error "not a ty lambda"--naturalLiteral :: Value -> Maybe Integer-naturalLiteral v =- case v of- Lit (NaturalLiteral i) -> Just i- DC dc [Left (Literal (WordLiteral i))]- | dcTag dc == 1- -> Just i- DC dc [Left (Literal (ByteArrayLiteral (PV.Vector _ _ (BA.ByteArray ba))))]- | dcTag dc == 2- -> Just (Jp# (BN# ba))- _ -> Nothing--integerLiteral :: Value -> Maybe Integer-integerLiteral v =- case v of- Lit (IntegerLiteral i) -> Just i- DC dc [Left (Literal (IntLiteral i))]- | dcTag dc == 1- -> Just i- DC dc [Left (Literal (ByteArrayLiteral (PV.Vector _ _ (BA.ByteArray ba))))]- | dcTag dc == 2- -> Just (Jp# (BN# ba))- | dcTag dc == 3- -> Just (Jn# (BN# ba))- _ -> Nothing+ subst0 = mkSubst iss0+ iss0 = mkInScopeSet (localFVsOfTerms [e] `unionUniqSet` tyFVsOfTypes [ty]) --- | Evaluation of primitive operations-primop- :: PrimEvaluator- -> TyConMap- -> Heap- -> Stack- -> Text- -- ^ Name of the primitive- -> PrimInfo- -- ^ Type of the primitive- -> [Type]- -- ^ Applied types- -> [Value]- -- ^ Applied values- -> Value- -- ^ The current value- -> [Term]- -- ^ The remaining terms which must be evaluated to a value- -> Maybe State-primop eval tcm h k nm ty tys vs v []- | nm `elem` ["Clash.Sized.Internal.Index.fromInteger#"- ,"GHC.CString.unpackCString#"- ,"Clash.Transformations.removedArg"- ,"GHC.Prim.MutableByteArray#"- ]- -- The above primitives are actually values, and not operations.- = unwind eval tcm h k (PrimVal nm ty tys (vs ++ [v]))- | nm == "Clash.Sized.Internal.BitVector.fromInteger#"- = case (vs,v) of- ([naturalLiteral -> Just n,mask], integerLiteral -> Just i) ->- unwind eval tcm h k (PrimVal nm ty tys [Lit (NaturalLiteral n)- ,mask- ,Lit (IntegerLiteral (wrapUnsigned n i))])- _ -> error ($(curLoc) ++ "Internal error" ++ show (vs,v))- | nm == "Clash.Sized.Internal.BitVector.fromInteger##"- = case (vs,v) of- ([mask], integerLiteral -> Just i) ->- unwind eval tcm h k (PrimVal nm ty tys [mask- ,Lit (IntegerLiteral (wrapUnsigned 1 i))])- _ -> error ($(curLoc) ++ "Internal error" ++ show (vs,v))- | nm == "Clash.Sized.Internal.Signed.fromInteger#"- = case (vs,v) of- ([naturalLiteral -> Just n],integerLiteral -> Just i) ->- unwind eval tcm h k (PrimVal nm ty tys [Lit (NaturalLiteral n)- ,Lit (IntegerLiteral (wrapSigned n i))])- _ -> error ($(curLoc) ++ "Internal error" ++ show (vs,v))- | nm == "Clash.Sized.Internal.Unsigned.fromInteger#"- = case (vs,v) of- ([naturalLiteral -> Just n],integerLiteral -> Just i) ->- unwind eval tcm h k (PrimVal nm ty tys [Lit (NaturalLiteral n)- ,Lit (IntegerLiteral (wrapUnsigned n i))])- _ -> error ($(curLoc) ++ "Internal error" ++ show (vs,v))- | otherwise = eval (isScrut k) tcm h k nm ty tys (vs ++ [v])-primop eval tcm h0 k nm ty tys vs v [e]- | nm `elem` [ "Clash.Sized.Vector.lazyV"- , "Clash.Sized.Vector.replicate"- , "Clash.Sized.Vector.replace_int"- ]- = let (h1,i) = newLetBinding tcm h0 e- in eval (isScrut k) tcm h1 k nm ty tys (vs ++ [v,Suspend (Var i)])-primop _ _ h k nm ty tys vs v (e:es) =- Just (h,PrimApply nm ty tys (vs ++ [v]) es:k,e)+instantiate _ _ = error "Evaluator.instantiate: Not a tylambda" -- | Evaluate a case-expression-scrutinise :: Heap -> Stack -> Value -> [Alt] -> State-scrutinise h k v [] = (h,k,valToTerm v)+scrutinise :: Value -> [Alt] -> Machine -> Machine+scrutinise v [] m = setTerm (valToTerm v) m -- [Note: empty case expressions] -- -- Clash does not have empty case-expressions; instead, empty case-expressions -- are used to indicate that the `whnf` function was called the context of a -- case-expression, which means certain special primitives must be forced. -- See also [Note: forcing special primitives]-scrutinise h k (Lit l) alts = case alts of- (DefaultPat,altE):alts1 -> (h,k,go altE alts1)- _ -> (h,k,go (error ("scrutinise: no match " ++- showPpr (Case (valToTerm (Lit l)) (ConstTy Arrow) alts))) alts)+scrutinise (Lit l) alts m = case alts of+ (DefaultPat, altE):alts1 -> setTerm (go altE alts1) m+ _ -> let term = go (error $ "Evaluator.scrutinise: no match "+ <> showPpr (Case (valToTerm (Lit l)) (ConstTy Arrow) alts)) alts+ in setTerm term m where go def [] = def go _ ((LitPat l1,altE):_) | l1 == l = altE@@ -567,24 +437,25 @@ in substTm "Evaluator.scrutinise" subst1 altE go def (_:alts1) = go def alts1 -scrutinise h k (DC dc xs) alts- | altE:_ <- [substAlt altDc tvs pxs xs altE+scrutinise (DC dc xs) alts m+ | altE:_ <- [substInAlt altDc tvs pxs xs altE | (DataPat altDc tvs pxs,altE) <- alts, altDc == dc ] ++ [altE | (DefaultPat,altE) <- alts ]- = (h,k,altE)+ = setTerm altE m -scrutinise h k v@(PrimVal nm _ _ vs) alts+scrutinise v@(PrimVal p _ vs) alts m | any (\case {(LitPat {},_) -> True; _ -> False}) alts = case alts of- ((DefaultPat,altE):alts1) -> (h,k,go altE alts1)- _ -> (h,k,go (error ("scrutinise: no match " ++- showPpr (Case (valToTerm v) (ConstTy Arrow) alts))) alts)+ ((DefaultPat,altE):alts1) -> setTerm (go altE alts1) m+ _ -> let term = go (error $ "Evaluator.scrutinise: no match "+ <> showPpr (Case (valToTerm v) (ConstTy Arrow) alts)) alts+ in setTerm term m where go def [] = def go _ ((LitPat l1,altE):_) | l1 == l = altE go def (_:alts1) = go def alts1 - l = case nm of+ l = case primName p of "Clash.Sized.Internal.BitVector.fromInteger#" | [_,Lit (IntegerLiteral 0),Lit l0] <- vs -> l0 "Clash.Sized.Internal.Index.fromInteger#"@@ -595,10 +466,10 @@ | [_,Lit l0] <- vs -> l0 _ -> error ("scrutinise: " ++ showPpr (Case (valToTerm v) (ConstTy Arrow) alts)) -scrutinise _ _ v alts = error ("scrutinise: " ++ showPpr (Case (valToTerm v) (ConstTy Arrow) alts))+scrutinise v alts _ = error ("scrutinise: " ++ showPpr (Case (valToTerm v) (ConstTy Arrow) alts)) -substAlt :: DataCon -> [TyVar] -> [Id] -> [Either Term Type] -> Term -> Term-substAlt dc tvs xs args e = substTm "Evaluator.substAlt" subst e+substInAlt :: DataCon -> [TyVar] -> [Id] -> [Either Term Type] -> Term -> Term+substInAlt dc tvs xs args e = substTm "Evaluator.substInAlt" subst e where tys = rights args tms = lefts args@@ -609,53 +480,38 @@ subst0 = mkSubst (mkInScopeSet inScope) -- | Allocate let-bindings on the heap-allocate :: Heap -> Stack -> [LetBinding] -> Term -> State-allocate (Heap gh gbl h ids is0) k xes e =- (Heap gh gbl (h `extendVarEnvList` xes') ids' isN,k,e')+allocate :: [LetBinding] -> Term -> Machine -> Machine+allocate xes e m =+ m { mHeapLocal = extendVarEnvList (mHeapLocal m) xes'+ , mSupply = ids'+ , mScopeNames = isN+ , mTerm = e'+ } where- xNms = map fst xes- is1 = extendInScopeSetList is0 xNms- (ids',s) = mapAccumL (letSubst h) ids xNms- (nms,s') = unzip s- isN = extendInScopeSetList is1 nms- subst = extendIdSubstList subst0 s'- subst0 = mkSubst (foldl' extendInScopeSet is1 nms)- xes' = zip nms (map (substTm "Evaluator.allocate0" subst . snd) xes)- e' = substTm "Evaluator.allocate1" subst e+ xNms = fmap fst xes+ is1 = extendInScopeSetList (mScopeNames m) xNms+ (ids', s) = mapAccumL (letSubst (mHeapLocal m)) (mSupply m) xNms+ (nms, s') = unzip s+ isN = extendInScopeSetList is1 nms+ subst = extendIdSubstList subst0 s'+ subst0 = mkSubst (foldl' extendInScopeSet is1 nms)+ xes' = zip nms (fmap (substTm "Evaluator.allocate0" subst . snd) xes)+ e' = substTm "Evaluator.allocate1" subst e -- | Create a unique name and substitution for a let-binder letSubst :: PureHeap -> Supply -> Id- -> ( Supply- , (Id,(Id,Term)))+ -> (Supply, (Id, (Id, Term))) letSubst h acc id0 =- let (acc',id1) = uniqueInHeap h acc id0+ let (acc',id1) = mkUniqueHeapId h acc id0 in (acc',(id1,(id0,Var id1)))---- | Create a name that's unique in the heap-uniqueInHeap- :: PureHeap- -> Supply- -> Id- -> (Supply, Id)-uniqueInHeap h ids x = case lookupVarEnv x' h of- Just _ -> uniqueInHeap h ids' x- _ -> (ids',x') where- (i,ids') = freshId ids- x' = modifyVarName (\nm -> nm {nameUniq = i}) x--wrapUnsigned :: Integer -> Integer -> Integer-wrapUnsigned n i = i `mod` sz- where- sz = 1 `shiftL` fromInteger n+ mkUniqueHeapId :: PureHeap -> Supply -> Id -> (Supply, Id)+ mkUniqueHeapId h' ids x =+ maybe (ids', x') (const $ mkUniqueHeapId h' ids' x) (lookupVarEnv x' h')+ where+ (i,ids') = freshId ids+ x' = modifyVarName (`setUnique` i) x -wrapSigned :: Integer -> Integer -> Integer-wrapSigned n i = if mask == 0 then 0 else res- where- mask = 1 `shiftL` fromInteger (n - 1)- res = case divMod i mask of- (s,i1) | even s -> i1- | otherwise -> i1 - mask
+ src/Clash/Core/Evaluator/Types.hs view
@@ -0,0 +1,249 @@+{-# LANGUAGE OverloadedStrings #-}++{-|+ Copyright : (C) 2020, QBayLogic B.V.+ License : BSD2 (see the file LICENSE)+ Maintainer : Christiaan Baaij <christiaan.baaij@gmail.com>++ Types for the Partial Evaluator+-}+module Clash.Core.Evaluator.Types where++import Control.Concurrent.Supply (Supply)+import Data.IntMap.Strict (IntMap)+import qualified Data.IntMap.Strict as IntMap (insert, lookup)+import Data.List (foldl')+import Data.Maybe (isJust)+import Data.Text.Prettyprint.Doc (hsep)++import Clash.Core.DataCon (DataCon)+import Clash.Core.Literal (Literal(CharLiteral))+import Clash.Core.Pretty (fromPpr)+import Clash.Core.Term (Term(..), PrimInfo(..), TickInfo, Alt)+import Clash.Core.TyCon (TyConMap)+import Clash.Core.Type (Type)+import Clash.Core.Var (Id, IdScope(..), TyVar)+import Clash.Core.VarEnv+import Clash.Pretty (ClashPretty(..), fromPretty)++{- [Note: forcing special primitives]+Clash uses the `whnf` function in two places (for now):++ 1. The case-of-known-constructor transformation+ 2. The reduceConstant transformation++The first transformation is needed to reach the required normal form. The+second transformation is more of cleanup transformation, so non-essential.++Normally, `whnf` would force the evaluation of all primitives, which is needed+in the `case-of-known-constructor` transformation. However, there are some+primitives which we want to leave unevaluated in the `reduceConstant`+transformation. Such primitives are:++ - Primitives such as `Clash.Sized.Vector.transpose`, `Clash.Sized.Vector.map`,+ etc. that do not reduce to an expression in normal form. Where the+ `reduceConstant` transformation is supposed to be normal-form preserving.+ - Primitives such as `GHC.Int.I8#`, `GHC.Word.W32#`, etc. which seem like+ wrappers around a 64-bit literal, but actually perform truncation to the+ desired bit-size.++This is why the Primitive Evaluator gets a flag telling whether it should+evaluate these special primitives.+-}++type PrimStep+ = TyConMap+ -> Bool+ -> PrimInfo+ -> [Type]+ -> [Value]+ -> Machine+ -> Maybe Machine++type PrimUnwind+ = TyConMap+ -> PrimInfo+ -> [Type]+ -> [Value]+ -> Value+ -> [Term]+ -> Machine+ -> Maybe Machine++type PrimEvaluator = (PrimStep, PrimUnwind)++data Machine = Machine+ { mPrimStep :: PrimStep+ , mPrimUnwind :: PrimUnwind+ , mHeapPrim :: PrimHeap+ , mHeapGlobal :: PureHeap+ , mHeapLocal :: PureHeap+ , mStack :: Stack+ , mSupply :: Supply+ , mScopeNames :: InScopeSet+ , mTerm :: Term+ }++instance Show Machine where+ show (Machine _ _ ph gh lh s _ _ x) =+ unlines+ [ "Machine:"+ , ""+ , "Heap (Prim):"+ , show ph+ , ""+ , "Heap (Globals):"+ , show gh+ , ""+ , "Heap (Locals):"+ , show lh+ , ""+ , "Stack:"+ , show (fmap clashPretty s)+ , ""+ , "Term:"+ , show x+ ]++type PrimHeap = (IntMap Term, Int)+type PureHeap = VarEnv Term++type Stack = [StackFrame]++data StackFrame+ = Update IdScope Id+ | Apply Id+ | Instantiate Type+ | PrimApply PrimInfo [Type] [Value] [Term]+ | Scrutinise Type [Alt]+ | Tickish TickInfo+ deriving Show++instance ClashPretty StackFrame where+ clashPretty (Update GlobalId i) = hsep ["Update(Global)", fromPpr i]+ clashPretty (Update LocalId i) = hsep ["Update(Local)", fromPpr i]+ clashPretty (Apply i) = hsep ["Apply", fromPpr i]+ clashPretty (Instantiate t) = hsep ["Instantiate", fromPpr t]+ clashPretty (PrimApply p tys vs ts) =+ hsep ["PrimApply", fromPretty (primName p), "::", fromPpr (primType p),+ "; type args=", fromPpr tys,+ "; val args=", fromPpr (map valToTerm vs),+ "term args=", fromPpr ts]+ clashPretty (Scrutinise a b) =+ hsep ["Scrutinise ", fromPpr a,+ fromPpr (Case (Literal (CharLiteral '_')) a b)]+ clashPretty (Tickish sp) =+ hsep ["Tick", fromPpr sp]++-- Values+data Value+ = Lambda Id Term+ -- ^ Functions+ | TyLambda TyVar Term+ -- ^ Type abstractions+ | DC DataCon [Either Term Type]+ -- ^ Data constructors+ | Lit Literal+ -- ^ Literals+ | PrimVal PrimInfo [Type] [Value]+ -- ^ Clash's number types are represented by their "fromInteger#" primitive+ -- function. So some primitives are values.+ | Suspend Term+ -- ^ Used by lazy primitives+ | TickValue TickInfo Value+ -- ^ Preserve ticks from Terms in Values+ | CastValue Value Type Type+ -- ^ Preserve casts from Terms in Values+ deriving Show++valToTerm :: Value -> Term+valToTerm v = case v of+ Lambda x e -> Lam x e+ TyLambda x e -> TyLam x e+ DC dc pxs -> foldl' (\e a -> either (App e) (TyApp e) a)+ (Data dc) pxs+ Lit l -> Literal l+ PrimVal ty tys vs -> foldl' App (foldl' TyApp (Prim ty) tys)+ (map valToTerm vs)+ Suspend e -> e+ TickValue t x -> Tick t (valToTerm x)+ CastValue x t1 t2 -> Cast (valToTerm x) t1 t2++-- Collect all the ticks from a value, exposing the ticked value.+--+collectValueTicks+ :: Value+ -> (Value, [TickInfo])+collectValueTicks = go []+ where+ go ticks (TickValue t v) = go (t:ticks) v+ go ticks v = (v, ticks)++-- | Are we in a context where special primitives must be forced.+--+-- See [Note: forcing special primitives]+forcePrims :: Machine -> Bool+forcePrims = go . mStack+ where+ go (Scrutinise{}:_) = True+ go (PrimApply{}:_) = True+ go (Tickish{}:xs) = go xs+ go _ = False++primCount :: Machine -> Int+primCount = snd . mHeapPrim++primLookup :: Int -> Machine -> Maybe Term+primLookup i = IntMap.lookup i . fst . mHeapPrim++primInsert :: Int -> Term -> Machine -> Machine+primInsert i x m =+ let (gh, c) = mHeapPrim m+ in m { mHeapPrim = (IntMap.insert i x gh, c + 1) }++primUpdate :: Int -> Term -> Machine -> Machine+primUpdate i x m =+ let (gh, c) = mHeapPrim m+ in m { mHeapPrim = (IntMap.insert i x gh, c) }++heapLookup :: IdScope -> Id -> Machine -> Maybe Term+heapLookup GlobalId i m =+ lookupVarEnv i $ mHeapGlobal m+heapLookup LocalId i m =+ lookupVarEnv i $ mHeapLocal m++heapContains :: IdScope -> Id -> Machine -> Bool+heapContains scope i = isJust . heapLookup scope i++heapInsert :: IdScope -> Id -> Term -> Machine -> Machine+heapInsert GlobalId i x m =+ m { mHeapGlobal = extendVarEnv i x (mHeapGlobal m) }+heapInsert LocalId i x m =+ m { mHeapLocal = extendVarEnv i x (mHeapLocal m) }++heapDelete :: IdScope -> Id -> Machine -> Machine+heapDelete GlobalId i m =+ m { mHeapGlobal = delVarEnv (mHeapGlobal m) i }+heapDelete LocalId i m =+ m { mHeapLocal = delVarEnv (mHeapLocal m) i }++stackPush :: StackFrame -> Machine -> Machine+stackPush f m = m { mStack = f : mStack m }++stackPop :: Machine -> Maybe (Machine, StackFrame)+stackPop m = case mStack m of+ [] -> Nothing+ (x:xs) -> Just (m { mStack = xs }, x)++stackClear :: Machine -> Machine+stackClear m = m { mStack = [] }++stackNull :: Machine -> Bool+stackNull = null . mStack++getTerm :: Machine -> Term+getTerm = mTerm++setTerm :: Term -> Machine -> Machine+setTerm x m = m { mTerm = x }+
src/Clash/Core/FreeVars.hs view
@@ -6,14 +6,12 @@ Free variable calculations -} -{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE RankNTypes #-} module Clash.Core.FreeVars (-- * Free variable calculation typeFreeVars- , termFreeVarsX , freeIds , freeLocalVars , freeLocalIds@@ -30,6 +28,7 @@ , localIdDoesNotOccurIn , localIdsDoNotOccurIn , localVarsDoNotOccurIn+ , countFreeOccurances -- * Internal , typeFreeVars' , termFreeVars'@@ -47,7 +46,8 @@ import Clash.Core.Type (Type (..)) import Clash.Core.Var (Id, IdScope (..), TyVar, Var (..), isLocalId)-import Clash.Core.VarEnv (VarSet, unitVarSet)+import Clash.Core.VarEnv+ (VarEnv, VarSet, emptyVarEnv, unionVarEnvWith, unitVarSet, unitVarEnv) -- | Gives the free type-variables in a Type, implemented as a 'Fold' --@@ -197,19 +197,6 @@ isTV (TyVar {}) = True isTV _ = False --- | Gives the free variables of a Term, implemented as a 'Fold'------ The 'Fold' is closed over the types of variables, so:------ @--- foldMapOf termFreeVars unitVarSet (case (x : (a:* -> k) Int)) of {}) = {x, a, k}--- @------ __NB__ this collects both global and local IDs, and you almost __NEVER__ want--- to use this. Use one of the other FV calculations instead-termFreeVarsX :: Fold Term (Var a)-termFreeVarsX = termFreeVars' (const True)- -- | Gives the "interesting" free variables in a Term, implemented as a 'Fold' -- -- The 'Fold' is closed over the types of variables, so:@@ -341,3 +328,11 @@ localFVsOfTerms = foldMap go where go = Lens.foldMapOf freeLocalVars unitVarSet++-- | Get the free variables of an expression and count the number of occurrences+countFreeOccurances+ :: Term+ -> VarEnv Int+countFreeOccurances =+ Lens.foldMapByOf freeLocalIds (unionVarEnvWith (+)) emptyVarEnv+ (`unitVarEnv` (1 :: Int))
src/Clash/Core/Literal.hs view
@@ -8,9 +8,8 @@ Term Literal -} -{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} module Clash.Core.Literal
src/Clash/Core/Name.hs view
@@ -6,12 +6,11 @@ Names -} -{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-} module Clash.Core.Name ( module Clash.Core.Name
src/Clash/Core/Pretty.hs view
@@ -8,10 +8,9 @@ -} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE ViewPatterns #-}-{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -fno-warn-orphans #-} @@ -30,6 +29,7 @@ where import Data.Char (isSymbol, isUpper, ord)+import Data.Default (Default(..)) import Data.Text (Text) import Control.Monad.Identity import qualified Data.Text as T@@ -44,11 +44,11 @@ import Clash.Core.Literal (Literal (..)) import Clash.Core.Name (Name (..)) import Clash.Core.Term- (Pat (..), Term (..), TickInfo (..), NameMod (..), CoreContext (..), primArg)+ (Pat (..), Term (..), TickInfo (..), NameMod (..), CoreContext (..), primArg, PrimInfo(primName)) import Clash.Core.TyCon (TyCon (..), TyConName, isTupleTyConLike) import Clash.Core.Type (ConstTy (..), Kind, LitTy (..), Type (..), TypeView (..), tyView)-import Clash.Core.Var (Id, TyVar, Var (..))+import Clash.Core.Var (Id, TyVar, Var (..), IdScope(..)) import Clash.Util import Clash.Pretty @@ -61,6 +61,12 @@ , displayQualifiers :: Bool -- ^ whether to display module qualifiers }+instance Default PrettyOptions where+ def = PrettyOptions+ { displayUniques = True+ , displayTypes = True+ , displayQualifiers = True+ } -- | Annotations carried on pretty-printed code. data ClashAnnotation@@ -160,7 +166,7 @@ pprPrec p (viewName -> (qual, occ, uniq)) = do qual' <- annotate (AnnSyntax Qualifier) <$> pprPrec p qual occ' <- pprPrec p occ- uniq' <- annotate (AnnSyntax Unique) <$> pprPrec p uniq+ uniq' <- annotate (AnnSyntax Unique) . brackets <$> (pprPrec p uniq) return $ qual' <> occ' <> uniq' instance ClashPretty (Name a) where@@ -206,10 +212,13 @@ instance PrettyPrec Term where pprPrec prec e = case e of- Var x -> pprPrec prec (varName x)+ Var x -> do+ v <- pprPrec prec (varName x)+ s <- pprPrecIdScope x+ pure (v <> brackets s) Data dc -> pprPrec prec dc Literal l -> pprPrec prec l- Prim nm _ -> pprPrecPrim prec nm+ Prim p -> pprPrecPrim prec (primName p) Lam v e1 -> annotate (AnnContext $ LamBody v) <$> pprPrecLam prec [v] e1 TyLam tv e1 -> annotate (AnnContext $ TyLamBody tv) <$>@@ -229,7 +238,10 @@ pprPrec prec (SrcSpan sp) = pprPrec prec sp pprPrec prec (NameMod PrefixName t) = ("<prefixName>" <>) <$> pprPrec prec t pprPrec prec (NameMod SuffixName t) = ("<suffixName>" <>) <$> pprPrec prec t+ pprPrec prec (NameMod SuffixNameP t) = ("<suffixNameP>" <>) <$> pprPrec prec t pprPrec prec (NameMod SetName t) = ("<setName>" <>) <$> pprPrec prec t+ pprPrec _ DeDup = pure "<deDup>"+ pprPrec _ NoDeDup = pure "<noDeDup>" instance PrettyPrec SrcSpan where pprPrec _ sp = return ("<src>"<>pretty (GHC.showSDocUnsafe (GHC.ppr sp)))@@ -284,6 +296,11 @@ LitPat l -> pprM l DefaultPat -> return "_" +pprPrecIdScope :: Monad m => Var a -> m ClashDoc+pprPrecIdScope (TyVar {}) = pure "TyVar"+pprPrecIdScope (Id _ _ _ GlobalId) = pure "GlobalId"+pprPrecIdScope (Id _ _ _ LocalId) = pure "LocalId"+ pprPrecPrim :: Monad m => Rational -> Text -> m ClashDoc pprPrecPrim prec nm = (<>) <$> (annotate (AnnSyntax Qualifier) <$> pprPrec prec qual)@@ -474,4 +491,3 @@ isSymbolASCII :: Char -> Bool isSymbolASCII c = c `elem` ("!#$%&*+./<=>?@\\^|~-" :: String)-
src/Clash/Core/Subst.hs view
@@ -7,10 +7,9 @@ Capture-free substitution function for CoreHW -} -{-# LANGUAGE CPP #-}-{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ViewPatterns #-} {-# OPTIONS_GHC -fno-warn-orphans #-} @@ -40,9 +39,13 @@ , extendGblSubstList -- ** Applying substitutions , substTm+ , substAlt+ , substId -- * Variable renaming , deShadowTerm+ , deShadowAlt , freshenTm+ , deshadowLetExpr -- * Alpha equivalence , aeqType , aeqTerm@@ -52,12 +55,13 @@ import Data.Coerce (coerce) import Data.Text.Prettyprint.Doc import qualified Data.List as List+import Data.Ord (comparing) import Clash.Core.FreeVars (noFreeVarsOfType, localFVsOfTerms, tyFVsOfTypes) import Clash.Core.Pretty (ppr, fromPpr) import Clash.Core.Term- (LetBinding, Pat (..), Term (..), TickInfo (..))+ (LetBinding, Pat (..), Term (..), TickInfo (..), PrimInfo(primName)) import Clash.Core.Type (Type (..)) import Clash.Core.VarEnv import Clash.Core.Var (Id, Var (..), TyVar, isGlobalId)@@ -488,7 +492,31 @@ goTick t@(SrcSpan _) = t goTick (NameMod m ty) = NameMod m (substTy subst ty)+ goTick t@DeDup = t+ goTick t@NoDeDup = t +-- | Substitute within a case-alternative+substAlt+ :: HasCallStack+ => Doc ()+ -> Subst+ -- ^ The substitution+ -> (Pat, Term)+ -- ^ The alternative in which to apply the substitution+ -> (Pat, Term)+substAlt doc subst (pat,alt) = case pat of+ DataPat dc tvs ids -> case List.mapAccumL substTyVarBndr' subst tvs of+ (subst1,tvs1) -> case List.mapAccumL substIdBndr subst1 ids of+ (subst2,ids1) -> (DataPat dc tvs1 ids1,substTm doc subst2 alt)+ _ -> (pat, substTm doc subst alt)++substId+ :: HasCallStack+ => Subst+ -> Id+ -> Id+substId subst oldId = snd $ substIdBndr subst oldId+ -- | Find the substitution for an 'Id' in the 'Subst' lookupIdSubst :: HasCallStack@@ -502,7 +530,14 @@ _ -> Var v | Just e <- lookupVarEnv v tmS = e -- Vital! See 'IdSubstEnv' Note [Extending the Subst]- | Just v' <- lookupInScope inScope v = Var (coerce v')+ --+ -- TODO: We match on Id here to workaround an issue where type variables+ -- TODO: "shadow" term variables. Omitting the check would make 'lookupIdSubst'+ -- TODO: potentially replace an "Id" with a TyVar. For more information:+ -- TODO:+ -- TODO: https://github.com/clash-lang/clash-compiler/issues/1046+ -- TODO:+ | Just v'@(Id {}) <- lookupInScope inScope v = Var (coerce v') | otherwise = WARN(True, "Subst.lookupIdSubst" <+> doc <+> fromPpr v) Var v @@ -587,6 +622,32 @@ -> Term deShadowTerm is e = substTm "deShadowTerm" (mkSubst is) e +-- | Ensure that non of the binders in an alternative shadow each-other, nor+-- conflict with the in-scope set+deShadowAlt ::+ HasCallStack =>+ InScopeSet ->+ (Pat, Term) ->+ (Pat, Term)+deShadowAlt is = substAlt "deShadowAlt" (mkSubst is)++-- | Ensure that non of the let-bindings of a let-expression shadow w.r.t the+-- in-scope set+deshadowLetExpr+ :: HasCallStack+ => InScopeSet+ -- ^ Current InScopeSet+ -> [LetBinding]+ -- ^ Bindings of the let-expression+ -> Term+ -- ^ The body of the let-expression+ -> ([LetBinding],Term)+ -- ^ Deshadowed let-bindings, where let-bound expressions and the let-body+ -- properly reference the renamed variables+deshadowLetExpr is bs e =+ case substBind "deshadowLetBindings" (mkSubst is) bs of+ (s1,bs1) -> (bs1, substTm "deShadowLetBody" s1 e)+ -- | A much stronger variant of `deShadowTerm` that ensures that all bound -- variables are unique. --@@ -678,6 +739,12 @@ go env (AppTy s1 t1) (AppTy s2 t2) = go env s1 s2 `thenCompare` go env t1 t2 go _ (LitTy l1) (LitTy l2) = compare l1 l2+ go env (AnnType _ t1) (AnnType _ t2) =+ -- XXX: maybe ignore annotations, like we ignore ticks, i.e.+ --+ -- go env (AnnType t1) t2 = go env t1 t2+ -- go env t1 (AnnType t2) = go env t1 t2+ go env t1 t2 go _ t1 t2 = compare (getRank t1) (getRank t2) getRank :: Type -> Word@@ -732,7 +799,7 @@ go env (Var id1) (Var id2) = compare (rnOccLId env id1) (rnOccRId env id2) go _ (Data dc1) (Data dc2) = compare dc1 dc2 go _ (Literal l1) (Literal l2) = compare l1 l2- go _ (Prim p1 _) (Prim p2 _) = compare p1 p2+ go _ (Prim p1) (Prim p2) = comparing primName p1 p2 go env (Lam b1 e1) (Lam b2 e2) = acmpType' env (varType b1) (varType b2) `thenCompare` go (rnTmBndr env b1 b2) e1 e2
src/Clash/Core/Term.hs view
@@ -7,13 +7,11 @@ Term representation in the CoreHW language: System F + LetRec + Case -} -{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE TemplateHaskell #-} module Clash.Core.Term ( Term (..)@@ -24,8 +22,8 @@ , TickInfo (..), NameMod (..) , PrimInfo (..) , WorkInfo (..)- , CoreContext (..), Context, isLambdaBodyCtx, isTickCtx- , collectArgs, collectArgsTicks, collectTicks, primArg+ , CoreContext (..), Context, isLambdaBodyCtx, isTickCtx, walkTerm+ , collectArgs, collectArgsTicks, collectTicks, collectTermIds, primArg , partitionTicks ) where@@ -33,7 +31,9 @@ -- External Modules import Control.DeepSeq import Data.Binary (Binary)+import qualified Data.DList as DList import Data.Either (lefts, rights)+import Data.Maybe (catMaybes) import Data.Hashable (Hashable) import Data.List (partition) import Data.Text (Text)@@ -53,7 +53,7 @@ = Var !Id -- ^ Variable reference | Data !DataCon -- ^ Datatype constructor | Literal !Literal -- ^ Literal- | Prim !Text !PrimInfo -- ^ Primitive+ | Prim !PrimInfo -- ^ Primitive | Lam !Id Term -- ^ Term-abstraction | TyLam !TyVar Term -- ^ Type-abstraction | App !Term !Term -- ^ Application@@ -71,6 +71,11 @@ | NameMod !NameMod !Type -- ^ Modifier for naming module instantiations and registers, are added by -- the user by using the functions @Clash.Magic.[prefixName,suffixName,setName]@+ | DeDup+ -- ^ Deduplicate, i.e. try to share expressions between multiple branches.+ | NoDeDup+ -- ^ Do not deduplicate, i.e. /keep/, an expression inside a case-alternative;+ -- do not try to share expressions between multiple branches. deriving (Eq,Show,Generic,NFData,Hashable,Binary) -- | Tag to indicate which instance/register name modifier was used@@ -79,16 +84,17 @@ -- ^ @Clash.Magic.prefixName@ | SuffixName -- ^ @Clash.Magic.suffixName@+ | SuffixNameP+ -- ^ @Clash.Magic.suffixNameP@ | SetName -- ^ @Clash.Magic.setName@ deriving (Eq,Show,Generic,NFData,Hashable,Binary) -data PrimInfo- = PrimInfo- { primType :: !Type+data PrimInfo = PrimInfo+ { primName :: !Text+ , primType :: !Type , primWorkInfo :: !WorkInfo- }- deriving (Show,Generic,NFData,Hashable,Binary)+ } deriving (Show,Generic,NFData,Hashable,Binary) data WorkInfo = WorkConstant@@ -225,8 +231,8 @@ -- ^ If @Term@ was a primitive: (name of primitive, #type args, #term args) primArg (collectArgs -> t) = case t of- (Prim nm _, args) ->- Just (nm, length (rights args), length (lefts args))+ (Prim p, args) ->+ Just (primName p, length (rights args), length (lefts args)) _ -> Nothing @@ -236,3 +242,46 @@ -> ([TickInfo], [TickInfo]) -- ^ (source ticks, nameMod ticks) partitionTicks = partition (\case {SrcSpan {} -> True; _ -> False})++-- | Visit all terms in a term, testing it with a predicate, and returning+-- a list of predicate yields.+walkTerm :: forall a . (Term -> Maybe a) -> Term -> [a]+walkTerm f = catMaybes . DList.toList . go+ where+ go :: Term -> DList.DList (Maybe a)+ go t = DList.cons (f t) $ case t of+ Var _ -> mempty+ Data _ -> mempty+ Literal _ -> mempty+ Prim _ -> mempty+ Lam _ t1 -> go t1+ TyLam _ t1 -> go t1+ App t1 t2 -> go t1 <> go t2+ TyApp t1 _ -> pure (f t1)+ Letrec bndrs t1 -> pure (f t1) <> mconcat (map (go . snd) bndrs)+ Case t1 _ alts -> pure (f t1) <> mconcat (map (go . snd) alts)+ Cast t1 _ _ -> go t1+ Tick _ t1 -> pure (f t1)++-- Collect all term ids mentioned in a term+collectTermIds :: Term -> [Id]+collectTermIds = concat . walkTerm (Just . go)+ where+ go :: Term -> [Id]+ go (Var i) = [i]+ go (Lam i _) = [i]+ go (Letrec bndrs _) = map fst bndrs+ go (Case _ _ alts) = concatMap (pat . fst) alts+ go (Data _) = []+ go (Literal _) = []+ go (Prim _) = []+ go (TyLam _ _) = []+ go (App _ _) = []+ go (TyApp _ _) = []+ go (Cast _ _ _) = []+ go (Tick _ _) = []++ pat :: Pat -> [Id]+ pat (DataPat _ _ ids) = ids+ pat (LitPat _) = []+ pat DefaultPat = []
+ src/Clash/Core/TermLiteral.hs view
@@ -0,0 +1,96 @@+{-|+Copyright : (C) 2019, Myrtle Software Ltd+License : BSD2 (see the file LICENSE)+Maintainer : Christiaan Baaij <christiaan.baaij@gmail.com>++Tools to convert a 'Term' into its "real" representation+-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TemplateHaskell #-}++module Clash.Core.TermLiteral+ ( TermLiteral+ , termToData+ , termToDataError+ , uncheckedTermToData+ ) where++import qualified Data.Text as Text+import Data.Text (Text)+import Data.Bifunctor (bimap)+import Data.Either (lefts)+import GHC.Natural+import GHC.Stack++import Clash.Core.Term (Term(Literal), collectArgs)+import Clash.Core.Literal+import Clash.Core.Pretty (showPpr)++import Clash.Core.TermLiteral.TH++-- | Tools to deal with literals encoded as a "Term".+class TermLiteral a where+ -- | Convert 'Term' to the constant it represents. Will return an error if+ -- (one of the subterms) fail to translate.+ termToData+ :: HasCallStack+ => Term+ -- ^ Term to convert+ -> Either Term a+ -- ^ 'Left' indicates a failure, containing the (sub)term that failed to+ -- translate. 'Right' indicates a success.++instance TermLiteral Term where+ termToData = pure++instance TermLiteral String where+ termToData (collectArgs -> (_, [Left (Literal (StringLiteral s))])) = Right s+ termToData t = Left t++instance TermLiteral Text where+ termToData t = Text.pack <$> termToData t++instance TermLiteral Int where+ termToData (collectArgs -> (_, [Left (Literal (IntLiteral n))])) =+ Right (fromInteger n)+ termToData t = Left t++instance TermLiteral Integer where+ termToData (collectArgs -> (_, [Left (Literal (IntegerLiteral n))])) = Right n+ termToData t = Left t++instance TermLiteral Char where+ termToData (collectArgs -> (_, [Left (Literal (CharLiteral c))])) = Right c+ termToData t = Left t++instance TermLiteral Natural where+ termToData (collectArgs -> (_, [Left (Literal (NaturalLiteral n))])) =+ Right (fromInteger n)+ termToData t = Left t++instance (TermLiteral a, TermLiteral b) => TermLiteral (a, b) where+ termToData (collectArgs -> (_, lefts -> [a, b])) = do+ a' <- termToData a+ b' <- termToData b+ pure (a', b')+ termToData t = Left t++instance TermLiteral a => TermLiteral (Maybe a) where+ termToData = $(deriveTermToData ''Maybe)++instance TermLiteral Bool where+ termToData = $(deriveTermToData ''Bool)++-- | Same as 'termToData', but returns printable error message if it couldn't+-- translate a term.+termToDataError :: TermLiteral a => Term -> Either String a+termToDataError term = bimap err id (termToData term)+ where+ err failedTerm =+ "Failed to translate term to literal. Term that failed to translate:\n\n"+ ++ showPpr failedTerm ++ "\n\nIn the full term:\n\n" ++ showPpr term++-- | Same as 'termToData', but errors hard if it can't translate a given term+-- to data.+uncheckedTermToData :: TermLiteral a => Term -> a+uncheckedTermToData = either error id . termToDataError
+ src/Clash/Core/TermLiteral/TH.hs view
@@ -0,0 +1,93 @@+{-# LANGUAGE TemplateHaskellQuotes #-}++module Clash.Core.TermLiteral.TH+ ( deriveTermToData+ ) where++import Data.Either+import qualified Data.Text as Text+import Language.Haskell.TH.Syntax++import Clash.Core.DataCon+import Clash.Core.Term (collectArgs, Term(Data))+import Clash.Core.Name (nameOcc)++-- Workaround for a strange GHC bug, where it complains about Subst only+-- existing as a boot file:+--+-- module Clash.Core.Subst cannot be linked; it is only available as a boot module+import Clash.Core.Subst ()++dcName' :: DataCon -> String+dcName' = Text.unpack . nameOcc . dcName++termToDataName :: Name+termToDataName = mkName "Clash.Core.TermLiteral.termToData"++deriveTermToData :: Name -> Q Exp+deriveTermToData typName = do+ TyConI (DataD _ _ _ _ constrs _) <- reify typName+ pure (deriveTermToData1 (map toConstr' constrs))+ where+ toConstr' (NormalC cName fields) = (cName, length fields)+ toConstr' c = error $ "Unexpected constructor: " ++ show c++deriveTermToData1 :: [(Name, Int)] -> Exp+deriveTermToData1 constrs =+ LamE+ [pat]+ (if null args then theCase else LetE args theCase)+ where+ nArgs = maximum (map snd constrs)++ args :: [Dec]+ args = zipWith (\n nm -> ValD (VarP nm) (NormalB (arg n)) []) [0..] argNames+ arg n = UInfixE (VarE argsName) (VarE '(!!)) (LitE (IntegerL n))++ -- case nm of {"ConstrOne" -> ConstOne <$> termToData arg0; "ConstrTwo" -> ...}+ theCase :: Exp+ theCase =+ CaseE+ (VarE nameName)+ (map match constrs ++ [emptyMatch])++ emptyMatch = Match WildP (NormalB (ConE 'Left `AppE` VarE termName)) []++ match :: (Name, Int) -> Match+ match (cName, nFields) =+ Match (LitP (StringL (show cName))) (NormalB (mkCall cName nFields)) []++ mkCall :: Name -> Int -> Exp+ mkCall cName 0 = ConE 'Right `AppE` ConE cName+ mkCall cName 1 =+ UInfixE+ (ConE cName)+ (VarE '(<$>))+ (VarE termToDataName `AppE` VarE (head argNames))+ mkCall cName nFields =+ foldl+ (\e aName ->+ UInfixE+ e+ (VarE '(<*>))+ (VarE termToDataName `AppE` VarE aName))+ (mkCall cName 1)+ (take (nFields-1) (tail argNames))++ -- term@(collectArgs -> (Data (dcName' -> nm), args))+ pat :: Pat+ pat =+ AsP+ termName+ (ViewP+ (VarE 'collectArgs)+ (TupP [ ConP 'Data [ViewP (VarE 'dcName') (VarP nameName)]+ , ViewP+ (VarE 'lefts)+ (if nArgs == 0 then WildP else VarP argsName)]))++ termName = mkName "term"+ argsName = mkName "args"+ argNames = [mkName ("arg" ++ show n) | n <- [0..nArgs-1]]+ nameName = mkName "nm"+
src/Clash/Core/TyCon.hs view
@@ -6,10 +6,9 @@ Type Constructors in CoreHW -} -{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} module Clash.Core.TyCon
src/Clash/Core/Type.hs view
@@ -8,16 +8,14 @@ Types in CoreHW -} -{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MagicHash #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MagicHash #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-} module Clash.Core.Type ( Type (..)@@ -35,6 +33,7 @@ , typeKind , mkTyConTy , mkFunTy+ , mkPolyFunTy , mkTyConApp , splitFunTy , splitFunTys@@ -82,7 +81,8 @@ import PrelNames (integerTyConKey, typeNatAddTyFamNameKey, typeNatExpTyFamNameKey, typeNatLeqTyFamNameKey, typeNatMulTyFamNameKey, typeNatSubTyFamNameKey,- typeNatCmpTyFamNameKey)+ typeNatCmpTyFamNameKey,+ typeSymbolAppendFamNameKey, typeSymbolCmpTyFamNameKey) import SrcLoc (wiredInSrcSpan) import Unique (getKey) @@ -165,13 +165,48 @@ -- representation of newtypes. So what are those additional 2 arguments compared to -- the "normal" function type? They're the kinds of argument and result type. tyView :: Type -> TypeView-tyView ty@(AppTy _ _) = case splitTyAppM ty of- Just (ConstTy Arrow, [ty1,ty2]) -> FunTy ty1 ty2- Just (ConstTy Arrow, [_,_,ty1,ty2]) -> FunTy ty1 ty2 -- See Note [Arrow arguments]- Just (ConstTy (TyCon tc), args) -> TyConApp tc args- _ -> OtherType ty-tyView (ConstTy (TyCon tc)) = TyConApp tc []-tyView t = OtherType t+-- XXX: this is a manually unrolled version of:+--+-- tyView tOrig = go [] tOrig+-- where+-- go args t = case t of+-- ConstTy c -> case c of+-- TyCon tc -> TyConApp tc args+-- Arrow -> case args of+-- (arg:res:rest) -> case rest of+-- [] -> FunTy arg res+-- [arg1,res1] -> FunTy arg1 res1+-- _ -> OtherType tOrig+-- AppTy l r -> go (r:args) l+-- _ -> OtherType tOrig+--+-- To get a FunTy without recursive calls. Because it is called so often this+-- saves us 5-10% runtime.+tyView tOrig = case tOrig of+ ConstTy c -> case c of+ TyCon tc -> TyConApp tc []+ _ -> OtherType tOrig+ AppTy l0 res -> case l0 of+ ConstTy (TyCon tc) -> TyConApp tc [res]+ AppTy l1 arg -> case l1 of+ ConstTy Arrow -> FunTy arg res+ ConstTy (TyCon tc) -> TyConApp tc [arg,res]+ AppTy l2 resK -> case l2 of+ ConstTy (TyCon tc) -> TyConApp tc [resK,arg,res]+ AppTy l3 argK -> case l3 of+ ConstTy (TyCon tc) -> TyConApp tc [argK,resK,arg,res]+ ConstTy Arrow -> FunTy arg res -- See Note [Arrow arguments]+ _ -> case go [argK,resK,arg,res] l3 of+ (ConstTy (TyCon tc),args)+ -> TyConApp tc args+ _ -> OtherType tOrig+ _ -> OtherType tOrig+ _ -> OtherType tOrig+ _ -> OtherType tOrig+ _ -> OtherType tOrig+ where+ go args (AppTy ty1 ty2) = go (ty2:args) ty1+ go args t1 = (t1,args) -- | A view on types in which newtypes are transparent, the Signal type is -- transparent, and type functions are evaluated to WHNF (when possible).@@ -302,6 +337,16 @@ go args (tyView -> FunTy arg res) = go (Right arg:args) res go args ty = (reverse args,ty) +-- | Make a polymorphic function type out of a result type and a list of+-- quantifiers and function argument types+mkPolyFunTy+ :: Type+ -- ^ Result type+ -> [Either TyVar Type]+ -- ^ List of quantifiers and function argument types+ -> Type+mkPolyFunTy = foldr (either ForAllTy mkFunTy)+ -- | Split a poly-function type in a: list of type-binders and argument types, -- and the result type. Looks through 'Signal' and type functions. splitCoreFunForallTy :: TyConMap@@ -352,17 +397,6 @@ applyFunTy _ (tyView -> FunTy _ resTy) _ = resTy applyFunTy _ _ _ = error $ $(curLoc) ++ "Report as bug: not a FunTy" --- | Split a type application in the applied type and the argument types-splitTyAppM :: Type- -> Maybe (Type, [Type])-splitTyAppM = fmap (second reverse) . go []- where- go args (AppTy ty1 ty2) =- case go args ty1 of- Nothing -> Just (ty1,ty2:args)- Just (ty1',ty1args) -> Just (ty1',ty2:ty1args )- go _ _ = Nothing- -- Type function substitutions -- Given a set of type functions, and list of argument types, get the first@@ -490,6 +524,18 @@ EQ -> Name User "GHC.Types.EQ" (getKey ordEQDataConKey) wiredInSrcSpan GT -> Name User "GHC.Types.GT" (getKey ordGTDataConKey) wiredInSrcSpan + | nameUniq tc == getKey typeSymbolCmpTyFamNameKey -- "GHC.TypeNats.CmpSymbol"+ , [s1, s2] <- mapMaybe (symLitView tcm) tys+ = Just $ ConstTy $ TyCon $+ case compare s1 s2 of+ LT -> Name User "GHC.Types.LT" (getKey ordLTDataConKey) wiredInSrcSpan+ EQ -> Name User "GHC.Types.EQ" (getKey ordEQDataConKey) wiredInSrcSpan+ GT -> Name User "GHC.Types.GT" (getKey ordGTDataConKey) wiredInSrcSpan++ | nameUniq tc == getKey typeSymbolAppendFamNameKey -- GHC.TypeLits.AppendSymbol"+ , [s1, s2] <- mapMaybe (symLitView tcm) tys+ = Just (LitTy (SymTy (s1 ++ s2)))+ | nameOcc tc `elem` ["GHC.TypeLits.Extra.FLog", "GHC.TypeNats.FLog"] , [i1, i2] <- mapMaybe (litView tcm) tys , i1 > 1@@ -547,6 +593,11 @@ litView _ (LitTy (NumTy i)) = Just i litView m (reduceTypeFamily m -> Just ty') = litView m ty' litView _ _ = Nothing++symLitView :: TyConMap -> Type -> Maybe String+symLitView _ (LitTy (SymTy s)) = Just s+symLitView m (reduceTypeFamily m -> Just ty') = symLitView m ty'+symLitView _ _ = Nothing -- | The type @forall a . a@ undefinedTy ::Type
src/Clash/Core/TysPrim.hs view
@@ -7,7 +7,7 @@ Builtin Type and Kind definitions -} -{-# LANGUAGE CPP #-}+{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} module Clash.Core.TysPrim
src/Clash/Core/Util.hs view
@@ -6,11 +6,11 @@ Smart constructor and destructor functions for CoreHW -} -{-# LANGUAGE CPP #-}-{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-} module Clash.Core.Util where @@ -18,28 +18,35 @@ import qualified Control.Lens as Lens import Control.Monad.Trans.Except (Except, throwE) import Data.Coerce (coerce)-import qualified Data.IntSet as IntSet import qualified Data.HashSet as HashSet+import qualified Data.Graph as Graph import Data.List (foldl', mapAccumR, elemIndices, nub)-import Data.Maybe (fromJust, mapMaybe, catMaybes)+import Data.Maybe+ (fromJust, isJust, mapMaybe, catMaybes)+import qualified Data.Set as Set+import qualified Data.Set.Lens as Lens+import qualified Data.String.Interpolate as I import qualified Data.Text as T import Data.Text.Prettyprint.Doc (line) #if !MIN_VERSION_base(4,11,0) import Data.Semigroup #endif +import PrelNames (ipClassKey)+import Unique (getKey)+ import Clash.Core.DataCon (DataCon (MkData), dcType, dcUnivTyVars, dcExtTyVars, dcArgTys) import Clash.Core.FreeVars- (termFreeVarsX, tyFVsOfTypes, typeFreeVars)+ (tyFVsOfTypes, typeFreeVars, freeLocalIds) import Clash.Core.Literal (literalType) import Clash.Core.Name (Name (..), OccName, mkUnsafeInternalName, mkUnsafeSystemName) import Clash.Core.Pretty (ppr, showPpr) import Clash.Core.Subst (extendTvSubst, mkSubst, mkTvSubst, substTy, substTyWith,- substTyInVar, extendTvSubstList)+ substTyInVar, extendTvSubstList, aeqType) import Clash.Core.Term (LetBinding, Pat (..), PrimInfo (..), Term (..), Alt, WorkInfo (..), TickInfo (..), collectArgs)@@ -277,7 +284,7 @@ Var t -> varType t Data dc -> dcType dc Literal l -> literalType l- Prim _ t -> primType t+ Prim t -> primType t Lam v e' -> mkFunTy (varType v) (termType m e') TyLam tv e' -> ForAllTy tv (termType m e') App _ _ -> case collectArgs e of@@ -326,7 +333,8 @@ -- Do not iterate 'piResultTy', because it's inefficient to substitute one -- variable at a time; instead use 'piResultTys' piResultTy- :: TyConMap+ :: HasCallStack+ => TyConMap -> Type -> Type -> Type@@ -339,15 +347,26 @@ -- Do not iterate 'piResultTyMaybe', because it's inefficient to substitute one -- variable at a time; instead use 'piResultTys' piResultTyMaybe- :: TyConMap+ :: HasCallStack+ => TyConMap -> Type -> Type -> Maybe Type piResultTyMaybe m ty arg | Just ty' <- coreView1 m ty = piResultTyMaybe m ty' arg- | FunTy _ res <- tyView ty- = Just res+ | FunTy a res <- tyView ty+ = if debugIsOn && not (aeqType a arg) then error [I.i|+ Unexpected application. A function with type:++ #{showPpr ty}++ Got applied to an argument of type:++ #{showPpr arg}+ |]+ else+ Just res | ForAllTy tv res <- ty = let emptySubst = mkSubst (mkInScopeSet (tyFVsOfTypes [arg,res])) in Just (substTy (extendTvSubst emptySubst tv arg) res)@@ -378,7 +397,8 @@ -- For efficiency reasons, when there are no foralls, we simply drop arrows from -- a function type/kind. piResultTys- :: TyConMap+ :: HasCallStack+ => TyConMap -> Type -> [Type] -> Type@@ -386,8 +406,18 @@ piResultTys m ty origArgs@(arg:args) | Just ty' <- coreView1 m ty = piResultTys m ty' origArgs- | FunTy _ res <- tyView ty- = piResultTys m res args+ | FunTy a res <- tyView ty+ = if debugIsOn && not (aeqType a arg) then error [I.i|+ Unexpected application. A function with type:++ #{showPpr ty}++ Got applied to an argument of type:++ #{showPpr arg}+ |]+ else+ piResultTys m res args | ForAllTy tv res <- ty = go (extendVarEnv tv arg emptyVarEnv) res args | otherwise@@ -592,15 +622,6 @@ ,resTy ,(LitTy (NumTy (n-1)))]) --availableUniques- :: Term- -> [Unique]-availableUniques t = [ n | n <- [0..] , n `IntSet.notMember` avoid ]- where- avoid = Lens.foldMapOf termFreeVarsX (\a i -> IntSet.insert (varUniq a) i) t- IntSet.empty- -- | Create let-bindings with case-statements that select elements out of a -- vector. Returns both the variables to which element-selections are bound -- and the let-bindings@@ -793,6 +814,17 @@ go _ _ = False +-- | Determines whether given type is an (alias of en) Enable line.+isEnable+ :: TyConMap+ -> Type+ -> Bool+isEnable m ty0+ | TyConApp (nameOcc -> "Clash.Signal.Internal.Enable") _ <- tyView ty0 = True+ | Just ty1 <- coreView1 m ty0 = isEnable m ty1+isEnable _ _ = False++-- | Determines whether given type is an (alias of en) Clock or Reset line isClockOrReset :: TyConMap -> Type@@ -907,13 +939,13 @@ primCo :: Type -> Term-primCo ty = Prim "_CO_" (PrimInfo ty WorkNever)+primCo ty = Prim (PrimInfo "_CO_" ty WorkNever) -- | Make an undefined term undefinedTm :: Type -> Term-undefinedTm = TyApp (Prim "Clash.Transformations.undefined" (PrimInfo undefinedTy WorkNever))+undefinedTm = TyApp (Prim (PrimInfo "Clash.Transformations.undefined" undefinedTy WorkNever)) substArgTys :: DataCon@@ -933,12 +965,145 @@ stripTicks (Tick _ e) = stripTicks e stripTicks e = e --- | Try to reduce an arbitrary type to a Symbol, and subsequently extract its--- String representation-tySym+-- | Try to reduce an arbitrary type to a literal type (Symbol or Nat),+-- and subsequently extract its String representation+tyLitShow :: TyConMap -> Type -> Except String String-tySym m (coreView1 m -> Just ty) = tySym m ty-tySym _ (LitTy (SymTy s)) = return s-tySym _ ty = throwE $ $(curLoc) ++ "Cannot reduce to a string:\n" ++ showPpr ty+tyLitShow m (coreView1 m -> Just ty) = tyLitShow m ty+tyLitShow _ (LitTy (SymTy s)) = return s+tyLitShow _ (LitTy (NumTy s)) = return (show s)+tyLitShow _ ty = throwE $ $(curLoc) ++ "Cannot reduce to a string:\n" ++ showPpr ty++-- | Determine whether we should split away types from a product type, i.e.+-- clocks should always be separate arguments, and not part of a product.+shouldSplit+ :: TyConMap+ -> Type+ -- ^ Type to examine+ -> Maybe (Term,[Type])+ -- ^ If we want to split values of the given type then we have /Just/:+ --+ -- 1. The (type-applied) data-constructor which, when applied to values of+ -- the types in 2., creates a value of the examined type+ --+ -- 2. The arguments types of the product we are trying to split.+ --+ -- Note that we only split one level at a time (although we check all the way+ -- down), e.g. given /(Int, (Clock, Bool))/ we return:+ --+ -- > Just ((,) @Int @(Clock, Bool), [Int, (Clock, Bool)])+ --+ -- An outer loop is required to subsequently split the /(Clock, Bool)/ tuple.+shouldSplit tcm (tyView -> TyConApp (nameOcc -> "Clash.Explicit.SimIO.SimIO") [tyArg]) =+ -- We also look through `SimIO` to find things like Files+ shouldSplit tcm tyArg+shouldSplit tcm ty = shouldSplit0 tcm (tyView (coreView tcm ty))++-- | Worker of 'shouldSplit', works on 'TypeView' instead of 'Type'+shouldSplit0+ :: TyConMap+ -> TypeView+ -> Maybe (Term,[Type])+shouldSplit0 tcm (TyConApp tcNm tyArgs)+ | Just tc <- lookupUniqMap tcNm tcm+ , [dc] <- tyConDataCons tc+ , let dcArgs = substArgTys dc tyArgs+ , let dcArgVs = map (tyView . coreView tcm) dcArgs+ = if any shouldSplitTy dcArgVs && not (isHidden tcNm tyArgs) then+ Just (mkApps (Data dc) (map Right tyArgs), dcArgs)+ else+ Nothing+ where+ shouldSplitTy :: TypeView -> Bool+ shouldSplitTy ty = isJust (shouldSplit0 tcm ty) || splitTy ty++ -- Hidden constructs (HiddenClock, HiddenReset, ..) don't need to be split+ -- because KnownDomain will be filtered anyway during netlist generation due+ -- to it being a zero-width type+ --+ -- TODO: This currently only handles (IP $x, KnownDomain) given that $x is any+ -- TODO: of the constructs handled in 'splitTy'. In practise this means only+ -- TODO: HiddenClock, HiddenReset, and HiddenEnable are handled. If a user were+ -- TODO: to define their own versions with -for example- the elements of the+ -- TODO: tuple swapped, 'isHidden' wouldn't recognize it. We could generalize+ -- TODO: this in the future.+ --+ isHidden :: Name a -> [Type] -> Bool+ isHidden nm [a1, a2] | TyConApp a2Nm _ <- tyView a2 =+ nameOcc nm == "GHC.Classes.(%,%)"+ && splitTy (tyView (stripIP a1))+ && nameOcc a2Nm == "Clash.Signal.Internal.KnownDomain"+ isHidden _ _ = False++ -- Currently we're only interested in splitting of Clock, Reset, and Enable+ splitTy (TyConApp tcNm0 _)+ = nameOcc tcNm0 `elem` [ "Clash.Signal.Internal.Clock"+ , "Clash.Signal.Internal.Reset"+ , "Clash.Signal.Internal.Enable"+ -- iverilog doesn't like it when we put file handles+ -- in a bitvector, so we need to make sure Clash+ -- splits them off+ , "Clash.Explicit.SimIO.File"+ , "GHC.IO.Handle.Types.Handle"+ ]+ splitTy _ = False++shouldSplit0 _ _ = Nothing++-- | Potentially split apart a list of function argument types. e.g. given:+--+-- > [Int,(Clock,(Reset,Bool)),Char]+--+-- we return+--+-- > [Int,Clock,Reset,Bool,Char]+--+-- But we would leave+--+-- > [Int, (Bool,Int), Char]+--+-- unchanged.+splitShouldSplit+ :: TyConMap+ -> [Type]+ -> [Type]+splitShouldSplit tcm = foldr go []+ where+ go ty rest = case shouldSplit tcm ty of+ Just (_,tys) -> splitShouldSplit tcm tys ++ rest+ Nothing -> ty : rest++-- | Strip implicit parameter wrappers (IP)+stripIP :: Type -> Type+stripIP t@(tyView -> TyConApp tcNm [_a1, a2]) =+ if nameUniq tcNm == getKey ipClassKey then a2 else t+stripIP t = t++-- | Do an inverse topological sorting of the let-bindings in a let-expression+inverseTopSortLetBindings+ :: HasCallStack+ => Term+ -> Term+inverseTopSortLetBindings (Letrec bndrs0 res) =+ let (graph,nodeMap,_) =+ Graph.graphFromEdges+ (map (\(i,e) -> let fvs = fmap varUniq+ (Set.elems (Lens.setOf freeLocalIds e) )+ in ((i,e),varUniq i,fvs)) bndrs0)+ nodes = postOrd graph+ bndrs1 = map ((\(x,_,_) -> x) . nodeMap) nodes+ in Letrec bndrs1 res+ where+ postOrd :: Graph.Graph -> [Graph.Vertex]+ postOrd g = postorderF (Graph.dff g) []++ postorderF :: Graph.Forest a -> [a] -> [a]+ postorderF ts = foldr (.) id (map postorder ts)++ postorder :: Graph.Tree a -> [a] -> [a]+ postorder (Graph.Node a ts) = postorderF ts . (a :)++inverseTopSortLetBindings e = e+{-# SCC inverseTopSortLetBindings #-}
src/Clash/Core/Var.hs view
@@ -7,12 +7,10 @@ Variables in CoreHW -} -{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE ExplicitForAll #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RankNTypes #-} module Clash.Core.Var ( Attr' (..)
src/Clash/Core/VarEnv.hs view
@@ -1,9 +1,8 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeSynonymInstances #-} module Clash.Core.VarEnv ( -- * Environment with variables as keys@@ -14,6 +13,7 @@ -- ** Indexing , lookupVarEnv , lookupVarEnv'+ , lookupVarEnvDirectly -- ** Construction , emptyVarEnv , unitVarEnv@@ -30,6 +30,8 @@ -- *** Mapping , mapVarEnv , mapMaybeVarEnv+ -- ** Folding+ , foldlWithUniqueVarEnv' -- ** Working with predicates -- *** Searching , elemVarEnv@@ -52,6 +54,7 @@ -- ** Conversions -- *** Lists , mkVarSet+ , eltsVarSet -- * In-scope sets , InScopeSet -- ** Accessors@@ -127,6 +130,13 @@ -> Maybe a lookupVarEnv = lookupUniqMap +-- | Lookup a value based on the unique of a variable+lookupVarEnvDirectly+ :: Unique+ -> VarEnv a+ -> Maybe a+lookupVarEnvDirectly = lookupUniqMap+ -- | Lookup a value based on the variable -- -- Errors out when the variable is not present@@ -220,6 +230,15 @@ -> VarEnv b mapMaybeVarEnv = mapMaybeUniqMap +-- | Strict left-fold over an environment using both the unique of the+-- the variable and the value+foldlWithUniqueVarEnv'+ :: (a -> Unique -> b -> a)+ -> a+ -> VarEnv b+ -> a+foldlWithUniqueVarEnv' = foldlWithUnique'+ -- | Extract the elements eltsVarEnv :: VarEnv a@@ -312,6 +331,11 @@ :: [Var a] -> VarSet mkVarSet xs = mkUniqSet (coerce xs)++eltsVarSet+ :: VarSet+ -> [Var Any]+eltsVarSet = eltsUniqSet -- * InScopeSet
src/Clash/Driver.hs view
@@ -8,16 +8,18 @@ Module that connects all the parts of the Clash compiler library -} +{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NondecreasingIndentation #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TupleSections #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskellQuotes #-} module Clash.Driver where import qualified Control.Concurrent.Supply as Supply import Control.DeepSeq-import Control.Exception (tryJust, bracket)-import Control.Lens (view, (^.), _4)+import Control.Exception (tryJust, bracket, throw)+import Control.Lens (view, _4)+import qualified Control.Monad as Monad import Control.Monad (guard, when, unless, foldM) import Control.Monad.Catch (MonadMask) import Control.Monad.IO.Class (MonadIO)@@ -31,6 +33,7 @@ import Data.List (intercalate) import Data.Maybe (fromMaybe) import Data.Semigroup.Monad+import qualified Data.Set as Set import qualified Data.Text import Data.Text.Lazy (Text) import qualified Data.Text.Lazy as Text@@ -41,6 +44,7 @@ renderOneLine) import qualified Data.Time.Clock as Clock import qualified Language.Haskell.Interpreter as Hint+import qualified Language.Haskell.Interpreter.Extension as Hint import qualified Language.Haskell.Interpreter.Unsafe as Hint import qualified System.Directory as Directory import System.Environment (getExecutablePath)@@ -53,38 +57,150 @@ import Text.Trifecta.Result (Result(Success, Failure), _errDoc) import Text.Read (readMaybe)++import PrelNames (eqTyConKey, ipClassKey)+import Unique (getKey)+ import SrcLoc (SrcSpan)+import Util (OverridingBool(Auto)) import GHC.BasicTypes.Extra () import Clash.Annotations.Primitive (HDL (..)) import Clash.Annotations.BitRepresentation.Internal (CustomReprs)-import Clash.Annotations.TopEntity (TopEntity (..))+import Clash.Annotations.TopEntity+ (TopEntity (..), PortName(PortName, PortProduct)) import Clash.Annotations.TopEntity.Extra () import Clash.Backend-import Clash.Core.Evaluator (PrimEvaluator)+import Clash.Core.Evaluator.Types (PrimStep, PrimUnwind) import Clash.Core.Name (Name (..))+import Clash.Core.Pretty (PrettyOptions(..), showPpr') import Clash.Core.Term (Term)-import Clash.Core.Type (Type)+import Clash.Core.Type+ (Type(ForAllTy, LitTy, AnnType), TypeView(..), tyView, mkFunTy, LitTy(SymTy)) import Clash.Core.TyCon (TyConMap, TyConName)-import Clash.Core.Var (Id, varName)-import Clash.Core.VarEnv (emptyVarEnv)+import Clash.Core.Util (shouldSplit)+import Clash.Core.Var+ (Id, varName, varUniq, varType)+import Clash.Core.VarEnv+ (elemVarEnv, emptyVarEnv, lookupVarEnv) import Clash.Driver.Types import Clash.Netlist (genNetlist) import Clash.Netlist.Util (genComponentName, genTopComponentName) import Clash.Netlist.BlackBox.Parser (runParse) import Clash.Netlist.BlackBox.Types (BlackBoxTemplate, BlackBoxFunction) import Clash.Netlist.Types- (BlackBox (..), Component (..), Identifier, FilteredHWType, HWMap)+ (BlackBox (..), Component (..), Identifier, FilteredHWType, HWMap,+ SomeBackend (..), TopEntityT(..), TemplateFunction, ComponentPrefix(..)) import Clash.Normalize (checkNonRecursive, cleanupGraph, normalize, runNormalization)-import Clash.Normalize.Util (callGraph)+import Clash.Normalize.Util (callGraph, tvSubstWithTyEq)+import qualified Clash.Primitives.Sized.ToInteger as P+import qualified Clash.Primitives.Sized.Vector as P+import qualified Clash.Primitives.GHC.Int as P+import qualified Clash.Primitives.GHC.Word as P+import qualified Clash.Primitives.Intel.ClockGen as P import Clash.Primitives.Types import Clash.Primitives.Util (hashCompiledPrimMap) import Clash.Unique (keysUniqMap, lookupUniqMap')-import Clash.Util (first, reportTimeDiff)+import Clash.Util.Interpolate (i)+import Clash.Util+ (ClashException(..), HasCallStack, first, reportTimeDiff,+ wantedLanguageExtensions, unwantedLanguageExtensions, debugIsOn)+import Clash.Util.Graph (reverseTopSort) +-- | Worker function of 'splitTopEntityT'+splitTopAnn+ :: TyConMap+ -> SrcSpan+ -- ^ Source location of top entity (for error reporting)+ -> Type+ -- ^ Top entity body+ -> TopEntity+ -- ^ Port annotations for top entity+ -> TopEntity+ -- ^ New top entity with split ports (or the old one if not applicable)+splitTopAnn tcm sp typ@(tyView -> FunTy {}) t@Synthesize{t_inputs} =+ t{t_inputs=go typ t_inputs}+ where+ go :: Type -> [PortName] -> [PortName]+ go _ [] = []+ go (tyView -> FunTy a res) (p:ps)+ | shouldNotHavePortName a+ -- Insert dummy PortName for args for which the user shouldn't have+ -- to provide a name.+ -- Ideally this would be any (non Hidden{Clock,Reset,Enable}) constraint.+ -- But because we can't properly detect constraints,+ -- we only skip some specific one. see "shouldNotHavePortName"+ = PortName "" : go res (p:ps)+ | otherwise =+ case shouldSplit tcm a of+ Just (_,argTys@(_:_:_)) ->+ -- Port must be split up into 'n' pieces.. can it?+ case p of+ PortProduct nm portNames0 ->+ let+ n = length argTys+ newPortNames = map (PortName . show) [(0::Int)..]+ portNames1 = map (prependName nm) (portNames0 ++ newPortNames)+ newLam = foldr1 mkFunTy (argTys ++ [res])+ in+ go newLam (take n portNames1 ++ ps)+ PortName nm ->+ throw (flip (ClashException sp) Nothing $ [i|+ Couldn't separate clock, reset, or enable from a product type due+ to a malformed Synthesize annotation. All clocks, resets, and+ enables should be given a unique port name. Type to be split:++ #{showPpr' (PrettyOptions False True False) a}++ Given port annotation: #{p}. You might want to use the+ following instead: PortProduct #{show nm} []. This allows Clash to+ autogenerate names based on the name #{show nm}.+ |])+ _ ->+ -- No need to split the port, carrying on..+ p : go res ps+ go (ForAllTy _tyVar ty) ps = go ty ps+ go _ty ps = ps++ prependName :: String -> PortName -> PortName+ prependName "" pn = pn+ prependName p (PortProduct nm ps) = PortProduct (p ++ "_" ++ nm) ps+ prependName p (PortName nm) = PortName (p ++ "_" ++ nm)++ -- Returns True for+ -- * type equality constraints (~)+ -- * HasCallStack+ shouldNotHavePortName :: Type -> Bool+ shouldNotHavePortName (tyView -> TyConApp (nameUniq -> tcUniq) tcArgs)+ | tcUniq == getKey eqTyConKey = True+ | tcUniq == getKey ipClassKey+ , [LitTy (SymTy "callStack"), _] <- tcArgs = True+ shouldNotHavePortName _ = False++splitTopAnn tcm sp (ForAllTy _tyVar typ) t = splitTopAnn tcm sp typ t+splitTopAnn tcm sp (AnnType _anns typ) t = splitTopAnn tcm sp typ t+splitTopAnn _tcm _sp _typ t = t++-- When splitting up a single argument into multiple arguments (see docs of+-- 'separateArguments') we should make sure to update TopEntity annotations+-- accordingly. See: https://github.com/clash-lang/clash-compiler/issues/1033+splitTopEntityT+ :: HasCallStack+ => TyConMap+ -> BindingMap+ -> TopEntityT+ -> TopEntityT+splitTopEntityT tcm bindingsMap tt@(TopEntityT id_ (Just t@(Synthesize {})) _) =+ case lookupVarEnv id_ bindingsMap of+ Just (Binding _id sp _ _) ->+ tt{topAnnotation=Just (splitTopAnn tcm sp (varType id_) t)}+ Nothing ->+ error "Internal error in 'splitTopEntityT'. Please report as a bug."+splitTopEntityT _ _ t = t+ -- | Get modification data of current clash binary. getClashModificationDate :: IO Clock.UTCTime getClashModificationDate = Directory.getModificationTime =<< getExecutablePath@@ -105,27 +221,27 @@ -> (CustomReprs -> TyConMap -> Type -> State HWMap (Maybe (Either String FilteredHWType))) -- ^ Hardcoded 'Type' -> 'HWType' translator- -> PrimEvaluator+ -> (PrimStep, PrimUnwind) -- ^ Hardcoded evaluator (delta-reduction)- -> [( Id- , Maybe TopEntity- , Maybe Id- )]- -- ^ topEntity bndr- -- + (maybe) TopEntity annotation- -- + (maybe) testBench bndr+ -> [TopEntityT]+ -- ^ Topentities and associated testbench -> ClashOpts -- ^ Debug information level for the normalization process -> (Clock.UTCTime,Clock.UTCTime) -> IO () generateHDL reprs bindingsMap hdlState primMap tcm tupTcm typeTrans eval- topEntities opts (startTime,prepTime) = go prepTime HashMap.empty topEntities where+ topEntities0 opts (startTime,prepTime) =+ go prepTime HashMap.empty (sortTop bindingsMap topEntities2)+ where+ topEntities1 = map (splitTopEntityT tcm bindingsMap) topEntities0+ -- Remove forall's used in type equality constraints+ topEntities2 = map (\(TopEntityT var annM tbM) -> TopEntityT var{varType=tvSubstWithTyEq (varType var)} annM tbM) topEntities1 go prevTime _ [] = putStrLn $ "Clash: Total compilation took " ++ reportTimeDiff prevTime startTime -- Process the next TopEntity- go prevTime seen ((topEntity,annM,benchM):topEntities') = do+ go prevTime seen (TopEntityT topEntity annM benchM:topEntities') = do let topEntityS = Data.Text.unpack (nameOcc (varName topEntity)) putStrLn $ "Clash: Compiling " ++ topEntityS @@ -137,19 +253,19 @@ -- Prefix top names with 'p', prefix other with 'p_tname' Just ann -> let nm = p ++ ('_':t_name ann)- in (nm,(Just (Data.Text.pack p),Just (Data.Text.pack nm)))+ in (nm,ComponentPrefix (Just (Data.Text.pack p)) (Just (Data.Text.pack nm))) -- Prefix top names with 'p', prefix other with 'p'- _ -> (p ++ '_':modName1,(Just (Data.Text.pack p),Just (Data.Text.pack p)))+ _ -> (p ++ '_':modName1,ComponentPrefix (Just (Data.Text.pack p)) (Just (Data.Text.pack p))) | Just ann <- annM -> case hdlKind (undefined :: backend) of -- Prefix other with 't_name'- VHDL -> (t_name ann,(Nothing,Just (Data.Text.pack (t_name ann))))- _ -> (t_name ann,(Nothing,Nothing))+ VHDL -> (t_name ann,ComponentPrefix Nothing (Just (Data.Text.pack (t_name ann))))+ _ -> (t_name ann,ComponentPrefix Nothing Nothing) _ -> case annM of Just ann -> case hdlKind (undefined :: backend) of- VHDL -> (t_name ann, (Nothing,Nothing))+ VHDL -> (t_name ann, ComponentPrefix Nothing Nothing) -- Prefix other with 't_name'- _ -> (t_name ann, (Nothing,Just (Data.Text.pack (t_name ann))))- _ -> (modName1, (Nothing,Nothing))+ _ -> (t_name ann, ComponentPrefix Nothing (Just (Data.Text.pack (t_name ann))))+ _ -> (modName1, ComponentPrefix Nothing Nothing) iw = opt_intWidth opts hdlsyn = opt_hdlSyn opts escpIds = opt_escapedIds opts@@ -169,17 +285,51 @@ unless (opt_cachehdl opts) $ putStrLn "Clash: Ignoring .manifest files" -- Calculate the hash over the callgraph and the topEntity annotation- (sameTopHash,sameBenchHash,manifest) <- do+ (useCacheTop,useCacheBench,manifest) <- do clashModDate <- getClashModificationDate let primMapHash = hashCompiledPrimMap primMap + let optsHash = hash opts { -- Ignore the following settings, they don't+ -- affect the generated HDL:+ -- 1. Debug+ opt_dbgLevel = DebugNone+ , opt_dbgTransformations = Set.empty+ -- 2. Caching+ , opt_cachehdl = True+ -- 3. Warnings+ , opt_primWarn = True+ , opt_color = Auto+ , opt_errorExtra = False+ , opt_checkIDir = True+ -- Ignore the following settings, they don't+ -- affect the generated HDL. However, they do+ -- influence whether HDL can be generated at all.+ --+ -- So later on we check whether the new flags+ -- changed in such a way that they could affect+ -- successful compilation, and use that information+ -- to decide whether to use caching or not.+ --+ -- 1. termination measures+ , opt_inlineLimit = 20+ , opt_specLimit = 20+ -- 2. Float support+ , opt_floatSupport = False+ -- Finally, also ignore the HDL dir setting,+ -- because when a user moves the entire dir+ -- with generated HDL, they probably still want+ -- to use that as a cache+ , opt_hdlDir = Nothing+ }+ let topHash = hash ( annM , primMapHash , show clashModDate , callGraphBindings bindingsMap topEntity+ , optsHash ) let@@ -188,9 +338,10 @@ Nothing -> Nothing Just bench -> let terms = callGraphBindings bindingsMap bench in- Just (hash (annM, primMapHash, show clashModDate, terms))+ Just (hash (annM, primMapHash, show clashModDate, terms, optsHash)) - let manifestI = Manifest (topHash,benchHashM) [] [] [] [] []+ let succesFlagsI = (opt_inlineLimit opts,opt_specLimit opts,opt_floatSupport opts)+ manifestI = Manifest (topHash,benchHashM) succesFlagsI [] [] [] [] [] let manFile =@@ -203,19 +354,27 @@ else (>>= readMaybe) . either (const Nothing) Just <$> tryJust (guard . isDoesNotExistError) (readFile manFile) return (maybe (False,False,manifestI)- (\man -> (fst (manifestHash man) == topHash- ,snd (manifestHash man) == benchHashM- ,man {manifestHash = (topHash,benchHashM)}- ))+ (\man ->+ let allowCache (inl0,spec0,fl0) (inl1,spec1,fl1) =+ inl0 <= inl1 && spec0 <= spec1 && (not (fl0 && not fl1))+ flagsAllowCache = allowCache (succesFlags man) succesFlagsI+ in (flagsAllowCache && fst (manifestHash man) == topHash+ ,flagsAllowCache && snd (manifestHash man) == benchHashM+ ,man { manifestHash = (topHash,benchHashM)+ , succesFlags = if flagsAllowCache+ then succesFlags man+ else succesFlagsI+ }+ )) manM) (supplyN,supplyTB) <- Supply.splitSupply . snd . Supply.freshId <$> Supply.newSupply- let topEntityNames = map (\(x,_,_) -> x) topEntities+ let topEntityNames = map topId topEntities2 - (topTime,manifest',seen') <- if sameTopHash+ (topTime,manifest',seen') <- if useCacheTop then do putStrLn ("Clash: Using cached result for: " ++ Data.Text.unpack (nameOcc (varName topEntity))) topTime <- Clock.getCurrentTime@@ -241,8 +400,8 @@ prepareDir (opt_cleanhdl opts) (extension hdlState') dir -- Now start the netlist generation (netlist,seen') <-- genNetlist False opts reprs transformedBindings topEntities primMap- tcm typeTrans iw mkId extId ite seen hdlDir prefixM topEntity+ genNetlist False opts reprs transformedBindings topEntities2 primMap+ tcm typeTrans iw mkId extId ite (SomeBackend hdlState') seen hdlDir prefixM topEntity netlistTime <- netlist `deepseq` Clock.getCurrentTime let normNetDiff = reportTimeDiff netlistTime normTime@@ -260,7 +419,7 @@ return (topTime,manifest',seen') benchTime <- case benchM of- Just tb | not sameBenchHash -> do+ Just tb | not useCacheBench -> do putStrLn $ "Clash: Compiling " ++ Data.Text.unpack (nameOcc (varName tb)) let modName' = genComponentName (opt_newInlineStrat opts) HashMap.empty@@ -272,7 +431,7 @@ typeTrans eval topEntityNames opts supplyTB tb normTime <- transformedBindings `deepseq` Clock.getCurrentTime let prepNormDiff = reportTimeDiff normTime topTime- putStrLn $ "Clash: Testbench normalisation took " ++ prepNormDiff+ putStrLn $ "Clash: Testbench normalization took " ++ prepNormDiff -- 2. Generate netlist for topEntity @@ -281,8 +440,8 @@ prepareDir (opt_cleanhdl opts) (extension hdlState2) dir -- Now start the netlist generation (netlist,seen'') <-- genNetlist True opts reprs transformedBindings topEntities primMap- tcm typeTrans iw mkId extId ite seen' hdlDir prefixM tb+ genNetlist True opts reprs transformedBindings topEntities2 primMap+ tcm typeTrans iw mkId extId ite (SomeBackend hdlState') seen' hdlDir prefixM tb netlistTime <- netlist `deepseq` Clock.getCurrentTime let normNetDiff = reportTimeDiff netlistTime normTime@@ -335,6 +494,8 @@ -- ^ Type name ("BlackBoxFunction" or "TemplateFunction") -> m (Either Hint.InterpreterError a) loadImportAndInterpret iPaths0 interpreterArgs topDir qualMod funcName typ = do+ Hint.liftIO $ Monad.when debugIsOn $+ putStr "Hint: Interpreting " >> putStrLn (qualMod ++ "." ++ funcName) -- Try to interpret function *without* loading module explicitly. If this -- succeeds, the module was already in the global package database(s). bbfE <- Hint.unsafeRunInterpreterWithArgsLibdir interpreterArgs topDir $ do@@ -350,13 +511,48 @@ Hint.unsafeRunInterpreterWithArgsLibdir interpreterArgs topDir $ do Hint.reset iPaths1 <- (iPaths0++) <$> Hint.get Hint.searchPath- Hint.set [Hint.searchPath Hint.:= iPaths1]+ Hint.set [ Hint.searchPath Hint.:= iPaths1+ , Hint.languageExtensions Hint.:= langExts] Hint.loadModules [qualMod] Hint.setImports [ "Clash.Netlist.BlackBox.Types", "Clash.Netlist.Types", qualMod] Hint.unsafeInterpret funcName typ Right _ -> do return bbfE+ where+ langExts = map Hint.asExtension $+ map show wantedLanguageExtensions +++ map ("No" ++ ) (map show unwantedLanguageExtensions) +-- | List of known BlackBoxFunctions used to prevent Hint from firing. This+-- improves Clash startup times.+knownBlackBoxFunctions :: HashMap String BlackBoxFunction+knownBlackBoxFunctions =+ HashMap.fromList $ map (first show) $+ [ ('P.bvToIntegerVHDL, P.bvToIntegerVHDL)+ , ('P.bvToIntegerVerilog, P.bvToIntegerVerilog)+ , ('P.foldBBF, P.foldBBF)+ , ('P.indexIntVerilog, P.indexIntVerilog)+ , ('P.indexToIntegerVerilog, P.indexToIntegerVerilog)+ , ('P.indexToIntegerVHDL, P.indexToIntegerVHDL)+ , ('P.intTF, P.intTF)+ , ('P.signedToIntegerVerilog, P.signedToIntegerVerilog)+ , ('P.signedToIntegerVHDL, P.signedToIntegerVHDL)+ , ('P.unsignedToIntegerVerilog, P.unsignedToIntegerVerilog)+ , ('P.unsignedToIntegerVHDL, P.unsignedToIntegerVHDL)+ , ('P.wordTF, P.wordTF)+ ]++-- | List of known TemplateFunctions used to prevent Hint from firing. This+-- improves Clash startup times.+knownTemplateFunctions :: HashMap String TemplateFunction+knownTemplateFunctions =+ HashMap.fromList $ map (first show) $+ [ ('P.altpllQsysTF, P.altpllQsysTF)+ , ('P.alteraPllQsysTF, P.alteraPllQsysTF)+ , ('P.alteraPllTF, P.alteraPllTF)+ , ('P.altpllTF, P.altpllTF)+ ]+ -- | Compiles blackbox functions and parses blackbox templates. compilePrimitive :: [FilePath]@@ -369,16 +565,26 @@ -> ResolvedPrimitive -- ^ Primitive to compile -> IO CompiledPrimitive-compilePrimitive idirs pkgDbs topDir (BlackBoxHaskell bbName wf bbGenName source) = do- let interpreterArgs = concatMap (("-package-db":) . (:[])) pkgDbs- -- Compile a blackbox template function or fetch it from an already compiled file.- r <- go interpreterArgs source- processHintError- (show bbGenName)- bbName- (\bbFunc -> BlackBoxHaskell bbName wf bbGenName (hash source, bbFunc))- r- where+compilePrimitive idirs pkgDbs topDir (BlackBoxHaskell bbName wf usedArgs bbGenName source) = do+ bbFunc <-+ -- TODO: Use cache for hint targets. Right now Hint will fire multiple times+ -- TODO: if multiple functions use the same blackbox haskell function.+ case HashMap.lookup fullName knownBlackBoxFunctions of+ Just f -> pure f+ Nothing -> do+ Monad.when debugIsOn (putStr "Hint: interpreting " >> putStrLn (show fullName))+ let interpreterArgs = concatMap (("-package-db":) . (:[])) pkgDbs+ -- Compile a blackbox template function or fetch it from an already compiled file.+ r <- go interpreterArgs source+ processHintError+ (show bbGenName)+ bbName+ id+ r++ pure (BlackBoxHaskell bbName wf usedArgs bbGenName (hash source, bbFunc))+ where+ fullName = qualMod ++ "." ++ funcName qualMod = intercalate "." modNames BlackBoxFunctionName modNames funcName = bbGenName @@ -409,12 +615,15 @@ go args Nothing = do loadImportAndInterpret idirs args topDir qualMod funcName "BlackBoxFunction" -compilePrimitive idirs pkgDbs topDir (BlackBox pNm wf tkind () oReg libM imps incs templ) = do+compilePrimitive idirs pkgDbs topDir+ (BlackBox pNm wf rVoid tkind () oReg libM imps fPlural incs rM riM templ) = do libM' <- mapM parseTempl libM imps' <- mapM parseTempl imps incs' <- mapM (traverse parseBB) incs templ' <- parseBB templ- return (BlackBox pNm wf tkind () oReg libM' imps' incs' templ')+ rM' <- traverse parseBB rM+ riM' <- traverse parseBB riM+ return (BlackBox pNm wf rVoid tkind () oReg libM' imps' fPlural incs' rM' riM' templ') where iArgs = concatMap (("-package-db":) . (:[])) pkgDbs @@ -450,11 +659,18 @@ let BlackBoxFunctionName modNames funcName = bbGenName qualMod = intercalate "." modNames hsh = hash qualMod- r <- loadImportAndInterpret idirs iArgs topDir qualMod funcName "TemplateFunction"- processHintError (show bbGenName) pNm (BBFunction (Data.Text.unpack pNm) hsh) r+ fullName = qualMod ++ "." ++ funcName+ tf <-+ case HashMap.lookup fullName knownTemplateFunctions of+ Just f -> pure f+ Nothing -> do+ r <- loadImportAndInterpret idirs iArgs topDir qualMod funcName "TemplateFunction"+ processHintError (show bbGenName) pNm id r+ pure (BBFunction (Data.Text.unpack pNm) hsh tf) compilePrimitive _ _ _ (Primitive pNm wf typ) = return (Primitive pNm wf typ)+{-# SCC compilePrimitive #-} processHintError :: Monad m@@ -515,9 +731,9 @@ let topInNames = map fst (inputs top) topInTypes <- mapM (fmap (Text.toStrict . renderOneLine) . hdlType (External topName) . snd) (inputs top)- let topOutNames = map (fst . snd) (outputs top)+ let topOutNames = map (fst . (\(_,x,_) -> x)) (outputs top) topOutTypes <- mapM (fmap (Text.toStrict . renderOneLine) .- hdlType (External topName) . snd . snd) (outputs top)+ hdlType (External topName) . snd . (\(_,x,_) -> x)) (outputs top) let compNames = map (componentName . view _4) components return (m { portInNames = topInNames , portInTypes = topInTypes@@ -608,7 +824,7 @@ -- ^ Root of the call graph -> [Term] callGraphBindings bindingsMap tm =- map ((^. _4) . (bindingsMap `lookupUniqMap'`)) (keysUniqMap cg)+ map (bindingTerm . (bindingsMap `lookupUniqMap'`)) (keysUniqMap cg) where cg = callGraph bindingsMap tm @@ -626,7 +842,7 @@ -> (CustomReprs -> TyConMap -> Type -> State HWMap (Maybe (Either String FilteredHWType))) -- ^ Hardcoded 'Type' -> 'HWType' translator- -> PrimEvaluator+ -> (PrimStep, PrimUnwind) -- ^ Hardcoded evaluator (delta-reduction) -> [Id] -- ^ TopEntities@@ -647,3 +863,27 @@ transformedBindings = runNormalization opts supply bindingsMap typeTrans reprs tcm tupTcm eval primMap emptyVarEnv topEntities doNorm++-- | topologically sort the top entities+sortTop+ :: BindingMap+ -> [TopEntityT]+ -> [TopEntityT]+sortTop bindingsMap topEntities =+ let (nodes,edges) = unzip (map go topEntities)+ in case reverseTopSort nodes (concat edges) of+ Left msg -> error msg+ Right tops -> tops+ where+ go t@(TopEntityT topE _ tbM) =+ let topRefs = goRefs topE topE+ tbRefs = maybe [] (goRefs topE) tbM+ in ((varUniq topE,t)+ ,map ((\top -> (varUniq topE, varUniq (topId top)))) (tbRefs ++ topRefs))++ goRefs top i_ =+ let cg = callGraph bindingsMap i_+ in+ filter+ (\t -> topId t /= top && topId t /= i_ && topId t `elemVarEnv` cg)+ topEntities
src/Clash/Driver/Types.hs view
@@ -9,29 +9,45 @@ -} {-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE RecordWildCards #-} module Clash.Driver.Types where -- For Int/Word size #include "MachDeps.h" -import Data.Text (Text)+import Control.DeepSeq (NFData)+import Data.Binary (Binary)+import Data.Hashable+import qualified Data.Set as Set+import Data.Text (Text)+import GHC.Generics (Generic) -import BasicTypes (InlineSpec)-import SrcLoc (SrcSpan)+import BasicTypes (InlineSpec)+import SrcLoc (SrcSpan)+import Util (OverridingBool(..)) -import Clash.Core.Term (Term)-import Clash.Core.Var (Id)-import Clash.Core.VarEnv (VarEnv)+import Clash.Core.Term (Term)+import Clash.Core.Var (Id)+import Clash.Core.VarEnv (VarEnv)+import Clash.Netlist.BlackBox.Types (HdlSyn (..)) -import Clash.Netlist.BlackBox.Types (HdlSyn (..)) -import Util (OverridingBool(..))+-- A function binder in the global environment.+--+data Binding = Binding+ { bindingId :: Id+ , bindingLoc :: SrcSpan+ , bindingSpec :: InlineSpec+ , bindingTerm :: Term+ } deriving (Binary, Generic, NFData, Show) -- | Global function binders ----- Global functions cannot be mutually recursive, only self-recursive-type BindingMap = VarEnv (Id,SrcSpan,InlineSpec,Term)+-- Global functions cannot be mutually recursive, only self-recursive.+type BindingMap = VarEnv Binding -- | Debug Message Verbosity data DebugLevel@@ -42,18 +58,21 @@ | DebugFinal -- ^ Show completely normalized expressions | DebugName- -- ^ Names of applied transformations+ -- ^ Show names of applied transformations+ | DebugTry+ -- ^ Show names of tried AND applied transformations | DebugApplied -- ^ Show sub-expressions after a successful rewrite | DebugAll -- ^ Show all sub-expressions on which a rewrite is attempted- deriving (Eq,Ord,Read)+ deriving (Eq,Ord,Read,Enum,Generic,Hashable) data ClashOpts = ClashOpts { opt_inlineLimit :: Int , opt_specLimit :: Int , opt_inlineFunctionLimit :: Word , opt_inlineConstantLimit :: Word , opt_dbgLevel :: DebugLevel+ , opt_dbgTransformations :: Set.Set String , opt_cachehdl :: Bool , opt_cleanhdl :: Bool , opt_primWarn :: Bool@@ -87,13 +106,59 @@ -- * /Just (Just x)/: replace undefined's by /x/ in -- the HDL , opt_checkIDir :: Bool+ , opt_aggressiveXOpt :: Bool+ -- ^ Enable aggressive X optimization, which may+ -- remove undefineds from generated HDL by replaced+ -- with defined alternatives.+ , opt_inlineWFCacheLimit :: Word+ -- ^ At what size do we cache normalized work-free+ -- top-level binders. } +instance Hashable ClashOpts where+ hashWithSalt s ClashOpts {..} =+ s `hashWithSalt`+ opt_inlineLimit `hashWithSalt`+ opt_specLimit `hashWithSalt`+ opt_inlineFunctionLimit `hashWithSalt`+ opt_inlineConstantLimit `hashWithSalt`+ opt_dbgLevel `hashSet`+ opt_dbgTransformations `hashWithSalt`+ opt_cachehdl `hashWithSalt`+ opt_cleanhdl `hashWithSalt`+ opt_primWarn `hashWithSalt`+ opt_cleanhdl `hashOverridingBool`+ opt_color `hashWithSalt`+ opt_intWidth `hashWithSalt`+ opt_hdlDir `hashWithSalt`+ opt_hdlSyn `hashWithSalt`+ opt_errorExtra `hashWithSalt`+ opt_floatSupport `hashWithSalt`+ opt_importPaths `hashWithSalt`+ opt_componentPrefix `hashWithSalt`+ opt_newInlineStrat `hashWithSalt`+ opt_escapedIds `hashWithSalt`+ opt_ultra `hashWithSalt`+ opt_forceUndefined `hashWithSalt`+ opt_checkIDir `hashWithSalt`+ opt_aggressiveXOpt `hashWithSalt`+ opt_inlineWFCacheLimit+ where+ hashOverridingBool :: Int -> OverridingBool -> Int+ hashOverridingBool s1 Auto = hashWithSalt s1 (0 :: Int)+ hashOverridingBool s1 Always = hashWithSalt s1 (1 :: Int)+ hashOverridingBool s1 Never = hashWithSalt s1 (2 :: Int)+ infixl 0 `hashOverridingBool` + hashSet :: Hashable a => Int -> Set.Set a -> Int+ hashSet = Set.foldl' hashWithSalt+ infixl 0 `hashSet`+ defClashOpts :: ClashOpts defClashOpts = ClashOpts { opt_dbgLevel = DebugNone+ , opt_dbgTransformations = Set.empty , opt_inlineLimit = 20 , opt_specLimit = 20 , opt_inlineFunctionLimit = 15@@ -114,6 +179,8 @@ , opt_ultra = False , opt_forceUndefined = Nothing , opt_checkIDir = True+ , opt_aggressiveXOpt = False+ , opt_inlineWFCacheLimit = 10 -- TODO: find "optimal" value } -- | Information about the generated HDL between (sub)runs of the compiler@@ -122,6 +189,12 @@ { manifestHash :: (Int,Maybe Int) -- ^ Hash of the TopEntity and all its dependencies -- + (maybe) Hash of the TestBench and all its dependencies+ , succesFlags :: (Int,Int,Bool)+ -- ^ Compiler flags used to achieve successful compilation:+ --+ -- * opt_inlineLimit+ -- * opt_specLimit+ -- * opt_floatSupport , portInNames :: [Text] , portInTypes :: [Text] -- ^ The rendered versions of the types of the input ports of the TopEntity
src/Clash/Netlist.hs view
@@ -8,17 +8,19 @@ Create Netlists out of normalized CoreHW Terms -} -{-# LANGUAGE MagicHash #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE ViewPatterns #-} module Clash.Netlist where import Control.Exception (throw)-import Control.Lens ((.=),(^.),_2)+import Control.Lens ((.=)) import qualified Control.Lens as Lens import Control.Monad (join) import Control.Monad.IO.Class (liftIO)@@ -26,12 +28,13 @@ import Control.Monad.State.Strict (State, runStateT) import Data.Binary.IEEE754 (floatToWord, doubleToWord) import Data.Char (ord)-import Data.Either (partitionEithers)+import Data.Either (partitionEithers, rights) import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HashMapS import qualified Data.HashMap.Lazy as HashMap-import Data.List (elemIndex, sortOn)-import Data.Maybe (catMaybes, listToMaybe, fromMaybe)+import Data.List (elemIndex, partition, sortOn)+import Data.Maybe+ (catMaybes, listToMaybe, mapMaybe, fromMaybe) import qualified Data.Set as Set import Data.Primitive.ByteArray (ByteArray (..)) import qualified Data.Text as StrictText@@ -54,25 +57,25 @@ import Clash.Core.Name (Name(..)) import Clash.Core.Pretty (showPpr) import Clash.Core.Term- (Alt, Pat (..), Term (..), TickInfo (..), collectArgs, collectArgsTicks, collectTicks)+ (Alt, Pat (..), Term (..), TickInfo (..), PrimInfo(primName), collectArgs, collectArgsTicks, collectTicks) import qualified Clash.Core.Term as Core import Clash.Core.Type- (Type (..), coreView1, splitFunTys, splitCoreFunForallTy)+ (Type (..), coreView1, splitFunForallTy, splitCoreFunForallTy) import Clash.Core.TyCon (TyConMap) import Clash.Core.Util- (mkApps, mkTicks, stripTicks, termType)-import Clash.Core.Var (Id, Var (..))+ (mkApps, mkTicks, splitShouldSplit, stripTicks, termType)+import Clash.Core.Var (Id, Var (..), isGlobalId) import Clash.Core.VarEnv (VarEnv, eltsVarEnv, emptyInScopeSet, emptyVarEnv, extendVarEnv, lookupVarEnv, lookupVarEnv', mkVarEnv)-import Clash.Driver.Types (BindingMap, ClashOpts (..))+import Clash.Driver.Types (BindingMap, Binding(..), ClashOpts (..)) import Clash.Netlist.BlackBox import Clash.Netlist.Id import Clash.Netlist.Types as HW import Clash.Netlist.Util import Clash.Primitives.Types as P import Clash.Util-+import qualified Clash.Util.Interpolate as I -- | Generate a hierarchical netlist out of a set of global binders with -- @topEntity@ at the top.@@ -85,7 +88,7 @@ -- ^ Custom bit representations for certain types -> BindingMap -- ^ Global binders- -> [(Id,Maybe TopEntity,Maybe Id)]+ -> [TopEntityT] -- ^ All the TopEntities -> CompiledPrimMap -- ^ Primitive definitions@@ -102,27 +105,27 @@ -- ^ extend valid identifiers -> Bool -- ^ Whether the backend supports ifThenElse expressions+ -> SomeBackend+ -- ^ The current HDL backend -> HashMap Identifier Word -- ^ Seen components -> FilePath -- ^ HDL dir- -> (Maybe Identifier,Maybe Identifier)+ -> ComponentPrefix -- ^ Component name prefix -> Id -- ^ Name of the @topEntity@ -> IO ([([Bool],SrcSpan,HashMap Identifier Word,Component)],HashMap Identifier Word)-genNetlist isTb opts reprs globals tops primMap tcm typeTrans iw mkId extId ite seen env prefixM topEntity = do- (_,s) <- runNetlistMonad isTb opts reprs globals (mkTopEntityMap tops)- primMap tcm typeTrans iw mkId extId ite seen env prefixM $+genNetlist isTb opts reprs globals tops primMap tcm typeTrans iw mkId extId ite be seen env prefixM topEntity = do+ (_,s) <- runNetlistMonad isTb opts reprs globals topEntityMap+ primMap tcm typeTrans iw mkId extId ite be seen env prefixM $ genComponent topEntity return ( eltsVarEnv $ _components s , _seenComps s ) where- mkTopEntityMap- :: [(Id,Maybe TopEntity,Maybe Id)]- -> VarEnv (Type,Maybe TopEntity)- mkTopEntityMap = mkVarEnv . map (\(a,b,_) -> (a,(varType a,b)))+ topEntityMap :: VarEnv TopEntityT+ topEntityMap = mkVarEnv (zip (map topId tops) tops) -- | Run a NetlistMonad action in a given environment runNetlistMonad@@ -134,7 +137,7 @@ -- ^ Custom bit representations for certain types -> BindingMap -- ^ Global binders- -> VarEnv (Type, Maybe TopEntity)+ -> VarEnv TopEntityT -- ^ TopEntity annotations -> CompiledPrimMap -- ^ Primitive Definitions@@ -151,16 +154,18 @@ -- ^ extend valid identifiers -> Bool -- ^ Whether the backend supports ifThenElse expressions+ -> SomeBackend+ -- ^ The current HDL backend -> HashMap Identifier Word -- ^ Seen components -> FilePath -- ^ HDL dir- -> (Maybe Identifier,Maybe Identifier)+ -> ComponentPrefix -- ^ Component name prefix -> NetlistMonad a -- ^ Action to run -> IO (a, NetlistState)-runNetlistMonad isTb opts reprs s tops p tcm typeTrans iw mkId extId ite seenIds_ env prefixM+runNetlistMonad isTb opts reprs s tops p tcm typeTrans iw mkId extId ite be seenIds_ env prefixM = flip runReaderT (NetlistEnv "" "" Nothing) . flip runStateT s' . runNetlist@@ -168,7 +173,7 @@ s' = NetlistState s 0 emptyVarEnv p typeTrans tcm (StrictText.empty,noSrcSpan) iw mkId- extId HashMapS.empty seenIds' Set.empty names tops env 0 prefixM reprs opts isTb ite+ extId HashMapS.empty seenIds' Set.empty names tops env 0 prefixM reprs opts isTb ite be HashMapS.empty (seenIds',names) = genNames (opt_newInlineStrat opts) mkId prefixM seenIds_@@ -176,17 +181,17 @@ genNames :: Bool -> (IdType -> Identifier -> Identifier)- -> (Maybe Identifier,Maybe Identifier)+ -> ComponentPrefix -> HashMap Identifier Word -> VarEnv Identifier -> BindingMap -> (HashMap Identifier Word, VarEnv Identifier) genNames newInlineStrat mkId prefixM s0 m0 = foldr go (s0,m0) where- go (v,_,_,_) (s,m) =- let nm' = genComponentName newInlineStrat s mkId prefixM v+ go b (s,m) =+ let nm' = genComponentName newInlineStrat s mkId prefixM (bindingId b) s' = HashMapS.insert nm' 0 s- m' = extendVarEnv v nm' m+ m' = extendVarEnv (bindingId b) nm' m in (s', m') -- | Generate a component for a given function (caching)@@ -201,8 +206,8 @@ Nothing -> do (_,sp) <- Lens.use curCompNm throw (ClashException sp ($(curLoc) ++ "No normalized expression found for: " ++ show compName) Nothing)- Just (_,_,_,expr_) -> do- makeCachedU compName components $ genComponentT compName expr_+ Just b -> do+ makeCachedU compName components $ genComponentT compName (bindingTerm b) -- | Generate a component for a given function genComponentT@@ -215,22 +220,22 @@ genComponentT compName componentExpr = do varCount .= 0 componentName1 <- (`lookupVarEnv'` compName) <$> Lens.use componentNames- topEntMM <- fmap snd . lookupVarEnv compName <$> Lens.use topEntityAnns+ topEntMM <- fmap topAnnotation . lookupVarEnv compName <$> Lens.use topEntityAnns prefixM <- Lens.use componentPrefix- let componentName2 = case (prefixM,join topEntMM) of- ((Just p,_),Just ann) -> p `StrictText.append` StrictText.pack ('_':t_name ann)+ let componentName2 = case (componentPrefixTop prefixM, join topEntMM) of+ (Just p, Just ann) -> p `StrictText.append` StrictText.pack ('_':t_name ann) (_,Just ann) -> StrictText.pack (t_name ann) _ -> componentName1- sp <- ((^. _2) . (`lookupVarEnv'` compName)) <$> Lens.use bindings+ sp <- (bindingLoc . (`lookupVarEnv'` compName)) <$> Lens.use bindings curCompNm .= (componentName2,sp) tcm <- Lens.use tcCache -- HACK: Determine resulttype of this function by looking at its definition- -- in topEntityAnns, instead of looking at its last binder (which obscure+ -- in topEntityAnns, instead of looking at its last binder (which obscures -- any attributes [see: Clash.Annotations.SynthesisAttributes]).- topEntityTypeM <- lookupVarEnv compName <$> Lens.use topEntityAnns- let topEntityTypeM' = snd . splitCoreFunForallTy tcm . fst <$> topEntityTypeM+ topEntityTypeM <- lookupVarEnv compName <$> Lens.use topEntityAnns+ let topEntityTypeM' = snd . splitCoreFunForallTy tcm . varType . topId <$> topEntityTypeM seenIds .= HashMapS.empty (wereVoids,compInps,argWrappers,compOutps,resUnwrappers,binders,resultM) <-@@ -239,20 +244,20 @@ let varType' = fromMaybe (varType res) topEntityTypeM' mkUniqueNormalized emptyInScopeSet topEntMM ((args, binds, res{varType=varType'})) Left err ->- throw (ClashException sp err Nothing)+ throw (ClashException sp ($curLoc ++ err) Nothing) netDecls <- fmap catMaybes . mapM mkNetDecl $ filter (maybe (const True) (/=) resultM . fst) binders decls <- concat <$> mapM (uncurry mkDeclarations) binders case resultM of Just result -> do- Just (NetDecl' _ rw _ _) <- mkNetDecl . head $ filter ((==result) . fst) binders+ Just (NetDecl' _ rw _ _ rIM) <- mkNetDecl . head $ filter ((==result) . fst) binders let (compOutps',resUnwrappers') = case compOutps of- [oport] -> ([(rw,oport)],resUnwrappers)+ [oport] -> ([(rw,oport,rIM)],resUnwrappers) _ -> let NetDecl n res resTy = head resUnwrappers- in (map (Wire,) compOutps- ,NetDecl' n rw res (Right resTy):tail resUnwrappers+ in (map (Wire,,Nothing) compOutps+ ,NetDecl' n rw res (Right resTy) Nothing:tail resUnwrappers ) component = Component componentName2 compInps compOutps' (netDecls ++ argWrappers ++ decls ++ resUnwrappers')@@ -267,16 +272,18 @@ return (wereVoids, sp, ids, component) mkNetDecl :: (Id, Term) -> NetlistMonad (Maybe Declaration)-mkNetDecl (id_,tm) = do+mkNetDecl (id_,tm) = preserveVarEnv $ do let typ = varType id_ hwTy <- unsafeCoreTypeToHWTypeM' $(curLoc) typ wr <- termToWireOrReg tm+ rIM <- getResInit (id_,tm) if isVoid hwTy then return Nothing else return . Just $ NetDecl' (addSrcNote sp) wr (id2identifier id_) (Right hwTy)+ rIM where nm = varName id_@@ -291,10 +298,11 @@ case iteAlts scrutHTy alts0 of Just _ | ite -> return Wire _ -> return Reg- termToWireOrReg (collectArgs -> (Prim nm' _,_)) = do- bbM <- HashMap.lookup nm' <$> Lens.use primitives+ termToWireOrReg (collectArgs -> (Prim p,_)) = do+ bbM <- HashMap.lookup (primName p) <$> Lens.use primitives case bbM of Just (extractPrim -> Just BlackBox {..}) | outputReg -> return Reg+ _ | primName p == "Clash.Explicit.SimIO.mealyIO" -> return Reg _ -> return Wire termToWireOrReg _ = return Wire @@ -302,14 +310,30 @@ then Just (StrictText.pack (showSDocUnsafe (ppr loc))) else Nothing --isWriteToBiSignalPrimitive :: Term -> Bool-isWriteToBiSignalPrimitive e = case collectArgs e of- (Prim nm _,_) -> nm == StrictText.pack "Clash.Signal.BiSignal.writeToBiSignal#"- _ -> False+ -- Set the initialization value of a signal when a primitive wants to set it+ getResInit+ :: (Id,Term) -> NetlistMonad (Maybe Expr)+ getResInit (i,collectArgsTicks -> (k,args,ticks)) = case k of+ Prim p -> extractPrimWarnOrFail (primName p) >>= go (primName p)+ _ -> return Nothing+ where+ go pNm (BlackBox {resultInit = Just nmD}) = withTicks ticks $ \_ -> do+ (bbCtx,_) <- mkBlackBoxContext pNm i args+ (bbTempl,templDecl) <- prepareBlackBox pNm nmD bbCtx+ case templDecl of+ [] -> return (Just (BlackBoxE pNm [] [] [] bbTempl bbCtx False))+ _ -> do+ (_,sloc) <- Lens.use curCompNm+ throw (ClashException sloc+ (unwords [ $(curLoc)+ , "signal initialization requires declarations:\n"+ , show templDecl+ ])+ Nothing)+ go _ _ = return Nothing --- | Generate a list of Declarations for a let-binder, return an empty list--- if the bound expression is represented by 0 bits+-- | Generate a list of concurrent Declarations for a let-binder, return an+-- empty list if the bound expression is represented by 0 bits mkDeclarations :: HasCallStack => Id@@ -317,25 +341,24 @@ -> Term -- ^ RHS of the let-binder -> NetlistMonad [Declaration]-mkDeclarations bndr e = do- hty <- unsafeCoreTypeToHWTypeM' $(curLoc) (varType bndr)- if isVoid hty && not (isBiSignalOut hty)- then return []- else mkDeclarations' bndr e+mkDeclarations = mkDeclarations' Concurrent --- | Generate a list of Declarations for a let-binder+-- | Generate a list of Declarations for a let-binder, return an empty list if+-- the bound expression is represented by 0 bits mkDeclarations' :: HasCallStack- => Id+ => DeclarationType+ -- ^ Concurrent of sequential declaration+ -> Id -- ^ LHS of the let-binder -> Term -- ^ RHS of the let-binder -> NetlistMonad [Declaration]-mkDeclarations' bndr (collectTicks -> (Var v,ticks)) =+mkDeclarations' _declType bndr (collectTicks -> (Var v,ticks)) = withTicks ticks $ \tickDecls -> do mkFunApp (id2identifier bndr) v [] tickDecls -mkDeclarations' _ e@(collectTicks -> (Case _ _ [],_)) = do+mkDeclarations' _declType _bndr e@(collectTicks -> (Case _ _ [],_)) = do (_,sp) <- Lens.use curCompNm throw $ ClashException sp@@ -346,86 +369,116 @@ ]) Nothing -mkDeclarations' bndr (collectTicks -> (Case scrut altTy alts@(_:_:_),ticks)) =+mkDeclarations' declType bndr (collectTicks -> (Case scrut altTy alts@(_:_:_),ticks)) = withTicks ticks $ \tickDecls -> do- mkSelection (Right bndr) scrut altTy alts tickDecls+ mkSelection declType (CoreId bndr) scrut altTy alts tickDecls -mkDeclarations' bndr app =+mkDeclarations' declType bndr app = do let (appF,args0,ticks) = collectArgsTicks app (args,tyArgs) = partitionEithers args0- in withTicks ticks $ \tickDecls -> do case appF of Var f- | null tyArgs -> mkFunApp (id2identifier bndr) f args tickDecls+ | null tyArgs -> withTicks ticks (mkFunApp (id2identifier bndr) f args) | otherwise -> do (_,sp) <- Lens.use curCompNm throw (ClashException sp ($(curLoc) ++ "Not in normal form: Var-application with Type arguments:\n\n" ++ showPpr app) Nothing)- -- Do not generate any assignments writing to a BiSignalOut, as these- -- do not have any significance in a HDL. The single exception occurs- -- when writing to a BiSignal using the primitive 'writeToBiSignal'. In- -- the generate HDL it will write to an inout port, NOT the variable- -- having the actual type BiSignalOut.- -- _ | isBiSignalOut (id2type bndr) && (not $ isWriteToBiSignalPrimitive app) ->- -- return [] _ -> do- hwTy <- unsafeCoreTypeToHWTypeM' $(curLoc) (id2type bndr)- if isBiSignalOut hwTy && not (isWriteToBiSignalPrimitive app)- then return []- else do- (exprApp,declsApp0) <- mkExpr False (Right bndr) (varType bndr) app- let dstId = id2identifier bndr- assn = case exprApp of- Identifier _ Nothing -> []- _ -> [Assignment dstId exprApp]- declsApp1 = if null declsApp0 then tickDecls else declsApp0- return (declsApp1 ++ assn)+ (exprApp,declsApp0) <- mkExpr False declType (CoreId bndr) app+ let dstId = id2identifier bndr+ assn =+ case exprApp of+ Identifier _ Nothing ->+ -- Supplied 'bndr' was used to assign a result to, so we+ -- don't have to manually turn it into a declaration+ []+ Noop ->+ -- Rendered expression rendered a "noop" - a list of+ -- declarations without a result. Used for things like+ -- mealy IO / inline assertions.+ []+ _ ->+ -- Turn returned expression into declaration by assigning+ -- it to 'dstId'+ [Assignment dstId exprApp]+ declsApp1 <- if null declsApp0+ then withTicks ticks return+ else pure declsApp0+ return (declsApp1 ++ assn) -- | Generate a declaration that selects an alternative based on the value of -- the scrutinee mkSelection- :: (Either Identifier Id)+ :: DeclarationType+ -> NetlistId -> Term -> Type -> [Alt] -> [Declaration] -> NetlistMonad [Declaration]-mkSelection bndr scrut altTy alts0 tickDecls = do- let dstId = either id id2identifier bndr+mkSelection declType bndr scrut altTy alts0 tickDecls = do+ let dstId = netlistId1 id id2identifier bndr tcm <- Lens.use tcCache let scrutTy = termType tcm scrut scrutHTy <- unsafeCoreTypeToHWTypeM' $(curLoc) scrutTy scrutId <- extendIdentifier Extended dstId "_selection" (_,sp) <- Lens.use curCompNm ite <- Lens.use backEndITE+ altHTy <- unsafeCoreTypeToHWTypeM' $(curLoc) altTy case iteAlts scrutHTy alts0 of Just (altT,altF) | ite+ , Concurrent <- declType -> do (scrutExpr,scrutDecls) <- case scrutHTy of SP {} -> first (mkScrutExpr sp scrutHTy (fst (last alts0))) <$>- mkExpr True (Left scrutId) scrutTy scrut- _ -> mkExpr False (Left scrutId) scrutTy scrut+ mkExpr True declType (NetlistId scrutId scrutTy) scrut+ _ -> mkExpr False declType (NetlistId scrutId scrutTy) scrut altTId <- extendIdentifier Extended dstId "_sel_alt_t" altFId <- extendIdentifier Extended dstId "_sel_alt_f"- (altTExpr,altTDecls) <- mkExpr False (Left altTId) altTy altT- (altFExpr,altFDecls) <- mkExpr False (Left altFId) altTy altF- return $! scrutDecls ++ altTDecls ++ altFDecls ++ tickDecls +++ (altTExpr,altTDecls) <- mkExpr False declType (NetlistId altTId altTy) altT+ (altFExpr,altFDecls) <- mkExpr False declType (NetlistId altFId altTy) altF+ -- This logic (and the same logic a few lines below) is faulty in the+ -- sense that it won't generate "void decls" if the alternatives' type+ -- is void, but the type of the scrut isn't. Ideally, we'd like to pass+ -- a boolean to 'mkExpr' indicating that it should only render "void decls"+ -- but that it should skip any others.+ --+ -- TODO: Fix ^+ if | isVoid altHTy && isVoid scrutHTy+ -> return $! scrutDecls ++ altTDecls ++ altFDecls+ | isVoid altHTy+ -> return $! altTDecls ++ altFDecls+ | otherwise+ -> return $! scrutDecls ++ altTDecls ++ altFDecls ++ tickDecls ++ [Assignment dstId (IfThenElse scrutExpr altTExpr altFExpr)] _ -> do reprs <- Lens.use customReprs let alts1 = (reorderDefault . reorderCustom tcm reprs scrutTy) alts0- altHTy <- unsafeCoreTypeToHWTypeM' $(curLoc) altTy (scrutExpr,scrutDecls) <- first (mkScrutExpr sp scrutHTy (fst (head alts1))) <$>- mkExpr True (Left scrutId) scrutTy scrut- (exprs,altsDecls) <- (second concat . unzip) <$> mapM (mkCondExpr scrutHTy) alts1- return $! scrutDecls ++ altsDecls ++ tickDecls ++ [CondAssignment dstId altHTy scrutExpr scrutHTy exprs]+ mkExpr True declType (NetlistId scrutId scrutTy) scrut+ (exprs,altsDecls) <- unzip <$> mapM (mkCondExpr scrutHTy) alts1+ case declType of+ Sequential -> do+ -- Assign to the result in every branch+ let (altNets,exprAlts) = unzip (zipWith (altAssign dstId)+ exprs altsDecls)+ return $! scrutDecls ++ tickDecls ++ concat altNets +++ [Seq [Branch scrutExpr scrutHTy exprAlts]]+ Concurrent ->+ if | isVoid altHTy && isVoid scrutHTy+ -> return $! concat altsDecls ++ scrutDecls+ | isVoid altHTy+ -> return $! concat altsDecls+ | otherwise+ -> return $! scrutDecls ++ concat altsDecls ++ tickDecls+ ++ [CondAssignment dstId altHTy scrutExpr scrutHTy exprs] where mkCondExpr :: HWType -> (Pat,Term) -> NetlistMonad ((Maybe HW.Literal,Expr),[Declaration]) mkCondExpr scrutHTy (pat,alt) = do altId <- extendIdentifier Extended- (either id id2identifier bndr)+ (netlistId1 id id2identifier bndr) "_sel_alt"- (altExpr,altDecls) <- mkExpr False (Left altId) altTy alt+ (altExpr,altDecls) <- mkExpr False declType (NetlistId altId altTy) alt (,altDecls) <$> case pat of DefaultPat -> return (Nothing,altExpr) DataPat dc _ _ -> return (Just (dcToLiteral scrutHTy (dcTag dc)),altExpr)@@ -448,6 +501,16 @@ _ -> throw (ClashException sp ($(curLoc) ++ "Not in normal form: Not a variable reference or primitive as subject of a case-statement:\n\n" ++ show scrutE) Nothing) _ -> scrutE + altAssign :: Identifier -> (Maybe HW.Literal,Expr) -> [Declaration]+ -> ([Declaration],(Maybe HW.Literal,[Seq]))+ altAssign i (m,expr) ds =+ let (nets,rest) = partition isNet ds+ assn = case expr of { Noop -> []; _ -> [SeqDecl (Assignment i expr)] }+ in (nets,(m,map SeqDecl rest ++ assn))+ where+ isNet NetDecl' {} = True+ isNet _ = False+ -- GHC puts default patterns in the first position, we want them in the -- last position. reorderDefault@@ -501,21 +564,26 @@ mkFunApp dstId fun args tickDecls = do topAnns <- Lens.use topEntityAnns tcm <- Lens.use tcCache- case lookupVarEnv fun topAnns of- Just (ty,annM)- | let (fArgTys,fResTy) = splitFunTys tcm ty- , length fArgTys == length args+ case (isGlobalId fun, lookupVarEnv fun topAnns) of+ (True, Just topEntity)+ | let ty = varType (topId topEntity)+ , let (fArgTys0,fResTy) = splitFunForallTy ty+ -- Take into account that clocks and stuff are split off from any product+ -- types containing them+ , let fArgTys1 = splitShouldSplit tcm $ rights fArgTys0+ , length fArgTys1 == length args -> do- argHWTys <- mapM (unsafeCoreTypeToHWTypeM' $(curLoc)) fArgTys- -- Filter out the arguments of hwtype `Void` and only translate them- -- to the intermediate HDL afterwards- let argsBundled = zip argHWTys (zip args fArgTys)- argsFiltered = filter (not . isVoid . fst) argsBundled- argsFiltered' = map snd argsFiltered- hWTysFiltered = filter (not . isVoid) argHWTys- (argExprs,argDecls) <- second concat . unzip <$>- mapM (\(e,t) -> mkExpr False (Left dstId) t e)- argsFiltered'+ let annM = topAnnotation topEntity+ argHWTys <- mapM (unsafeCoreTypeToHWTypeM' $(curLoc)) fArgTys1+ (argExprs, concat -> argDecls) <- unzip <$>+ mapM (\(e,t) -> mkExpr False Concurrent (NetlistId dstId t) e)+ (zip args fArgTys1)++ -- Filter void arguments, but make sure to render their declarations:+ let+ filteredTypeExprs = filter (not . isVoid . fst) (zip argHWTys argExprs)+ (hWTysFiltered, argExprsFiltered) = unzip filteredTypeExprs+ dstHWty <- unsafeCoreTypeToHWTypeM' $(curLoc) fResTy env <- Lens.use hdlDir mkId <- Lens.use mkIdentifierFn@@ -530,29 +598,38 @@ Nothing -> return (env </> topName <.> "manifest") Just man <- readMaybe <$> liftIO (readFile manFile) instDecls <- mkTopUnWrapper fun annM man (dstId,dstHWty)- (zip argExprs hWTysFiltered)+ (zip argExprsFiltered hWTysFiltered) tickDecls return (argDecls ++ instDecls) - | otherwise -> error $ $(curLoc) ++ "under-applied TopEntity"- _ -> do+ | otherwise -> error $ $(curLoc) ++ "under-applied TopEntity: " ++ showPpr fun+ (True, Nothing) -> do normalized <- Lens.use bindings case lookupVarEnv fun normalized of- Just _ -> do+ Nothing -> error [I.i|+ Internal error: unknown normalized binder:++ #{showPpr fun}+ |]+ Just (Binding{bindingTerm}) -> do (_,_,_,Component compName compInps co _) <- preserveVarEnv $ genComponent fun let argTys = map (termType tcm) args argHWTys <- mapM coreTypeToHWTypeM' argTys- -- Filter out the arguments of hwtype `Void` and only translate- -- them to the intermediate HDL afterwards- let argsBundled = zip argHWTys (zip args argTys)- argsFiltered = filter (maybe True (not . isVoid) . fst) argsBundled- argsFiltered' = map snd argsFiltered- tysFiltered = map snd argsFiltered'- compOutp = snd <$> listToMaybe co- if length tysFiltered == length compInps++ (argExprs, concat -> argDecls) <- unzip <$>+ mapM (\(e,t) -> mkExpr False Concurrent (NetlistId dstId t) e)+ (zip args argTys)++ -- Filter void arguments, but make sure to render their declarations:+ let+ argTypeExprs = zip argHWTys (zip argTys argExprs)+ filteredTypeExprs = filterOnFst (not . isVoidMaybe True) argTypeExprs+ (argTysFiltered, argsFiltered) = unzip filteredTypeExprs++ let compOutp = (\(_,x,_) -> x) <$> listToMaybe co+ if length argTysFiltered == length compInps then do- (argExprs,argDecls) <- fmap (second concat . unzip) $! mapM (\(e,t) -> mkExpr False (Left dstId) t e) argsFiltered'- (argExprs',argDecls') <- (second concat . unzip) <$> mapM (toSimpleVar dstId) (zip argExprs tysFiltered)+ (argExprs',argDecls') <- (second concat . unzip) <$> mapM (toSimpleVar dstId) (zip argsFiltered argTysFiltered) let inpAssigns = zipWith (\(i,t) e -> (Identifier i Nothing,In,t,e)) compInps argExprs' outpAssign = case compOutp of Nothing -> []@@ -563,11 +640,52 @@ instLabel3 <- mkUniqueIdentifier Basic instLabel2 let instDecl = InstDecl Entity Nothing compName instLabel3 [] (outpAssign ++ inpAssigns) return (argDecls ++ argDecls' ++ tickDecls ++ [instDecl])- else error $ $(curLoc) ++ "under-applied normalized function"- Nothing -> case args of- [] -> return [Assignment dstId (Identifier (nameOcc $ varName fun) Nothing)]- _ -> error $ $(curLoc) ++ "Unknown function: " ++ showPpr fun+ else error [I.i|+ Under-applied normalized function at component #{compName}: + #{showPpr fun}++ Core:++ #{showPpr bindingTerm}++ Applied to arguments:+ #{showPpr args}++ Applied to filtered arguments:+ #{argsFiltered}++ Component inputs:+ #{compInps}+ |]+ _ ->+ case args of+ [] ->+ -- TODO: Figure out what to do with zero-width constructs+ return [Assignment dstId (Identifier (nameOcc $ varName fun) Nothing)]+ _ -> error [I.i|+ Netlist generation encountered a local function. This should not+ happen. Function:++ #{showPpr fun}++ Arguments:++ #{showPpr args}++ Posssible user issues:++ * A top entity has an higher-order argument, e.g (Int -> Int) or+ Maybe (Int -> Int)++ Possible internal compiler issues:++ * 'bindOrLiftNonRep' failed to fire++ * 'caseCon' failed to eliminate something of a type such as+ "Maybe (Int -> Int)"+ |]+ toSimpleVar :: Identifier -> (Expr,Type) -> NetlistMonad (Expr,[Declaration])@@ -585,8 +703,9 @@ -- | Generate an expression for a term occurring on the RHS of a let-binder mkExpr :: HasCallStack => Bool -- ^ Treat BlackBox expression as declaration- -> (Either Identifier Id) -- ^ Id to assign the result to- -> Type -- ^ Type of the LHS of the let-binder+ -> DeclarationType+ -- ^ Should the returned declarations be concurrent or sequential?+ -> NetlistId -- ^ Id to assign the result to -> Term -- ^ Term to convert to an expression -> NetlistMonad (Expr,[Declaration]) -- ^ Returned expression and a list of generate BlackBox declarations mkExpr _ _ _ (stripTicks -> Core.Literal l) = do@@ -608,25 +727,35 @@ ByteArrayLiteral (PV.Vector _ _ (ByteArray ba)) -> return (HW.Literal Nothing (NumLit (Jp# (BN# ba))),[]) _ -> error $ $(curLoc) ++ "not an integer or char literal" -mkExpr bbEasD bndr ty app =+mkExpr bbEasD declType bndr app = let (appF,args,ticks) = collectArgsTicks app (tmArgs,tyArgs) = partitionEithers args in withTicks ticks $ \tickDecls -> do- hwTy <- unsafeCoreTypeToHWTypeM' $(curLoc) ty+ hwTys <- mapM (unsafeCoreTypeToHWTypeM' $(curLoc)) (netlistTypes bndr) (_,sp) <- Lens.use curCompNm+ let hwTyA = head hwTys case appF of- Data dc -> mkDcApplication hwTy bndr dc tmArgs- Prim nm _ -> mkPrimitive False bbEasD bndr nm args ty tickDecls+ Data dc -> mkDcApplication hwTys bndr dc tmArgs+ Prim pInfo -> mkPrimitive False bbEasD bndr pInfo args tickDecls Var f- | null tmArgs -> return (Identifier (nameOcc $ varName f) Nothing,[])+ | null tmArgs ->+ if isVoid hwTyA then+ return (Noop, [])+ else+ return (Identifier (nameOcc $ varName f) Nothing, []) | not (null tyArgs) ->- throw (ClashException sp ($(curLoc) ++ "Not in normal form: Var-application with Type arguments:\n\n" ++ showPpr app) Nothing)+ throw (ClashException sp ($(curLoc) ++ "Not in normal form: "+ ++ "Var-application with Type arguments:\n\n" ++ showPpr app) Nothing) | otherwise -> do- argNm0 <- extendIdentifier Extended (either id id2identifier bndr) "_fun_arg"+ argNm0 <- extendIdentifier Extended (netlistId1 id id2identifier bndr)+ "_fun_arg" argNm1 <- mkUniqueIdentifier Extended argNm0- hwTyA <- unsafeCoreTypeToHWTypeM' $(curLoc) ty decls <- mkFunApp argNm1 f tmArgs tickDecls- return (Identifier argNm1 Nothing, NetDecl' Nothing Wire argNm1 (Right hwTyA):decls)+ if isVoid hwTyA then+ return (Noop, decls)+ else+ return ( Identifier argNm1 Nothing+ , NetDecl' Nothing Wire argNm1 (Right hwTyA) Nothing:decls) Case scrut ty' [alt] -> mkProjection bbEasD bndr scrut ty' alt Case scrut tyA alts -> do tcm <- Lens.use tcCache@@ -636,15 +765,19 @@ let wr = case iteAlts scrutHTy alts of Just _ | ite -> Wire _ -> Reg- argNm0 <- extendIdentifier Extended (either id id2identifier bndr) "_sel_arg"+ argNm0 <- extendIdentifier Extended (netlistId1 id id2identifier bndr) "_sel_arg" argNm1 <- mkUniqueIdentifier Extended argNm0- hwTyA <- unsafeCoreTypeToHWTypeM' $(curLoc) tyA- decls <- mkSelection (Left argNm1) scrut tyA alts tickDecls- return (Identifier argNm1 Nothing, NetDecl' Nothing wr argNm1 (Right hwTyA):decls)+ decls <- mkSelection declType (NetlistId argNm1 (netlistTypes1 bndr))+ scrut tyA alts tickDecls+ if isVoid hwTyA then+ return (Noop, decls)+ else+ return ( Identifier argNm1 Nothing+ , NetDecl' Nothing wr argNm1 (Right hwTyA) Nothing:decls) Letrec binders body -> do netDecls <- fmap catMaybes $ mapM mkNetDecl binders decls <- concat <$> mapM (uncurry mkDeclarations) binders- (bodyE,bodyDecls) <- mkExpr bbEasD bndr ty (mkApps (mkTicks body ticks) args)+ (bodyE,bodyDecls) <- mkExpr bbEasD declType bndr (mkApps (mkTicks body ticks) args) return (bodyE,netDecls ++ decls ++ bodyDecls) _ -> throw (ClashException sp ($(curLoc) ++ "Not in normal form: application of a Lambda-expression\n\n" ++ showPpr app) Nothing) @@ -654,7 +787,7 @@ mkProjection :: Bool -- ^ Projection must bind to a simple variable- -> Either Identifier Id+ -> NetlistId -- ^ The signal to which the projection is (potentially) assigned -> Term -- ^ The subject/scrutinee of the projection@@ -675,59 +808,79 @@ ++ showPpr e) Nothing) sHwTy <- unsafeCoreTypeToHWTypeM' $(curLoc) scrutTy vHwTy <- unsafeCoreTypeToHWTypeM' $(curLoc) altTy- (selId,modM,decls) <- do- scrutNm <- either return- (\b -> extendIdentifier Extended- (id2identifier b)- "_projection")- bndr- (scrutExpr,newDecls) <- mkExpr False (Left scrutNm) scrutTy scrut+ scrutRendered <- do+ scrutNm <-+ netlistId1+ return+ (\b -> extendIdentifier Extended (id2identifier b) "_projection")+ bndr+ (scrutExpr,newDecls) <- mkExpr False Concurrent (NetlistId scrutNm scrutTy) scrut case scrutExpr of- Identifier newId modM -> return (newId,modM,newDecls)+ Identifier newId modM ->+ pure (Right (newId, modM, newDecls))+ Noop ->+ -- Scrutinee was a zero-width / void construct. We need to render its+ -- declarations, but it's no use assigning it to a new variable.+ -- TODO: Figure out whether we need to render alternatives too.+ -- TODO: seems useless?+ pure (Left newDecls) _ -> do scrutNm' <- mkUniqueIdentifier Extended scrutNm let scrutDecl = NetDecl Nothing scrutNm' sHwTy scrutAssn = Assignment scrutNm' scrutExpr- return (scrutNm',Nothing,newDecls ++ [scrutDecl,scrutAssn])+ pure (Right (scrutNm', Nothing, newDecls ++ [scrutDecl, scrutAssn])) - let altVarId = nameOcc (varName varTm)- modifier <- case pat of+ case scrutRendered of+ Left newDecls -> pure (Noop, newDecls)+ Right (selId, modM, decls) -> do+ let altVarId = nameOcc (varName varTm)+ modifier <- case pat of DataPat dc exts tms -> do- let tms' = if bindsExistentials exts tms- then throw (ClashException sp ($(curLoc) ++ "Not in normal form: Pattern binds existential variables:\n\n" ++ showPpr e) Nothing)- else tms+ let+ tms' =+ if bindsExistentials exts tms then+ throw (ClashException sp ($(curLoc)+ ++ "Not in normal form: Pattern binds existential variables:\n\n"+ ++ showPpr e) Nothing)+ else+ tms argHWTys <- mapM coreTypeToHWTypeM' (map varType tms) let tmsBundled = zip argHWTys tms' tmsFiltered = filter (maybe False (not . isVoid) . fst) tmsBundled tmsFiltered' = map snd tmsFiltered case elemIndex varTm {varType = altTy} tmsFiltered' of- Nothing -> pure Nothing- Just fI- | sHwTy /= vHwTy -> pure $ nestModifier modM (Just (Indexed (sHwTy,dcTag dc - 1,fI)))- -- When element and subject have the same HW-type,- -- then the projections is just the identity- | otherwise -> pure $ nestModifier modM (Just (DC (Void Nothing,0)))- _ -> throw (ClashException sp ($(curLoc) ++ "Not in normal form: Unexpected pattern in case-projection:\n\n" ++ showPpr e) Nothing)- let extractExpr = Identifier (maybe altVarId (const selId) modifier) modifier- case bndr of- Left scrutNm | mkDec -> do- scrutNm' <- mkUniqueIdentifier Extended scrutNm- let scrutDecl = NetDecl Nothing scrutNm' vHwTy- scrutAssn = Assignment scrutNm' extractExpr- return (Identifier scrutNm' Nothing,scrutDecl:scrutAssn:decls)- _ -> return (extractExpr,decls)+ Nothing -> pure Nothing+ Just fI+ | sHwTy /= vHwTy ->+ pure $ nestModifier modM (Just (Indexed (sHwTy,dcTag dc - 1,fI)))+ -- When element and subject have the same HW-type,+ -- then the projections is just the identity+ | otherwise ->+ pure $ nestModifier modM (Just (DC (Void Nothing,0)))+ _ -> throw (ClashException sp ($(curLoc)+ ++ "Not in normal form: Unexpected pattern in case-projection:\n\n"+ ++ showPpr e) Nothing)+ let extractExpr = Identifier (maybe altVarId (const selId) modifier) modifier+ case bndr of+ NetlistId scrutNm _ | mkDec -> do+ scrutNm' <- mkUniqueIdentifier Extended scrutNm+ let scrutDecl = NetDecl Nothing scrutNm' vHwTy+ scrutAssn = Assignment scrutNm' extractExpr+ return (Identifier scrutNm' Nothing,scrutDecl:scrutAssn:decls)+ MultiId {} -> error "mkProjection: MultiId"+ _ -> return (extractExpr,decls) where nestModifier Nothing m = m nestModifier m Nothing = m nestModifier (Just m1) (Just m2) = Just (Nested m1 m2) - -- | Generate an expression for a DataCon application occurring on the RHS of a let-binder mkDcApplication :: HasCallStack- => HWType- -- ^ HWType of the LHS of the let-binder- -> (Either Identifier Id)+ => [HWType]+ -- ^ HWType of the LHS of the let-binder, can multiple types when we're+ -- creating a "split" product type (e.g. a tuple of a Clock and Reset)+ -> NetlistId -- ^ Id to assign the result to -> DataCon -- ^ Applied DataCon@@ -735,19 +888,26 @@ -- ^ DataCon Arguments -> NetlistMonad (Expr,[Declaration]) -- ^ Returned expression and a list of generate BlackBox declarations-mkDcApplication dstHType bndr dc args = do+mkDcApplication [dstHType] bndr dc args = do let dcNm = nameOcc (dcName dc)- tcm <- Lens.use tcCache- let argTys = map (termType tcm) args- argNm <- either return (\b -> extendIdentifier Extended (nameOcc (varName b)) "_dc_arg") bndr- argHWTys <- mapM coreTypeToHWTypeM' argTys- -- Filter out the arguments of hwtype `Void` and only translate- -- them to the intermediate HDL afterwards- let argsBundled = zip argHWTys (zip args argTys)- (hWTysFiltered,argsFiltered) = unzip- (filter (maybe True (not . isVoid) . fst) argsBundled)- (argExprs,argDecls) <- fmap (second concat . unzip) $! mapM (\(e,t) -> mkExpr False (Left argNm) t e) argsFiltered- fmap (,argDecls) $! case (hWTysFiltered,argExprs) of+ tcm <- Lens.use tcCache+ let argTys = map (termType tcm) args+ argNm <- netlistId1 return (\b -> extendIdentifier Extended (nameOcc (varName b)) "_dc_arg") bndr+ argHWTys <- mapM coreTypeToHWTypeM' argTys++ (argExprs, concat -> argDecls) <- unzip <$>+ mapM (\(e,t) -> mkExpr False Concurrent (NetlistId argNm t) e) (zip args argTys)++ -- Filter void arguments, but make sure to render their declarations:+ let+ filteredTypeExprDecls =+ filter+ (not . isVoidMaybe True . fst)+ (zip argHWTys argExprs)++ (hWTysFiltered, argExprsFiltered) = unzip filteredTypeExprDecls++ fmap (,argDecls) $! case (hWTysFiltered,argExprsFiltered) of -- Is the DC just a newtype wrapper? ([Just argHwTy],[argExpr]) | argHwTy == dstHType -> return (HW.DataCon dstHType (DC (Void Nothing,-1)) [argExpr])@@ -755,15 +915,20 @@ SP _ dcArgPairs -> do let dcI = dcTag dc - 1 dcArgs = snd $ indexNote ($(curLoc) ++ "No DC with tag: " ++ show dcI) dcArgPairs dcI- case compare (length dcArgs) (length argExprs) of- EQ -> return (HW.DataCon dstHType (DC (dstHType,dcI)) argExprs)+ case compare (length dcArgs) (length argExprsFiltered) of+ EQ -> return (HW.DataCon dstHType (DC (dstHType,dcI)) argExprsFiltered) LT -> error $ $(curLoc) ++ "Over-applied constructor" GT -> error $ $(curLoc) ++ "Under-applied constructor" Product _ _ dcArgs ->- case compare (length dcArgs) (length argExprs) of- EQ -> return (HW.DataCon dstHType (DC (dstHType,0)) argExprs)+ case compare (length dcArgs) (length argExprsFiltered) of+ EQ -> return (HW.DataCon dstHType (DC (dstHType,0)) argExprsFiltered) LT -> error $ $(curLoc) ++ "Over-applied constructor" GT -> error $ $(curLoc) ++ "Under-applied constructor"+ CustomProduct _ _ _ _ dcArgs ->+ case compare (length dcArgs) (length argExprsFiltered) of+ EQ -> return (HW.DataCon dstHType (DC (dstHType,0)) argExprsFiltered)+ LT -> error $ $(curLoc) ++ "Over-applied constructor"+ GT -> error $ $(curLoc) ++ "Under-applied constructor" Sum _ _ -> return (HW.DataCon dstHType (DC (dstHType,dcTag dc - 1)) []) CustomSP _ _ _ dcArgsTups -> do@@ -773,8 +938,8 @@ let argTup = indexNote note dcArgsTups dcI let (_, _, dcArgs) = argTup - case compare (length dcArgs) (length argExprs) of- EQ -> return (HW.DataCon dstHType (DC (dstHType, dcI)) argExprs)+ case compare (length dcArgs) (length argExprsFiltered) of+ EQ -> return (HW.DataCon dstHType (DC (dstHType, dcI)) argExprsFiltered) LT -> error $ $(curLoc) ++ "Over-applied constructor" GT -> error $ $(curLoc) ++ "Under-applied constructor" @@ -787,16 +952,16 @@ tg -> error $ $(curLoc) ++ "unknown bool literal: " ++ showPpr dc ++ "(tag: " ++ show tg ++ ")" in return dc' Vector 0 _ -> return (HW.DataCon dstHType VecAppend [])- Vector 1 _ -> case argExprs of+ Vector 1 _ -> case argExprsFiltered of [e] -> return (HW.DataCon dstHType VecAppend [e]) _ -> error $ $(curLoc) ++ "Unexpected number of arguments for `Cons`: " ++ showPpr args- Vector _ _ -> case argExprs of+ Vector _ _ -> case argExprsFiltered of [e1,e2] -> return (HW.DataCon dstHType VecAppend [e1,e2]) _ -> error $ $(curLoc) ++ "Unexpected number of arguments for `Cons`: " ++ showPpr args- RTree 0 _ -> case argExprs of+ RTree 0 _ -> case argExprsFiltered of [e] -> return (HW.DataCon dstHType RTreeAppend [e]) _ -> error $ $(curLoc) ++ "Unexpected number of arguments for `LR`: " ++ showPpr args- RTree _ _ -> case argExprs of+ RTree _ _ -> case argExprsFiltered of [e1,e2] -> return (HW.DataCon dstHType RTreeAppend [e1,e2]) _ -> error $ $(curLoc) ++ "Unexpected number of arguments for `BR`: " ++ showPpr args String ->@@ -804,31 +969,51 @@ 1 -> HW.Literal Nothing (StringLit "") _ -> error $ $(curLoc) ++ "mkDcApplication undefined for: " ++ show (dstHType,dc,dcTag dc,args,argHWTys) in return dc'- Void {} -> return (Identifier "__VOID__" Nothing)+ Void {} -> return Noop Signed _ | dcNm == "GHC.Integer.Type.S#"- -> pure (head argExprs)+ -> pure (head argExprsFiltered)+ -- ByteArray# are non-translatable / void, except when they're literals | dcNm == "GHC.Integer.Type.Jp#"+ , HW.Literal Nothing (NumLit _) <- head argExprs -> pure (head argExprs) | dcNm == "GHC.Integer.Type.Jn#"+ -- ByteArray# are non-translatable / void, except when they're literals , HW.Literal Nothing (NumLit i) <- head argExprs -> pure (HW.Literal Nothing (NumLit (negate i))) Unsigned _ | dcNm == "GHC.Natural.NatS#"- -> pure (head argExprs)+ -> pure (head argExprsFiltered) | dcNm == "GHC.Natural.NatJ#"+ -- ByteArray# are non-translatable / void, except when they're literals+ , HW.Literal Nothing (NumLit _) <- head argExprs -> pure (head argExprs)--- KnownDomain {} ->--- return (Identifier "__KNOWNDOMAIN__" Nothing)--- pure $--- error $ $(curLoc) ++ "mkDcApplication undefined for KnownDomain. "--- ++ "Did a blackbox definition try to render it? "--- ++ "Context: \n\n"--- ++ "dstHType: " ++ show dstHType ++ "\n\n"--- ++ "dc: " ++ show dc ++ "\n\n"--- ++ "args: " ++ show args ++ "\n\n"--- ++ "argHWTys: " ++ show argHWTys ++ "\n\n"--- ++ "Callstack: "--- ++ prettyCallStack callStack _ -> error $ $(curLoc) ++ "mkDcApplication undefined for: " ++ show (dstHType,dc,args,argHWTys)++-- Handle MultiId assignment+mkDcApplication dstHTypes (MultiId argNms) _ args = do+ tcm <- Lens.use tcCache+ let argTys = map (termType tcm) args+ argHWTys <- mapM coreTypeToHWTypeM' argTys+ -- Filter out the arguments of hwtype `Void` and only translate+ -- them to the intermediate HDL afterwards+ let argsBundled = zip argHWTys (zipEqual (map CoreId argNms) args)+ (_hWTysFiltered,argsFiltered) = unzip+ (filter (maybe True (not . isVoid) . fst) argsBundled)+ (argExprs,argDecls) <- fmap (second concat . unzip) $!+ mapM (uncurry (mkExpr False Concurrent)) argsFiltered+ if length dstHTypes == length argExprs then do+ let assns = mapMaybe+ (\case (_,Noop) -> Nothing+ (dstId,e) -> let nm = netlistId1 id id2identifier dstId+ in case e of+ Identifier nm0 Nothing+ | nm == nm0 -> Nothing+ _ -> Just (Assignment nm e))+ (zip (map CoreId argNms) argExprs)+ return (Noop,argDecls ++ assns)+ else+ error "internal error"++mkDcApplication _ _ _ _ = error "internal error"
src/Clash/Netlist.hs-boot view
@@ -9,6 +9,7 @@ ,mkExpr ,mkDcApplication ,mkDeclarations+ ,mkDeclarations' ,mkNetDecl ,mkProjection ,mkSelection@@ -21,7 +22,7 @@ import Clash.Core.Type (Type) import Clash.Core.Var (Id) import Clash.Netlist.Types (Expr, HWType, Identifier, NetlistMonad, Component,- Declaration)+ Declaration, NetlistId, DeclarationType) import SrcLoc (SrcSpan) import GHC.Stack (HasCallStack)@@ -32,28 +33,29 @@ mkExpr :: HasCallStack => Bool- -> Either Identifier Id- -> Type+ -> DeclarationType+ -> NetlistId -> Term -> NetlistMonad (Expr,[Declaration]) mkDcApplication :: HasCallStack- => HWType- -> Either Identifier Id+ => [HWType]+ -> NetlistId -> DataCon -> [Term] -> NetlistMonad (Expr,[Declaration]) mkProjection :: Bool- -> Either Identifier Id+ -> NetlistId -> Term -> Type -> Alt -> NetlistMonad (Expr, [Declaration]) mkSelection- :: Either Identifier Id+ :: DeclarationType+ -> NetlistId -> Term -> Type -> [Alt]@@ -63,6 +65,7 @@ mkNetDecl :: LetBinding -> NetlistMonad (Maybe Declaration) mkDeclarations :: HasCallStack => Id -> Term -> NetlistMonad [Declaration]+mkDeclarations' :: HasCallStack => DeclarationType -> Id -> Term -> NetlistMonad [Declaration] mkFunApp :: HasCallStack
src/Clash/Netlist/BlackBox.hs view
@@ -8,32 +8,32 @@ Functions to create BlackBox Contexts and fill in BlackBox templates -} -{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TemplateHaskell #-} module Clash.Netlist.BlackBox where import Control.Exception (throw) import Control.Lens ((<<%=),(%=)) import qualified Control.Lens as Lens-import Control.Monad (when)+import Control.Monad (when, replicateM) import Control.Monad.IO.Class (liftIO) import Data.Char (ord) import Data.Either (lefts, partitionEithers) import qualified Data.HashMap.Lazy as HashMap import qualified Data.IntMap as IntMap-import Data.List (elemIndex)-import Data.Maybe (catMaybes, fromJust)+import Data.List (elemIndex, partition)+import Data.Maybe (catMaybes, fromJust, fromMaybe) import Data.Semigroup.Monad import qualified Data.Set as Set import Data.Text.Lazy (fromStrict) import qualified Data.Text.Lazy as Text import Data.Text (unpack) import qualified Data.Text as TextS+import GHC.Stack+ (callStack, prettyCallStack) import qualified System.Console.ANSI as ANSI import System.Console.ANSI ( hSetSGR, SGR(SetConsoleIntensity, SetColor), Color(Magenta)@@ -44,7 +44,10 @@ import Util (OverridingBool(..)) import Clash.Annotations.Primitive- (PrimitiveGuard(HasBlackBox, WarnNonSynthesizable, WarnAlways, DontTranslate))+ (PrimitiveGuard(HasBlackBox, WarnNonSynthesizable, WarnAlways, DontTranslate),+ extractPrim)+import Clash.Annotations.TopEntity+ (TopEntity(Synthesize), PortName(PortName)) import Clash.Core.DataCon as D (dcTag) import Clash.Core.FreeVars (freeIds) import Clash.Core.Literal as L (Literal (..))@@ -53,18 +56,19 @@ import Clash.Core.Pretty (showPpr) import Clash.Core.Subst (extendIdSubst, mkSubst, substTm) import Clash.Core.Term as C- (Term (..), collectArgs, collectArgsTicks)-import Clash.Core.Type as C (Type (..), ConstTy (..),- splitFunTys, splitFunTy)-import Clash.Core.TyCon as C (tyConDataCons)-import Clash.Core.Util (isFun, termType)+ (PrimInfo (..), Term (..), WorkInfo (..), collectArgs, collectArgsTicks)+import Clash.Core.Type as C+ (Type (..), ConstTy (..), TypeView (..), mkFunTy, splitFunTys, splitFunTy, tyView)+import Clash.Core.TyCon as C (TyConMap, tyConDataCons)+import Clash.Core.Util+ (collectBndrs, inverseTopSortLetBindings, isFun, mkApps, splitShouldSplit, termType) import Clash.Core.Var as V (Id, Var (..), mkLocalId, modifyVarName) import Clash.Core.VarEnv (extendInScopeSet, mkInScopeSet, lookupVarEnv, uniqAway, unitVarSet) import {-# SOURCE #-} Clash.Netlist (genComponent, mkDcApplication, mkDeclarations, mkExpr, mkNetDecl,- mkProjection, mkSelection, mkFunApp)+ mkProjection, mkSelection, mkFunApp, mkDeclarations') import qualified Clash.Backend as Backend import Clash.Driver.Types (opt_primWarn, opt_color, ClashOpts)@@ -74,6 +78,8 @@ import Clash.Netlist.Types as N import Clash.Netlist.Util as N import Clash.Primitives.Types as P+import qualified Clash.Primitives.Util as P+import Clash.Signal.Internal (ActiveEdge (..)) import Clash.Unique (lookupUniqMap') import Clash.Util @@ -102,35 +108,59 @@ -- ^ Blackbox function name -> Id -- ^ Identifier binding the primitive/blackbox application- -> [Term]+ -> [Either Term Type] -- ^ Arguments of the primitive/blackbox application -> NetlistMonad (BlackBoxContext,[Declaration])-mkBlackBoxContext bbName resId args = do+mkBlackBoxContext bbName resId args@(lefts -> termArgs) = do -- Make context inputs- tcm <- Lens.use tcCache let resNm = nameOcc (varName resId)- (imps,impDecls) <- unzip <$> mapM (mkArgument resNm) args- (funs,funDecls) <- mapAccumLM (addFunction tcm) IntMap.empty (zip args [0..])+ resTy <- unsafeCoreTypeToHWTypeM' $(curLoc) (V.varType resId)+ (imps,impDecls) <- unzip <$> mapM (mkArgument resNm) termArgs+ (funs,funDecls) <-+ mapAccumLM+ (addFunction (V.varType resId))+ IntMap.empty+ (zip termArgs [0..]) -- Make context result let res = Identifier resNm Nothing- resTy <- unsafeCoreTypeToHWTypeM' $(curLoc) (V.varType resId) lvl <- Lens.use curBBlvl (nm,_) <- Lens.use curCompNm - return ( Context bbName (res,resTy) imps funs [] lvl nm+ -- Set "context name" to value set by `Clash.Magic.setName`, default to the+ -- name of the closest binder+ ctxName1 <- fromMaybe resNm <$> Lens.view setName+ -- Update "context name" with prefixes and suffixes set by+ -- `Clash.Magic.prefixName` and `Clash.Magic.suffixName`+ ctxName2 <- affixName ctxName1++ return ( Context bbName (res,resTy) imps funs [] lvl nm (Just ctxName2) , concat impDecls ++ concat funDecls ) where- addFunction tcm im (arg,i) = if isFun tcm arg- then do curBBlvl Lens.+= 1- (f,d) <- mkFunInput resId arg- curBBlvl Lens.-= 1- let im' = IntMap.insert i f im- return (im',d)- else return (im,[])+ addFunction resTy im (arg,i) = do+ tcm <- Lens.use tcCache+ if isFun tcm arg then do+ -- Only try to calculate function plurality when primitive actually+ -- exists. Here to prevent crashes on __INTERNAL__ primitives.+ prim <- HashMap.lookup bbName <$> Lens.use primitives+ funcPlurality <-+ case extractPrim <$> prim of+ Just (Just p) ->+ P.getFunctionPlurality p args resTy i+ _ ->+ pure 1 + curBBlvl Lens.+= 1+ (fs,ds) <- unzip <$> replicateM funcPlurality (mkFunInput resId arg)+ curBBlvl Lens.-= 1++ let im' = IntMap.insert i fs im+ return (im', concat ds)+ else+ return (im, [])+ prepareBlackBox :: TextS.Text -> BlackBox@@ -156,7 +186,7 @@ isLiteral :: Term -> Bool isLiteral e = case collectArgs e of (Data _, args) -> all (either isLiteral (const True)) args- (Prim _ _, args) -> all (either isLiteral (const True)) args+ (Prim _, args) -> all (either isLiteral (const True)) args (C.Literal _,_) -> True _ -> False @@ -175,9 +205,9 @@ let eTyMsg = "(" ++ showPpr e ++ " :: " ++ showPpr ty ++ ")" ((e',t,l),d) <- case hwTyM of Nothing- | (Prim nm _,_) <- collectArgs e- , nm == "Clash.Transformations.removedArg"- -> return ((Identifier nm Nothing, Void Nothing, False),[])+ | (Prim p,_) <- collectArgs e+ , primName p == "Clash.Transformations.removedArg"+ -> return ((Identifier (primName p) Nothing, Void Nothing, False),[]) | otherwise -> return ((error ($(curLoc) ++ "Forced to evaluate untranslatable type: " ++ eTyMsg), Void Nothing, False), []) Just hwTy -> case collectArgsTicks e of@@ -198,16 +228,16 @@ return ((N.Literal (Just (Unsigned 64,64)) (N.NumLit i),hwTy,True),[]) (C.Literal (NaturalLiteral n), [],_) -> return ((N.Literal (Just (Unsigned iw,iw)) (N.NumLit n),hwTy,True),[])- (Prim f _,args,ticks) -> withTicks ticks $ \tickDecls -> do- (e',d) <- mkPrimitive True False (Left bndr) f args ty tickDecls+ (Prim pinfo,args,ticks) -> withTicks ticks $ \tickDecls -> do+ (e',d) <- mkPrimitive True False (NetlistId bndr ty) pinfo args tickDecls case e' of (Identifier _ _) -> return ((e',hwTy,False), d) _ -> return ((e',hwTy,isLiteral e), d) (Data dc, args,_) -> do- (exprN,dcDecls) <- mkDcApplication hwTy (Left bndr) dc (lefts args)+ (exprN,dcDecls) <- mkDcApplication [hwTy] (NetlistId bndr ty) dc (lefts args) return ((exprN,hwTy,isLiteral e),dcDecls) (Case scrut ty' [alt],[],_) -> do- (projection,decls) <- mkProjection False (Left bndr) scrut ty' alt+ (projection,decls) <- mkProjection False (NetlistId bndr ty) scrut ty' alt return ((projection,hwTy,False),decls) _ -> return ((Identifier (error ($(curLoc) ++ "Forced to evaluate unexpected function argument: " ++ eTyMsg)) Nothing@@ -217,7 +247,8 @@ -- | Extract a compiled primitive from a guarded primitive. Emit a warning if -- the guard wants to, or fail entirely. extractPrimWarnOrFail- :: TextS.Text+ :: HasCallStack+ => TextS.Text -- ^ Name of primitive -> NetlistMonad CompiledPrimitive extractPrimWarnOrFail nm = do@@ -233,6 +264,7 @@ let msg = $(curLoc) ++ "No blackbox found for: " ++ unpack nm ++ ". Did you forget to include directories containing " ++ "primitives? You can use '-i/my/prim/dir' to achieve this."+ ++ (if debugIsOn then "\n\n" ++ prettyCallStack callStack ++ "\n\n" else []) throw (ClashException sp msg Nothing) where go@@ -246,6 +278,7 @@ let msg = $(curLoc) ++ "Clash was forced to translate '" ++ unpack nm ++ "', but this value was marked with DontTranslate. Did you forget" ++ " to include a blackbox for one of the constructs using this?"+ ++ (if debugIsOn then "\n\n" ++ prettyCallStack callStack ++ "\n\n" else []) throw (ClashException sp msg Nothing) go (WarnAlways warning cp) = do@@ -276,27 +309,27 @@ -- ^ Put BlackBox expression in parenthesis -> Bool -- ^ Treat BlackBox expression as declaration- -> Either Identifier Id+ -> NetlistId -- ^ Id to assign the result to- -> TextS.Text- -- ^ Name of primitive+ -> PrimInfo+ -- ^ Primitive info -> [Either Term Type] -- ^ Arguments- -> Type- -- ^ Result type -> [Declaration] -- ^ Tick declarations -> NetlistMonad (Expr,[Declaration])-mkPrimitive bbEParen bbEasD dst nm args ty tickDecls =- go =<< extractPrimWarnOrFail nm+mkPrimitive bbEParen bbEasD dst pInfo args tickDecls =+ go =<< extractPrimWarnOrFail (primName pInfo) where+ ty = head (netlistTypes dst)+ go :: CompiledPrimitive -> NetlistMonad (Expr, [Declaration]) go = \case- P.BlackBoxHaskell bbName wf funcName (_fHash, func) -> do- bbFunRes <- func bbEasD nm args ty+ P.BlackBoxHaskell bbName wf _usedArgs funcName (_fHash, func) -> do+ bbFunRes <- func bbEasD (primName pInfo) args ty case bbFunRes of Left err -> do -- Blackbox template function returned an error:@@ -309,45 +342,68 @@ Right (BlackBoxMeta {..}, bbTemplate) -> -- Blackbox template generation succesful. Rerun 'go', but this time -- around with a 'normal' @BlackBox@- go (P.BlackBox bbName wf bbKind () bbOutputReg bbLibrary bbImports bbIncludes bbTemplate)- p@P.BlackBox {outputReg = wr} ->+ go (P.BlackBox+ bbName wf bbRenderVoid bbKind () bbOutputReg bbLibrary bbImports+ bbFunctionPlurality bbIncludes Nothing Nothing bbTemplate)+ p@P.BlackBox {} -> case kind p of TDecl -> do let tempD = template p pNm = name p- wr' = if wr then Reg else Wire- resM <- resBndr True wr' dst+ resM <- resBndr1 True dst case resM of Just (dst',dstNm,dstDecl) -> do- (bbCtx,ctxDcls) <- mkBlackBoxContext nm dst' (lefts args)+ (bbCtx,ctxDcls) <- mkBlackBoxContext (primName pInfo) dst' args (templ,templDecl) <- prepareBlackBox pNm tempD bbCtx let bbDecl = N.BlackBoxD pNm (libraries p) (imports p) (includes p) templ bbCtx return (Identifier dstNm Nothing,dstDecl ++ ctxDcls ++ templDecl ++ tickDecls ++ [bbDecl])- Nothing -> return (Identifier "__VOID__" Nothing,[])++ -- Render declarations as a Noop when requested+ Nothing | RenderVoid <- renderVoid p -> do+ let dst1 = mkLocalId ty (mkUnsafeSystemName "__VOID_TDECL_NOOP__" 0)+ (bbCtx,ctxDcls) <- mkBlackBoxContext (primName pInfo) dst1 args+ (templ,templDecl) <- prepareBlackBox pNm tempD bbCtx+ let bbDecl = N.BlackBoxD pNm (libraries p) (imports p)+ (includes p) templ bbCtx+ return (Noop, ctxDcls ++ templDecl ++ tickDecls ++ [bbDecl])++ -- Otherwise don't render them+ Nothing -> return (Noop,[]) TExpr -> do let tempE = template p pNm = name p if bbEasD then do- resM <- resBndr True Wire dst+ resM <- resBndr1 True dst case resM of Just (dst',dstNm,dstDecl) -> do- (bbCtx,ctxDcls) <- mkBlackBoxContext nm dst' (lefts args)+ (bbCtx,ctxDcls) <- mkBlackBoxContext (primName pInfo) dst' args (bbTempl,templDecl) <- prepareBlackBox pNm tempE bbCtx let tmpAssgn = Assignment dstNm (BlackBoxE pNm (libraries p) (imports p) (includes p) bbTempl bbCtx bbEParen) return (Identifier dstNm Nothing, dstDecl ++ ctxDcls ++ templDecl ++ [tmpAssgn])- Nothing -> return (Identifier "__VOID__" Nothing,[])++ -- Render expression as a Noop when requested+ Nothing | RenderVoid <- renderVoid p -> do+ let dst1 = mkLocalId ty (mkUnsafeSystemName "__VOID_TEXPRD_NOOP__" 0)+ (bbCtx,ctxDcls) <- mkBlackBoxContext (primName pInfo) dst1 args+ (templ,templDecl) <- prepareBlackBox pNm tempE bbCtx+ let bbDecl = N.BlackBoxD pNm (libraries p) (imports p)+ (includes p) templ bbCtx+ return (Noop, ctxDcls ++ templDecl ++ tickDecls ++ [bbDecl])++ -- Otherwise don't render them+ Nothing -> return (Identifier "__VOID_TEXPRD__" Nothing,[]) else do- resM <- resBndr False Wire dst+ resM <- resBndr1 False dst case resM of Just (dst',_,_) -> do- (bbCtx,ctxDcls) <- mkBlackBoxContext nm dst' (lefts args)+ (bbCtx,ctxDcls) <- mkBlackBoxContext (primName pInfo) dst' args (bbTempl,templDecl0) <- prepareBlackBox pNm tempE bbCtx- let templDecl1 = case nm of+ let templDecl1 = case primName pInfo of "Clash.Sized.Internal.BitVector.fromInteger#" | [N.Literal _ (NumLit _), N.Literal _ _, N.Literal _ _] <- extractLiterals bbCtx -> [] "Clash.Sized.Internal.BitVector.fromInteger##"@@ -360,6 +416,16 @@ | [N.Literal _ (NumLit _), N.Literal _ _] <- extractLiterals bbCtx -> [] _ -> templDecl0 return (BlackBoxE pNm (libraries p) (imports p) (includes p) bbTempl bbCtx bbEParen,ctxDcls ++ templDecl1)+ -- Render expression as a Noop when requested+ Nothing | RenderVoid <- renderVoid p -> do+ let dst1 = mkLocalId ty (mkUnsafeSystemName "__VOID_TEXPRE_NOOP__" 0)+ (bbCtx,ctxDcls) <- mkBlackBoxContext (primName pInfo) dst1 args+ (templ,templDecl) <- prepareBlackBox pNm tempE bbCtx+ let bbDecl = N.BlackBoxD pNm (libraries p) (imports p)+ (includes p) templ bbCtx+ return (Noop, ctxDcls ++ templDecl ++ tickDecls ++ [bbDecl])++ -- Otherwise don't render them Nothing -> return (Identifier "__VOID__" Nothing,[]) P.Primitive pNm _ _ | pNm == "GHC.Prim.tagToEnum#" -> do@@ -369,12 +435,13 @@ tcm <- Lens.use tcCache let dcs = tyConDataCons (tcm `lookupUniqMap'` tcN) dc = dcs !! fromInteger i- (exprN,dcDecls) <- mkDcApplication hwTy dst dc []+ (exprN,dcDecls) <- mkDcApplication [hwTy] dst dc [] return (exprN,dcDecls) [Right _, Left scrut] -> do tcm <- Lens.use tcCache let scrutTy = termType tcm scrut- (scrutExpr,scrutDecls) <- mkExpr False (Left "c$tte_rhs") scrutTy scrut+ (scrutExpr,scrutDecls) <-+ mkExpr False Concurrent (NetlistId "c$tte_rhs" scrutTy) scrut case scrutExpr of Identifier id_ Nothing -> return (DataTag hwTy (Left id_),scrutDecls) _ -> do@@ -392,7 +459,8 @@ tcm <- Lens.use tcCache let scrutTy = termType tcm scrut scrutHTy <- unsafeCoreTypeToHWTypeM' $(curLoc) scrutTy- (scrutExpr,scrutDecls) <- mkExpr False (Left "c$dtt_rhs") scrutTy scrut+ (scrutExpr,scrutDecls) <-+ mkExpr False Concurrent (NetlistId "c$dtt_rhs" scrutTy) scrut case scrutExpr of Identifier id_ Nothing -> return (DataTag scrutHTy (Right id_),scrutDecls) _ -> do@@ -401,37 +469,358 @@ netAssignRhs = Assignment tmpRhs scrutExpr return (DataTag scrutHTy (Right tmpRhs),[netDeclRhs,netAssignRhs] ++ scrutDecls) _ -> error $ $(curLoc) ++ "dataToTag: " ++ show (map (either showPpr showPpr) args)++ | pNm == "Clash.Explicit.SimIO.mealyIO" -> do+ resM <- resBndr1 True dst+ case resM of+ Just (_,dstNm,dstDecl) -> do+ tcm <- Lens.use tcCache+ mealyDecls <- collectMealy dstNm dst tcm (lefts args)+ return (Noop, dstDecl ++ mealyDecls)+ Nothing -> return (Noop,[])++ | pNm == "Clash.Explicit.SimIO.bindSimIO#" ->+ collectBindIO dst (lefts args)++ | pNm == "Clash.Explicit.SimIO.apSimIO#" -> do+ collectAppIO dst (lefts args) []++ | pNm == "Clash.Explicit.SimIO.fmapSimIO#" -> do+ resM <- resBndr1 True dst+ case resM of+ Just (_,dstNm,dstDecl) -> do+ tcm <- Lens.use tcCache+ let (fun0:arg0:_) = lefts args+ arg1 = unSimIO tcm arg0+ fun1 = case fun0 of+ Lam b bE ->+ let is0 = mkInScopeSet (Lens.foldMapOf freeIds unitVarSet fun0)+ subst = extendIdSubst (mkSubst is0) b arg1+ in substTm "mkPrimitive.fmapSimIO" subst bE+ _ -> mkApps fun0 [Left arg1]+ (expr,bindDecls) <- mkExpr False Sequential dst fun1+ let assn = case expr of+ Noop -> []+ _ -> [Assignment dstNm expr]+ return (Identifier dstNm Nothing, dstDecl ++ bindDecls ++ assn)+ Nothing -> do+ let (_:arg0:_) = lefts args+ (_,bindDecls) <- mkExpr True Sequential dst arg0+ return (Noop, bindDecls)++ | pNm == "Clash.Explicit.SimIO.unSimIO#" ->+ mkExpr False Sequential dst (head (lefts args))++ | pNm == "Clash.Explicit.SimIO.pureSimIO#" -> do+ (expr,decls) <- mkExpr False Sequential dst (head (lefts args))+ resM <- resBndr True dst+ case resM of+ Just (_,dstNms,dstDecl) -> case expr of+ Noop ->+ return (Noop,decls)+ _ -> case dstNms of+ [dstNm] ->+ return ( Identifier dstNm Nothing+ , dstDecl ++ decls ++ [Assignment dstNm expr])+ _ -> error "internal error"+ _ ->+ return (Noop,decls)+ | otherwise -> return (BlackBoxE "" [] [] [] (BBTemplate [Text $ mconcat ["NO_TRANSLATION_FOR:",fromStrict pNm]]) (emptyBBContext pNm) False,[]) + -- Do we need to create a new identifier to assign the result?+ --+ -- CoreId: No, this is an original LHS of a let-binder, and already has a+ -- corresponding NetDecl; unlike NetlistIds, it is not already+ -- assigned, it will be assigned by the BlackBox/Primitive.+ --+ -- NetlistId: This is a derived (either from an CoreId or other NetlistId)+ -- identifier created in the NetlistMonad that's already being+ -- used in an assignment, i.e. we cannot assign it again.+ --+ -- So if it is a declaration BlackBox (indicated by 'mkDec'),+ -- we will have to create a new NetlistId, create a NetDecl for+ -- it, and use this new NetlistId for the assignment inside the+ -- declaration BlackBox+ --+ -- MultiId: This is like a CoreId, but it's split over multiple identifiers+ -- because it was originally of a product type where the element+ -- types should not be part of an aggregate type in the generated+ -- HDL (e.g. Clocks should not be part of an aggregate, because+ -- tools like verilator don't like it) resBndr :: Bool- -> WireOrReg- -> (Either Identifier Id)- -> NetlistMonad (Maybe (Id,Identifier,[Declaration]))+ -- Do we need to create and declare a new identifier in case we're given+ -- a NetlistId?+ -> NetlistId+ -- CoreId/NetlistId/MultiId+ -> NetlistMonad (Maybe ([Id],[Identifier],[Declaration])) -- Nothing when the binder would have type `Void`- resBndr mkDec wr dst' = case dst' of- Left dstL -> case mkDec of- False -> do- -- TODO: check that it's okay to use `mkUnsafeSystemName`- let nm' = mkUnsafeSystemName dstL 0- id_ = mkLocalId ty nm'- return (Just (id_,dstL,[]))- True -> do- nm' <- extendIdentifier Extended dstL "_res"- nm'' <- mkUniqueIdentifier Extended nm'- -- TODO: check that it's okay to use `mkUnsafeInternalName`- let nm3 = mkUnsafeSystemName nm'' 0- hwTy <- N.unsafeCoreTypeToHWTypeM' $(curLoc) ty- let id_ = mkLocalId ty nm3- idDecl = NetDecl' Nothing wr nm'' (Right hwTy)- case hwTy of- Void {} -> return Nothing- _ -> return (Just (id_,nm'',[idDecl]))- Right dstR -> return (Just (dstR,nameOcc . varName $ dstR,[]))+ resBndr mkDec dst' = do+ resHwTy <- unsafeCoreTypeToHWTypeM' $(curLoc) ty+ if isVoid resHwTy then+ pure Nothing+ else+ case dst' of+ NetlistId dstL _ -> case mkDec of+ False -> do+ -- TODO: check that it's okay to use `mkUnsafeSystemName`+ let nm' = mkUnsafeSystemName dstL 0+ id_ = mkLocalId ty nm'+ return (Just ([id_],[dstL],[]))+ True -> do+ nm1 <- extendIdentifier Extended dstL "_res"+ nm2 <- mkUniqueIdentifier Extended nm1+ -- TODO: check that it's okay to use `mkUnsafeInternalName`+ let nm3 = mkUnsafeSystemName nm2 0+ id_ = mkLocalId ty nm3+ idDeclM <- mkNetDecl (id_,mkApps (Prim pInfo) args)+ case idDeclM of+ Nothing -> return Nothing+ Just idDecl -> return (Just ([id_],[nm2],[idDecl]))+ CoreId dstR -> return (Just ([dstR],[nameOcc . varName $ dstR],[]))+ MultiId ids -> return (Just (ids,map (nameOcc . varName) ids,[])) + -- Like resBndr, but fails on MultiId+ resBndr1+ :: HasCallStack+ => Bool+ -> NetlistId+ -> NetlistMonad (Maybe (Id,Identifier,[Declaration]))+ resBndr1 mkDec dst' = resBndr mkDec dst' >>= \case+ Nothing -> pure Nothing+ Just ([id_],[nm_],decls) -> pure (Just (id_,nm_,decls))+ _ -> error "internal error"++-- | Turn a 'mealyIO' expression into a two sequential processes, one "initial"+-- process for the starting state, and one clocked sequential process.+collectMealy+ :: HasCallStack+ => Identifier+ -- ^ Identifier to assign the final result to+ -> NetlistId+ -- ^ Id to assign the final result to+ -> TyConMap+ -> [Term]+ -- ^ The arguments to 'mealyIO'+ -> NetlistMonad [Declaration]+collectMealy dstNm dst tcm (kd:clk:mealyFun:mealyInit:mealyIn:_) = do+ let (lefts -> args0,res0) = collectBndrs mealyFun+ is0 = mkInScopeSet (Lens.foldMapOf freeIds unitVarSet res0 <>+ Lens.foldMapOf freeIds unitVarSet mealyInit <>+ Lens.foldMapOf freeIds unitVarSet mealyIn)+ -- Given that we're creating a sequential list of statements from the+ -- let-bindings, make sure that everything is inverse topologically sorted+ (bs,res) = case inverseTopSortLetBindings res0 of+ Letrec bsN (C.Var resN) -> (bsN,resN)+ Letrec bsN e ->+ let u = case dst of+ CoreId u0 -> u0+ _ -> uniqAway is0+ (mkLocalId (termType tcm e)+ (mkUnsafeSystemName "mealyres" 0))+ in (bsN ++ [(u,e)], u)+ e ->+ let u = case dst of+ CoreId u0 -> u0+ _ -> uniqAway is0+ (mkLocalId (termType tcm e)+ (mkUnsafeSystemName "mealyres" 0))+ in ([(u,e)], u)+ -- Drop the 'State# World' argument+ args1 = init args0+ -- Take into account that the state argument is split over multiple+ -- binders because it contained types that are not allowed to occur in+ -- a HDL aggregate type+ mealyInitLength = length (splitShouldSplit tcm [termType tcm mealyInit])+ (sArgs,iArgs) = splitAt mealyInitLength args1+ -- Give all binders a unique name+ normE <- mkUniqueNormalized is0+ (Just (Just (Synthesize "" [] (PortName ""))))+ ([],map (,mealyInit) sArgs ++ map (,mealyIn) iArgs ++ bs,res)+ case normE of+ -- We're not expecting any input or output wrappers+ (_,[],[],_,[],binders0,Just result) -> do+ let (sBinders,binders1) = splitAt (length sArgs) binders0+ (iBinders,binders2) = splitAt (length iArgs) binders1+ -- Get all the "original" let-bindings, without the above "mealyres".+ -- We don't want to make a NetDecl for it.+ bindersN = case res0 of+ Letrec _ (C.Var {}) -> binders2+ _ -> init binders2++ -- Create new net declarations for state-binders, input-binders, and all+ -- the "original" let-bindings in 'mealyFun'+ --+ -- The first set is only assigned in the always block, so they must be+ -- 'reg' in Verilog terminology+ netDeclsSeq <- fmap catMaybes (mapM mkNetDecl (sBinders ++ bindersN))+ -- The second set is assigned using concurrent assignment, so don't need+ -- to be 'reg'+ netDeclsInp <- fmap catMaybes (mapM mkNetDecl iBinders)++ -- If the 'mealyFun' was not a let-expression with a variable reference+ -- as a body then we used the LHS of the entire 'mealyIO' expression as+ -- a new let-binding; otherwise 'mkUniqueNormalized' would not work.+ --+ -- However, 'mkUniqueNormalized' made a new unique name for that LHS,+ -- which is not what we want. We want to assign the last expression to the+ -- LHS of 'mealyIO'.+ let bindersE = case res0 of+ Letrec _ (C.Var {}) -> binders2+ _ -> case dst of+ -- See above why we do this.+ CoreId u0 -> init binders2 ++ [(u0,snd (last binders2))]+ _ -> binders2+ seqDecls <- concat <$> mapM (uncurry (mkDeclarations' Sequential)) bindersE++ -- When the body the let-expression of 'mealyFun' was variable reference,+ -- or in case we had to create a new identifier because the original LHS+ -- was not available: then we need to assign+ (resExpr,resDecls) <- case res0 of+ Letrec _ (C.Var {}) -> mkExpr False Concurrent dst (C.Var result)+ _ -> case dst of+ CoreId {} -> pure (Noop,[])+ _ -> mkExpr False Concurrent dst (C.Var result)+ let resAssn = case resExpr of+ Noop -> []+ _ -> [Seq [AlwaysComb [SeqDecl (Assignment dstNm resExpr)]]]++ -- Create the declarations for the "initial state" block+ let sDst = case sBinders of+ [(b,_)] -> CoreId b+ _ -> MultiId (map fst sBinders)+ (exprInit,initDecls) <- mkExpr False Sequential sDst mealyInit+ let initAssign = case exprInit of+ Identifier _ Nothing -> []+ Noop -> []+ _ -> [Assignment (id2identifier (fst (head sBinders))) exprInit]++ -- Create the declarations that corresponding to the input+ let iDst = case iBinders of+ [(b,_)] -> CoreId b+ _ -> MultiId (map fst iBinders)+ (exprArg,inpDeclsMisc) <- mkExpr False Concurrent iDst mealyIn++ -- Split netdecl declarations and other declarations+ let (netDeclsSeqMisc,seqDeclsOther) = partition isNet (seqDecls ++ resDecls)+ (netDeclsInit,initDeclsOther) = partition isNet initDecls+ -- All assignments happens within a sequential block, so the nets need to+ -- be of type 'reg' in Verilog nomenclature+ let netDeclsSeq1 = map toReg (netDeclsSeq ++ netDeclsSeqMisc ++ netDeclsInit)++ -- We run mealy block in the opposite clock edge of the the ambient system+ -- because we're basically clocked logic; so we need to have our outputs+ -- ready before the ambient system starts sampling them. The clockGen code+ -- ensures that the "opposite" edge always comes first.+ kdTy <- unsafeCoreTypeToHWTypeM $(curLoc) (termType tcm kd)+ let edge = case stripVoid (stripFiltered kdTy) of+ KnownDomain _ _ Rising _ _ _ -> Falling+ KnownDomain _ _ Falling _ _ _ -> Rising+ _ -> error "internal error"+ (clkExpr,clkDecls) <-+ mkExpr False Concurrent (NetlistId "__MEALY_CLK__" (termType tcm clk)) clk++ -- collect the declarations related to the input+ let netDeclsInp1 = netDeclsInp ++ inpDeclsMisc++ -- Collate everything+ return (clkDecls ++ netDeclsSeq1 ++ netDeclsInp1 +++ [ Assignment (id2identifier (fst (head iBinders))) exprArg+ , Seq [Initial (map SeqDecl (initDeclsOther ++ initAssign))]+ , Seq [AlwaysClocked edge clkExpr (map SeqDecl seqDeclsOther)]+ ] ++ resAssn)+ _ -> error "internal error"+ where+ isNet NetDecl' {} = True+ isNet _ = False++ toReg (NetDecl' cmM _ r ty eM) = NetDecl' cmM Reg r ty eM+ toReg d = d++collectMealy _ _ _ _ = error "internal error"++-- | Collect the sequential declarations for 'bindIO'+collectBindIO :: NetlistId -> [Term] -> NetlistMonad (Expr,[Declaration])+collectBindIO dst (m:Lam x q@(Lam _ e):_) = do+ tcm <- Lens.use tcCache+ ds0 <- collectAction tcm+ case splitNormalized tcm q of+ Right (args,bs0,res) -> do+ let Letrec bs _ = inverseTopSortLetBindings (Letrec bs0 (C.Var res))+ let is0 = mkInScopeSet (Lens.foldMapOf freeIds unitVarSet q)+ normE <- mkUniqueNormalized is0 Nothing (args,bs,res)+ case normE of+ (_,_,[],_,[],binders,Just result) -> do+ ds1 <- concat <$> mapM (uncurry (mkDeclarations' Sequential)) binders+ netDecls <- fmap catMaybes (mapM mkNetDecl binders)+ let assn = Assignment (netlistId1 id id2identifier dst)+ (Identifier (id2identifier result) Nothing)+ return (Noop, (netDecls ++ ds0 ++ ds1 ++ [assn]))+ _ -> error "internal error"+ _ -> case e of+ Letrec {} -> error "internal error"+ (collectArgs -> (Prim p,args))+ | primName p == "Clash.Explicit.SimIO.bindSimIO#" -> do+ (expr,ds1) <- collectBindIO dst (lefts args)+ return (expr, ds0 ++ ds1)+ _ -> do+ (expr,ds1) <- mkExpr False Sequential dst e+ return (expr, ds0 ++ ds1)+ where+ collectAction tcm = case splitNormalized tcm m of+ Right (args,bs0,res) -> do+ let Letrec bs _ = inverseTopSortLetBindings (Letrec bs0 (C.Var res))+ let is0 = mkInScopeSet (Lens.foldMapOf freeIds unitVarSet m)+ normE <- mkUniqueNormalized is0 Nothing (args,(x,m):bs,res)+ case normE of+ (_,_,[],_,[],binders,Just result) -> do+ let binders1 = tail binders ++ [(fst (head binders), C.Var result)]+ ds1 <- concat <$> mapM (uncurry (mkDeclarations' Sequential)) binders1+ netDecls <- fmap catMaybes (mapM mkNetDecl binders)+ return (netDecls ++ ds1)+ _ -> error "internal error"+ _ -> do+ netDecls <- fmap catMaybes (mapM mkNetDecl [(x,m)])+ ds1 <- mkDeclarations' Sequential x m+ return (netDecls ++ ds1)++collectBindIO _ es = error ("internal error:\n" ++ showPpr es)++-- | Collect the sequential declarations for 'appIO'+collectAppIO :: NetlistId -> [Term] -> [Term] -> NetlistMonad (Expr,[Declaration])+collectAppIO dst (fun1:arg1:_) rest = case collectArgs fun1 of+ (Prim (PrimInfo "Clash.Explicit.SimIO.fmapSimIO#" _ _),(lefts -> (fun0:arg0:_))) -> do+ tcm <- Lens.use tcCache+ let argN = map (Left . unSimIO tcm) (arg0:arg1:rest)+ mkExpr False Sequential dst (mkApps fun0 argN)+ (Prim (PrimInfo "Clash.Explicit.SimIO.apSimIO#" _ _),(lefts -> args)) -> do+ collectAppIO dst args (arg1:rest)+ _ -> error ("internal error:\n" ++ showPpr (fun1:arg1:rest))+++collectAppIO _ es _ = error ("internal error:\n" ++ showPpr es)++-- | Unwrap the new-type wrapper for things of type SimIO, this is needed to+-- allow applications of the `State# World` token to the underlying IO type.+--+-- XXX: this is most likely needed because Ghc2Core that threw away the cast+-- that this unwrapping; we should really start to support casts.+unSimIO+ :: TyConMap+ -> Term+ -> Term+unSimIO tcm arg =+ let argTy = termType tcm arg+ in case tyView argTy of+ TyConApp _ [tcArg] ->+ mkApps (Prim (PrimInfo "Clash.Explicit.SimIO.unSimIO#" (mkFunTy argTy tcArg) WorkNever))+ [Left arg]+ _ -> error ("internal error:\n" ++ showPpr arg)+ -- | Create an template instantiation text and a partial blackbox content for an -- argument term, given that the term is a function. Errors if the term is not -- a function@@ -455,18 +844,18 @@ tcm <- Lens.use tcCache -- TODO: Rewrite this function to use blackbox functions. Right now it -- TODO: generates strings that are later parsed/interpreted again. Silly!- (bbCtx,dcls) <- mkBlackBoxContext "__INTERNAL__" resId (lefts args)+ (bbCtx,dcls) <- mkBlackBoxContext "__INTERNAL__" resId args templ <- case appE of- Prim nm _ -> do- bb <- extractPrimWarnOrFail nm+ Prim p -> do+ bb <- extractPrimWarnOrFail (primName p) case bb of P.BlackBox {..} ->- pure (Left (kind,outputReg,libraries,imports,includes,nm,template))+ pure (Left (kind,outputReg,libraries,imports,includes,primName p,template)) P.Primitive pn _ pt -> error $ $(curLoc) ++ "Unexpected blackbox type: " ++ "Primitive " ++ show pn ++ " " ++ show pt- P.BlackBoxHaskell pName _workInfo fName (_, func) -> do+ P.BlackBoxHaskell pName _workInfo _usedArgs fName (_, func) -> do -- Determine result type of this blackbox. If it's not a -- function, simply use its term type. let@@ -571,7 +960,7 @@ normalized <- Lens.use bindings case lookupVarEnv fun normalized of Just _ -> do- (wereVoids,_,_,N.Component compName compInps [snd -> compOutp] _) <-+ (wereVoids,_,_,N.Component compName compInps [(_,compOutp,_)] _) <- preserveVarEnv $ genComponent fun let inpAssign (i, t) e' = (Identifier i Nothing, In, t, e')@@ -638,7 +1027,7 @@ goExpr e' = do tcm <- Lens.use tcCache let eType = termType tcm e'- (appExpr,appDecls) <- mkExpr False (Left "c$bb_res") eType e'+ (appExpr,appDecls) <- mkExpr False Concurrent (NetlistId "c$bb_res" eType) e' let assn = Assignment "~RESULT" appExpr nm <- if null appDecls then return ""@@ -660,7 +1049,9 @@ return (Right (("",[assn]),Wire)) go _ _ (Case scrut ty [alt]) = do- (projection,decls) <- mkProjection False (Left "c$bb_res") scrut ty alt+ tcm <- Lens.use tcCache+ let sTy = termType tcm scrut+ (projection,decls) <- mkProjection False (NetlistId "c$bb_res" sTy) scrut ty alt let assn = Assignment "~RESULT" projection nm <- if null decls then return ""@@ -670,7 +1061,7 @@ go _ _ (Case scrut ty alts@(_:_:_)) = do -- TODO: check that it's okay to use `mkUnsafeSystemName` let resId' = resId {varName = mkUnsafeSystemName "~RESULT" 0}- selectionDecls <- mkSelection (Right resId') scrut ty alts []+ selectionDecls <- mkSelection Concurrent (CoreId resId') scrut ty alts [] nm <- mkUniqueIdentifier Basic "selection" tcm <- Lens.use tcCache let scrutTy = termType tcm scrut@@ -692,7 +1083,7 @@ let binders' = map (\(id_,tm) -> (goR result id_,tm)) binders netDecls <- fmap catMaybes . mapM mkNetDecl $ filter ((/= result) . fst) binders decls <- concat <$> mapM (uncurry mkDeclarations) binders'- Just (NetDecl' _ rw _ _) <- mkNetDecl . head $ filter ((==result) . fst) binders+ Just (NetDecl' _ rw _ _ _) <- mkNetDecl . head $ filter ((==result) . fst) binders nm <- mkUniqueIdentifier Basic "fun" return (Right ((nm,netDecls ++ decls),rw)) Nothing -> return (Right (("",[]),Wire))
+ src/Clash/Netlist/BlackBox.hs-boot view
@@ -0,0 +1,29 @@+{-|+ Copyright : (C) 2019, Google Inc+ License : BSD2 (see the file LICENSE)+ Maintainer : QBayLogic B.V. <devops@qbaylogic.com>+-}++module Clash.Netlist.BlackBox where++import Data.Text (Text)+import GHC.Stack (HasCallStack)+import Clash.Core.Term (Term)+import Clash.Core.Type (Type)+import Clash.Core.Var (Id)+import Clash.Netlist.Types (BlackBoxContext, Declaration, NetlistMonad)+import Clash.Primitives.Types (CompiledPrimitive)++extractPrimWarnOrFail+ :: HasCallStack+ => Text+ -> NetlistMonad CompiledPrimitive++mkBlackBoxContext+ :: Text+ -- ^ Blackbox function name+ -> Id+ -- ^ Identifier binding the primitive/blackbox application+ -> [Either Term Type]+ -- ^ Arguments of the primitive/blackbox application+ -> NetlistMonad (BlackBoxContext,[Declaration])
src/Clash/Netlist/BlackBox/Parser.hs view
@@ -55,13 +55,13 @@ pTagD :: Parser Element pTagD = IF <$> (symbol "~IF" *> pTagE) <*> (spaces *> (string "~THEN" *> pBlackBoxD))- <*> (string "~ELSE" *> pBlackBoxD <* string "~FI")+ <*> (string "~ELSE" *> option ([Text ""]) pBlackBoxD <* string "~FI") <|> Component <$> pDecl <|> pTagE -- | Parse a Declaration pDecl :: Parser Decl-pDecl = Decl <$> (symbol "~INST" *> natural') <*>+pDecl = Decl <$> (symbol "~INST" *> natural') <*> pure 0 <*> ((:) <$> pOutput <*> many pInput) <* string "~INST" -- | Parse the output tag of Declaration@@ -82,7 +82,7 @@ <|> Const <$> (string "~CONST" *> brackets' natural') <|> Lit <$> (string "~LIT" *> brackets' natural') <|> Name <$> (string "~NAME" *> brackets' natural')- <|> Var <$> try (string "~VAR" *> brackets' pSigD) <*> brackets' natural'+ <|> ToVar <$> try (string "~VAR" *> brackets' pSigD) <*> brackets' natural' <|> (Sym Text.empty) <$> (string "~SYM" *> brackets' natural') <|> Typ Nothing <$ string "~TYPO" <|> (Typ . Just) <$> try (string "~TYP" *> brackets' natural')@@ -129,6 +129,7 @@ <|> ActiveEdge <$> (string "~ACTIVEEDGE" *> brackets pEdge) <*> brackets' natural' <|> IsSync <$> (string "~ISSYNC" *> brackets' natural') <|> IsInitDefined <$> (string "~ISINITDEFINED" *> brackets' natural')+ <|> CtxName <$ string "~CTXNAME" natural' :: TokenParsing m => m Int natural' = fmap fromInteger natural
src/Clash/Netlist/BlackBox/Types.hs view
@@ -7,8 +7,12 @@ Types used in BlackBox modules -} +{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}+-- since GHC 8.6 we can haddock individual contructor fields \o/+#if __GLASGOW_HASKELL__ >= 806+#define FIELD ^+#endif module Clash.Netlist.BlackBox.Types ( BlackBoxMeta(..)@@ -19,9 +23,11 @@ , Element(..) , Decl(..) , HdlSyn(..)+ , RenderVoid(..) ) where import Control.DeepSeq (NFData)+import Data.Aeson (FromJSON) import Data.Binary (Binary) import Data.Hashable (Hashable) import Data.Text.Lazy (Text)@@ -35,6 +41,15 @@ import qualified Clash.Signal.Internal as Signal +-- | Whether this primitive should be rendered when its result type is void.+-- Defaults to 'NoRenderVoid'.+data RenderVoid+ = RenderVoid+ -- ^ Render blackbox, even if result type is void+ | NoRenderVoid+ -- ^ Don't render blackbox result type is void. Default for all blackboxes.+ deriving (Show, Generic, NFData, Binary, Hashable, FromJSON)+ data TemplateKind = TDecl | TExpr@@ -44,16 +59,18 @@ -- fields. (They are intentionally renamed to prevent name clashes.) data BlackBoxMeta = BlackBoxMeta { bbOutputReg :: Bool- , bbKind :: TemplateKind- , bbLibrary :: [BlackBoxTemplate]- , bbImports :: [BlackBoxTemplate]- , bbIncludes :: [((S.Text, S.Text), BlackBox)]+ , bbKind :: TemplateKind+ , bbLibrary :: [BlackBoxTemplate]+ , bbImports :: [BlackBoxTemplate]+ , bbFunctionPlurality :: [(Int, Int)]+ , bbIncludes :: [((S.Text, S.Text), BlackBox)]+ , bbRenderVoid :: RenderVoid } -- | Use this value in your blackbox template function if you do want to -- accept the defaults as documented in @Clash.Primitives.Types.BlackBox@. emptyBlackBoxMeta :: BlackBoxMeta-emptyBlackBoxMeta = BlackBoxMeta False TExpr [] [] []+emptyBlackBoxMeta = BlackBoxMeta False TExpr [] [] [] [] NoRenderVoid -- | A BlackBox function generates a blackbox template, given the inputs and -- result type of the function it should provide a blackbox for. This is useful@@ -82,7 +99,7 @@ -- - Clash.Netlist.BlackBox.Types.renderElem -- - Clash.Netlist.BlackBox.Types.renderTag -- - Clash.Netlist.BlackBox.Types.setSym--- - Clash.Netlist.BlackBox.Types.usedArguments+-- - Clash.Netlist.BlackBox.Types.getUsedArguments -- - Clash.Netlist.BlackBox.Types.usedVariables -- - Clash.Netlist.BlackBox.Types.verifyBlackBoxContext -- - Clash.Netlist.BlackBox.Types.walkElement@@ -104,7 +121,7 @@ -- ^ Like Arg, but input hole must be a literal | Name !Int -- ^ Name hole- | Var [Element] !Int+ | ToVar [Element] !Int -- ^ Like Arg but only insert variable reference (creating an assignment -- elsewhere if necessary). | Sym !Text !Int@@ -174,6 +191,9 @@ | DevNull [Element] -- ^ Evaluate <hole> but swallow output | SigD [Element] !(Maybe Int)+ | CtxName+ -- ^ The "context name", name set by `Clash.Magic.setName`, defaults to the+ -- name of the closest binder deriving (Show, Generic, NFData, Binary, Hashable) -- | Component instantiation hole. First argument indicates which function argument@@ -183,7 +203,21 @@ -- -- The LHS of the tuple is the name of the signal, while the RHS of the tuple -- is the type of the signal-data Decl = Decl !Int [(BlackBoxTemplate,BlackBoxTemplate)]+data Decl+ = Decl+ !Int+ -- FIELD Argument position of the function to instantiate+ !Int+ -- FIELD Subposition of function: blackboxes can request multiple instances+ -- to be rendered of their given functions. This subposition indicates the+ -- nth function instance to be rendered (zero-indexed).+ --+ -- This is a hack: the proper solution would postpone rendering the+ -- function until the very last moment. The blackbox language has no way+ -- to indicate the subposition, and every ~INST will default its subposition+ -- to zero. Haskell blackboxes can use this data type.+ [(BlackBoxTemplate,BlackBoxTemplate)]+ -- FIELD (name of signal, type of signal) deriving (Show, Generic, NFData, Binary, Hashable) data HdlSyn = Vivado | Quartus | Other
src/Clash/Netlist/BlackBox/Util.hs view
@@ -8,13 +8,12 @@ in templates -} -{-# LANGUAGE CPP #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TemplateHaskell #-} module Clash.Netlist.BlackBox.Util where @@ -31,7 +30,7 @@ #if !MIN_VERSION_base(4,11,0) import Data.Monoid #endif-import Data.Maybe (mapMaybe, maybeToList)+import Data.Maybe (mapMaybe, maybeToList, fromJust) import Data.Semigroup.Monad import qualified Data.Text import Data.Text.Lazy (Text)@@ -44,7 +43,7 @@ import Text.Read (readEither) import Text.Trifecta.Result hiding (Err) -import Clash.Backend (Backend (..), Usage (..))+import Clash.Backend (Backend (..), Usage (..), mkUniqueIdentifier) import qualified Clash.Backend as Backend import Clash.Netlist.BlackBox.Parser import Clash.Netlist.BlackBox.Types@@ -55,17 +54,11 @@ Modifier (..), Declaration(BlackBoxD)) import qualified Clash.Netlist.Types as N-import Clash.Netlist.Util (typeSize)+import Clash.Netlist.Util (typeSize, isVoid, stripVoid) import Clash.Signal.Internal (ResetKind(..), ResetPolarity(..), InitBehavior(..)) import Clash.Util --- | Strip as many "Void" layers as possible. Might still return a Void if the--- void doesn't contain a hwtype.-stripVoid :: HWType -> HWType-stripVoid (Void (Just e)) = stripVoid e-stripVoid e = e- inputHole :: Element -> Maybe Int inputHole = \case Arg _ n -> pure n@@ -102,23 +95,29 @@ case e of Lit n -> case indexMaybe (bbInputs bbCtx) n of- Just (inp, _, False) ->+ Just (inp, isVoid -> False, False) -> Just ( "Argument " ++ show n ++ " should be literal, as blackbox " ++ "used ~LIT[" ++ show n ++ "], but was:\n\n" ++ show inp) _ -> Nothing Const n -> case indexMaybe (bbInputs bbCtx) n of- Just (inp, _, False) ->+ Just (inp, isVoid -> False, False) -> Just ( "Argument " ++ show n ++ " should be literal, as blackbox " ++ "used ~CONST[" ++ show n ++ "], but was:\n\n" ++ show inp) _ -> Nothing- Component (Decl n l') ->+ Component (Decl n subn l') -> case IntMap.lookup n (bbFunctions bbCtx) of- Just _func ->- orElses $- map- (verifyBlackBoxContext bbCtx . N.BBTemplate)- (concatTups l')+ Just funcs ->+ case indexMaybe funcs subn of+ Nothing ->+ Just ( "Blackbox requested at least " ++ show (subn+1)+ ++ " renders of function at argument " ++ show n ++ " but "+ ++ "found only " ++ show (length funcs) )+ Just _ ->+ orElses $+ map+ (verifyBlackBoxContext bbCtx . N.BBTemplate)+ (concatTups l') Nothing -> Just ( "Blackbox requested instantiation of function at argument " ++ show n ++ ", but BlackBoxContext did not contain one.")@@ -162,9 +161,9 @@ m Element setSym' e = case e of- Var nm i | i < length (bbInputs bbCtx) -> case bbInputs bbCtx !! i of+ ToVar nm i | i < length (bbInputs bbCtx) -> case bbInputs bbCtx !! i of (Identifier nm' Nothing,_,_) ->- return (Var [Text (Text.fromStrict nm')] i)+ return (ToVar [Text (Text.fromStrict nm')] i) (e',hwTy,_) -> do varM <- IntMap.lookup i <$> use _2@@ -177,8 +176,8 @@ ,N.Assignment nm' e' ] _2 %= (IntMap.insert i (nm',decls))- return (Var [Text (Text.fromStrict nm')] i)- Just (nm',_) -> return (Var [Text (Text.fromStrict nm')] i)+ return (ToVar [Text (Text.fromStrict nm')] i)+ Just (nm',_) -> return (ToVar [Text (Text.fromStrict nm')] i) Sym _ i -> do symM <- IntMap.lookup i <$> use _1 case symM of@@ -198,8 +197,8 @@ error ("Symbol #" ++ show (t,i) ++ " is already defined in BlackBox for: " ++ bbnm)- Component (Decl n l') ->- Component <$> (Decl n <$> mapM (combineM (mapM setSym') (mapM setSym')) l')+ Component (Decl n subN l') ->+ Component <$> (Decl n subN <$> mapM (combineM (mapM setSym') (mapM setSym')) l') IF c t f -> IF <$> pure c <*> mapM setSym' t <*> mapM setSym' f SigD e' m -> SigD <$> (mapM setSym' e') <*> pure m BV t e' m -> BV <$> pure t <*> mapM setSym' e' <*> pure m@@ -216,8 +215,21 @@ error $ $(curLoc) ++ "Could not convert ~NAME[" ++ show i ++ "]" ++ " to string:" ++ msg ++ "\n\nError occured while " ++ "processing blackbox for " ++ bbnm+ Lit i ->+ case elementToText bbCtx (Lit i) of+ Right t -> t+ Left msg ->+ error $ $(curLoc) ++ "Could not convert ~LIT[" ++ show i ++ "]"+ ++ " to string:" ++ msg ++ "\n\nError occured while "+ ++ "processing blackbox for " ++ bbnm Result _ | Identifier t _ <- fst (bbResult bbCtx) -> Text.fromStrict t CompName -> Text.fromStrict (bbCompName bbCtx)+ CtxName ->+ case bbCtxName bbCtx of+ Just nm -> Text.fromStrict nm+ _ | Identifier t _ <- fst (bbResult bbCtx) -> Text.fromStrict t+ _ -> error $ $(curLoc) ++ "Internal error when processing blackbox "+ ++ "for " ++ bbnm _ -> error $ $(curLoc) ++ "Unexpected element in GENSYM when processing " ++ "blackbox for " ++ bbnm )@@ -285,7 +297,10 @@ bb' <- case bb of N.BBTemplate bt -> do t <- renderTemplate bbNamedCtx bt- return (\col -> PP.nest (col-2) (PP.pretty (t (col + 2))))+ return (\col -> let t1 = t (col + 2)+ in if Text.null t1+ then PP.emptyDoc+ else PP.nest (col-2) (PP.pretty t1)) N.BBFunction _ _ (N.TemplateFunction _ _ bf) -> do t <- bf bbNamedCtx return (\_ -> t)@@ -301,27 +316,32 @@ return bb' -- | Render a single template element-renderElem :: Backend backend- => BlackBoxContext- -> Element- -> State backend (Int -> Text)-renderElem b (Component (Decl n (l:ls))) = do+renderElem+ :: HasCallStack+ => Backend backend+ => BlackBoxContext+ -> Element+ -> State backend (Int -> Text)+renderElem b (Component (Decl n subN (l:ls))) = do (o,oTy,_) <- idToExpr <$> combineM (lineToIdentifier b) (return . lineToType b) l is <- mapM (fmap idToExpr . combineM (lineToIdentifier b) (return . lineToType b)) ls- let Just (templ0,_,libs,imps,inc,pCtx) = IntMap.lookup n (bbFunctions b)+ let func0 = IntMap.lookup n (bbFunctions b)+ errr = concat [ "renderElem: not enough functions rendered? Needed "+ , show (subN +1 ), " got only ", show (length (fromJust func0)) ]+ func1 = indexNote' errr subN <$> func0+ Just (templ0,_,libs,imps,inc,pCtx) = func1 b' = pCtx { bbResult = (o,oTy), bbInputs = bbInputs pCtx ++ is } layoutOptions = LayoutOptions (AvailablePerLine 120 0.4)+ render = N.BBTemplate . parseFail . renderLazy . layoutPretty layoutOptions templ1 <- case templ0 of Left t -> return t- Right (nm,ds) -> do- block <- getMon (blockDecl nm ds)- return $ N.BBTemplate- $ parseFail- $ renderLazy- $ layoutPretty layoutOptions block+ Right (nm0,ds) -> do+ nm1 <- mkUniqueIdentifier Basic nm0+ block <- getMon (blockDecl nm1 ds)+ return (render block) templ4 <- case templ1 of@@ -337,10 +357,7 @@ nm2 <- Backend.mkUniqueIdentifier Basic "bb" let bbD = BlackBoxD nm1 libs imps inc (N.BBTemplate templ3) b' block <- getMon (blockDecl nm2 (templDecls ++ [bbD]))- return $ N.BBTemplate- $ parseFail- $ renderLazy- $ layoutPretty layoutOptions block+ return (render block) case verifyBlackBoxContext b' templ4 of Nothing -> do@@ -568,7 +585,7 @@ Left msg -> error $ $(curLoc) ++ unwords [ "Error when reducing to string" , "in ~NAME construct:", msg ] -renderTag _ (Var [Text t] _) = return t+renderTag _ (ToVar [Text t] _) = return t renderTag _ (Sym t _) = return t renderTag b (BV True es e) = do@@ -649,7 +666,7 @@ Just nm -> return (Text.fromStrict nm) _ -> error $ $(curLoc) ++ "~INCLUDENAME[" ++ show n ++ "] does not correspond to any index of the 'includes' field that is specified in the primitive definition" renderTag b (OutputWireReg n) = case IntMap.lookup n (bbFunctions b) of- Just (_,rw,_,_,_,_) -> case rw of {N.Wire -> return "wire"; N.Reg -> return "reg"}+ Just ((_,rw,_,_,_,_):_) -> case rw of {N.Wire -> return "wire"; N.Reg -> return "reg"} _ -> error $ $(curLoc) ++ "~OUTPUTWIREREG[" ++ show n ++ "] used where argument " ++ show n ++ " is not a function" renderTag b (Repeat [es] [i]) = do i' <- Text.unpack <$> renderTag b i@@ -692,6 +709,13 @@ renderTag b CompName = pure (Text.fromStrict (bbCompName b)) +renderTag b CtxName = case bbCtxName b of+ Just nm -> return (Text.fromStrict nm)+ _ | Identifier t _ <- fst (bbResult b)+ -> return (Text.fromStrict t)+ _ -> error "internal error"++ renderTag _ e = do e' <- getMon (prettyElem e) error $ $(curLoc) ++ "Unable to evaluate: " ++ show e' @@ -735,6 +759,7 @@ exprToString :: Expr -> Maybe String+exprToString (Literal _ (NumLit i)) = Just (show i) exprToString (Literal _ (StringLit l)) = Just l exprToString (BlackBoxE "Clash.Promoted.Symbol.SSymbol" _ _ _ _ ctx _) = let (e',_,_) = head (bbInputs ctx)@@ -749,23 +774,26 @@ -> Mon m Text prettyBlackBox bbT = Text.concat <$> mapM prettyElem bbT -prettyElem :: Monad m- => Element- -> Mon m Text+prettyElem+ :: (HasCallStack, Monad m)+ => Element+ -> Mon m Text prettyElem (Text t) = return t-prettyElem (Component (Decl i args)) = do+prettyElem (Component (Decl i 0 args)) = do args' <- mapM (\(a,b) -> (,) <$> prettyBlackBox a <*> prettyBlackBox b) args renderOneLine <$> (nest 2 (string "~INST" <+> int i <> line <> string "~OUTPUT" <+> string "=>" <+> string (fst (head args')) <+> string (snd (head args')) <+> string "~" <> line <> vcat (mapM (\(a,b) -> string "~INPUT" <+> string "=>" <+> string a <+> string b <+> string "~") (tail args'))) <> line <> string "~INST")+prettyElem (Component (Decl {})) =+ error $ $(curLoc) ++ "prettyElem can't (yet) render ~INST when subfuncion /= 0!" prettyElem (Result b) = if b then return "~ERESULT" else return "~RESULT" prettyElem (Arg b i) = renderOneLine <$> (if b then string "~EARG" else string "~ARG" <> brackets (int i)) prettyElem (Lit i) = renderOneLine <$> (string "~LIT" <> brackets (int i)) prettyElem (Const i) = renderOneLine <$> (string "~CONST" <> brackets (int i)) prettyElem (Name i) = renderOneLine <$> (string "~NAME" <> brackets (int i))-prettyElem (Var es i) = do+prettyElem (ToVar es i) = do es' <- prettyBlackBox es renderOneLine <$> (string "~VAR" <> brackets (string es') <> brackets (int i)) prettyElem (Sym _ i) = renderOneLine <$> (string "~SYM" <> brackets (int i))@@ -882,6 +910,7 @@ renderOneLine <$> (string "~TEMPLATE" <> brackets (string $ Text.concat bbname') <> brackets (string $ Text.concat source'))+prettyElem CtxName = return "~CTXNAME" -- | Recursively walk @Element@, applying @f@ to each element in the tree. walkElement@@ -897,7 +926,7 @@ -- TODO: alternatives. It would probably be better to replace it by Lens -- TODO: logic? case el of- Component (Decl _ args) ->+ Component (Decl _ _ args) -> concatMap (\(a,b) -> concatMap go a ++ concatMap go b) args IndexType e -> go e FilePath e -> go e@@ -916,7 +945,7 @@ Const _ -> [] Lit _ -> [] Name _ -> []- Var es _ -> concatMap go es+ ToVar es _ -> concatMap go es Sym _ _ -> [] Typ _ -> [] TypM _ -> []@@ -948,10 +977,12 @@ Vars _ -> [] Repeat es1 es2 -> concatMap go es1 ++ concatMap go es2+ CtxName -> [] -- | Determine variables used in an expression. Used for VHDL sensitivity list. -- Also see: https://github.com/clash-lang/clash-compiler/issues/365 usedVariables :: Expr -> [Identifier]+usedVariables Noop = [] usedVariables (Identifier i _) = [i] usedVariables (DataCon _ _ es) = concatMap usedVariables es usedVariables (DataTag _ e') = [either id id e']@@ -963,8 +994,8 @@ matchArg (Arg _ i) = Just i matchArg _ = Nothing - matchVar (Var [Text v] _) = Just (Text.toStrict v)- matchVar _ = Nothing+ matchVar (ToVar [Text v] _) = Just (Text.toStrict v)+ matchVar _ = Nothing t' = onBlackBox id (\_ _ _ -> []) t usedIs = mapMaybe (indexMaybe (bbInputs bb)) (concatMap (walkElement matchArg) t')@@ -972,20 +1003,20 @@ sList' = concatMap (walkElement matchVar) t' -- | Collect arguments (e.g., ~ARG, ~LIT) used in this blackbox-usedArguments :: N.BlackBox -> [Int]-usedArguments (N.BBFunction _nm _hsh (N.TemplateFunction k _ _)) = k-usedArguments (N.BBTemplate t) = nub (concatMap (walkElement matchArg) t)+getUsedArguments :: N.BlackBox -> [Int]+getUsedArguments (N.BBFunction _nm _hsh (N.TemplateFunction k _ _)) = k+getUsedArguments (N.BBTemplate t) = nub (concatMap (walkElement matchArg) t) where matchArg = \case Arg _ i -> Just i- Component (Decl i _) -> Just i+ Component (Decl i _ _) -> Just i Const i -> Just i IsLit i -> Just i IsActiveEnable i -> Just i Lit i -> Just i Name i -> Just i- Var _ i -> Just i+ ToVar _ i -> Just i -- Domain properties (only need type): IsInitDefined _ -> Nothing@@ -1030,6 +1061,7 @@ TypElem _ -> Nothing TypM _ -> Nothing Vars _ -> Nothing+ CtxName -> Nothing onBlackBox :: (BlackBoxTemplate -> r)
+ src/Clash/Netlist/BlackBox/Util.hs-boot view
@@ -0,0 +1,19 @@+{-|+ Copyright : (C) 2019, Google Inc+ License : BSD2 (see the file LICENSE)+ Maintainer : QBayLogic B.V. <devops@qbaylogic.com>+-}++module Clash.Netlist.BlackBox.Util where++import Data.Text.Lazy (Text)+import Control.Monad.State (State)+import Clash.Backend (Backend)+import Clash.Netlist.Types (BlackBoxContext)+import Clash.Netlist.BlackBox.Types (BlackBoxTemplate)++renderTemplate+ :: Backend backend+ => BlackBoxContext -- ^ Context used to fill in the hole+ -> BlackBoxTemplate -- ^ Blackbox template+ -> State backend (Int -> Text)
src/Clash/Netlist/Id.hs view
@@ -6,9 +6,8 @@ Transform/format a Netlist Identifier so that it is acceptable as a HDL identifier -} -{-# LANGUAGE CPP #-}+{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ViewPatterns #-} module Clash.Netlist.Id ( IdType (..)
src/Clash/Netlist/Types.hs view
@@ -8,17 +8,14 @@ Type and instance definitions for Netlist modules -} -{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DerivingStrategies #-}-{-# LANGUAGE DeriveLift #-}-{-# LANGUAGE GADTs #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE PatternSynonyms #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TemplateHaskell #-} -- since GHC 8.6 we can haddock individual contructor fields \o/ #if __GLASGOW_HASKELL__ >= 806@@ -53,10 +50,11 @@ import SrcLoc (SrcSpan) +import Clash.Annotations.BitRepresentation (FieldAnn) import Clash.Annotations.TopEntity (TopEntity) import Clash.Backend (Backend) import Clash.Core.Type (Type)-import Clash.Core.Var (Attr')+import Clash.Core.Var (Attr', Id, varType) import Clash.Core.TyCon (TyConMap) import Clash.Core.VarEnv (VarEnv) import Clash.Driver.Types (BindingMap, ClashOpts)@@ -65,11 +63,22 @@ import Clash.Primitives.Types (CompiledPrimMap) import Clash.Signal.Internal (ResetPolarity, ActiveEdge, ResetKind, InitBehavior)-import Clash.Util (makeLenses)+import Clash.Util (HasCallStack, makeLenses) import Clash.Annotations.BitRepresentation.Internal (CustomReprs, DataRepr', ConstrRepr') +-- | Structure describing a top entity: it's id, its port annotations, and+-- associated testbench.+data TopEntityT = TopEntityT+ { topId :: Id+ -- ^ Id of top entity+ , topAnnotation :: Maybe TopEntity+ -- ^ (Maybe) a topentity annotation+ , associatedTestbench :: Maybe Id+ -- ^ (Maybe) a test bench associated with the topentity+ } deriving (Generic)+ -- | Monad that caches generated components (StateT) and remembers hidden inputs -- of components that are being generated (WriterT) newtype NetlistMonad a =@@ -117,12 +126,11 @@ -- filter duplicate warning invocations for dubious blackbox instantiations, -- see GitHub pull request #286. , _componentNames :: VarEnv Identifier- , _topEntityAnns :: VarEnv (Type, Maybe TopEntity)+ , _topEntityAnns :: VarEnv TopEntityT , _hdlDir :: FilePath , _curBBlvl :: Int -- ^ The current scoping level assigned to black box contexts- , _componentPrefix :: (Maybe Identifier,Maybe Identifier)- -- ^ Prefix for top-level components, and prefix for all other components+ , _componentPrefix :: ComponentPrefix , _customReprs :: CustomReprs , _clashOpts :: ClashOpts -- ^ Settings Clash was called with@@ -130,9 +138,21 @@ -- ^ Whether we're compiling a testbench (suppresses some warnings) , _backEndITE :: Bool -- ^ Whether the backend supports ifThenElse expressions+ , _backend :: SomeBackend+ -- ^ The current HDL backend , _htyCache :: HWMap } +data ComponentPrefix+ = ComponentPrefix+ { componentPrefixTop :: Maybe Identifier -- ^ Prefix for top-level components+ , componentPrefixOther :: Maybe Identifier -- ^ Prefix for all other components+ } deriving Show++-- | Existentially quantified backend+data SomeBackend where+ SomeBackend :: Backend backend => backend -> SomeBackend+ -- | Signal reference type Identifier = Text @@ -143,7 +163,7 @@ = Component { componentName :: !Identifier -- ^ Name of the component , inputs :: [(Identifier,HWType)] -- ^ Input ports- , outputs :: [(WireOrReg,(Identifier,HWType))] -- ^ Output ports+ , outputs :: [(WireOrReg,(Identifier,HWType),Maybe Expr)] -- ^ Output ports , declarations :: [Declaration] -- ^ Internal declarations } deriving Show@@ -210,10 +230,15 @@ | CustomSum !Identifier !DataRepr' !Size [(ConstrRepr', Identifier)] -- ^ Same as Sum, but with a user specified bit representation. For more info, -- see: Clash.Annotations.BitRepresentations.+ | CustomProduct !Identifier !DataRepr' !Size (Maybe [Text]) [(FieldAnn, HWType)]+ -- ^ Same as Product, but with a user specified bit representation. For more+ -- info, see: Clash.Annotations.BitRepresentations. | Annotated [Attr'] !HWType -- ^ Annotated with HDL attributes | KnownDomain !Identifier !Integer !ActiveEdge !ResetKind !InitBehavior !ResetPolarity -- ^ Domain name, period, active edge, reset kind, initial value behavior+ | FileType+ -- ^ File type for simulation-level I/O deriving (Eq, Ord, Show, Generic, NFData, Hashable) -- | Extract hardware attributes from Annotated. Returns an empty list if@@ -261,11 +286,37 @@ WireOrReg -- FIELD Wire or register !Identifier -- FIELD Name of signal (Either Identifier HWType) -- FIELD Pointer to type of signal or type of signal+ (Maybe Expr) -- FIELD Initial value -- ^ Signal declaration | TickDecl Comment -- ^ HDL tick corresponding to a Core tick+ -- | Sequential statement+ | Seq [Seq] deriving Show +-- | Sequential statements+data Seq+ -- | Clocked sequential statements+ = AlwaysClocked+ ActiveEdge -- FIELD Edge of the clock the statement should be executed+ Expr -- FIELD Clock expression+ [Seq] -- FIELD Statements to be executed on the active clock edge+ -- | Statements running at simulator start+ | Initial+ [Seq] -- FIELD Statements to run at simulator start+ -- | Statements to run always+ | AlwaysComb+ [Seq] -- FIELD Statements to run always+ -- | Declaration in sequential form+ | SeqDecl+ Declaration -- FIELD The declaration+ -- | Branching statement+ | Branch+ !Expr -- FIELD Scrutinized expresson+ !HWType -- FIELD Type of the scrutinized expression+ [(Maybe Literal,[Seq])] -- FIELD List of: (Maybe match, RHS of Alternative)+ deriving Show+ data EntityOrComponent = Entity | Comp | Empty deriving Show @@ -282,9 +333,9 @@ -> HWType -- ^ Type of signal -> Declaration-pattern NetDecl note d ty <- NetDecl' note Wire d (Right ty)+pattern NetDecl note d ty <- NetDecl' note Wire d (Right ty) _ where- NetDecl note d ty = NetDecl' note Wire d (Right ty)+ NetDecl note d ty = NetDecl' note Wire d (Right ty) Nothing data PortDirection = In | Out deriving (Eq,Ord,Show,Generic,NFData,Hashable)@@ -320,8 +371,13 @@ !Bool -- FIELD Wrap in paretheses? | ConvBV (Maybe Identifier) HWType Bool Expr | IfThenElse Expr Expr Expr+ -- | Do nothing+ | Noop deriving Show +instance NFData Expr where+ rnf x = x `seq` ()+ -- | Literals used in an expression data Literal = NumLit !Integer -- ^ Number literal@@ -354,12 +410,12 @@ { bbName :: Text -- ^ Blackbox function name (for error reporting) , bbResult :: (Expr,HWType) -- ^ Result name and type , bbInputs :: [(Expr,HWType,Bool)] -- ^ Argument names, types, and whether it is a literal- , bbFunctions :: IntMap (Either BlackBox (Identifier,[Declaration])+ , bbFunctions :: IntMap [(Either BlackBox (Identifier,[Declaration]) ,WireOrReg ,[BlackBoxTemplate] ,[BlackBoxTemplate] ,[((Text,Text),BlackBox)]- ,BlackBoxContext)+ ,BlackBoxContext)] -- ^ Function arguments (subset of inputs): -- -- * ( Blackbox Template@@ -373,6 +429,9 @@ -- is equal to the scoping level of this context. , bbCompName :: Identifier -- ^ The component the BlackBox is instantiated in+ , bbCtxName :: Maybe Identifier+ -- ^ The "context name", name set by `Clash.Magic.setName`, defaults to the+ -- name of the closest binder } deriving Show @@ -405,6 +464,70 @@ get = (\is -> TemplateFunction is err err) <$> get where err = const $ error "TemplateFunction functions can't be preserved by serialisation" +-- | Netlist-level identifier+data NetlistId+ = NetlistId Identifier Type+ -- ^ Identifier generated in the NetlistMonad, always derived from another+ -- 'NetlistId'+ | CoreId Id+ -- ^ An original Core identifier+ | MultiId [Id]+ -- ^ A split identifier (into several sub-identifiers), needed to assign+ -- expressions of types that have to be split apart (e.g. tuples of Files)+ deriving Show++-- | Eliminator for 'NetlistId'+netlistId+ :: (Identifier -> r)+ -- ^ Eliminator for Identifiers generated in the NetlistMonad+ -> (Id -> r)+ -- ^ Eliminator for original Core Identifiers+ -> NetlistId+ -> [r]+netlistId f g = \case+ NetlistId i _ -> [f i]+ CoreId i -> [g i]+ MultiId is -> map g is++-- | Eliminator for 'NetlistId', fails on 'MultiId'+netlistId1+ :: HasCallStack+ => (Identifier -> r)+ -- ^ Eliminator for Identifiers generated in the NetlistMonad+ -> (Id -> r)+ -- ^ Eliminator for original Core Identifiers+ -> NetlistId+ -> r+netlistId1 f g = \case+ NetlistId i _ -> f i+ CoreId i -> g i+ m -> error ("netlistId1 MultiId: " ++ show m)++-- | Return the type(s) of a 'NetListId', returns multiple types when given a+-- 'MultiId'+netlistTypes+ :: NetlistId+ -> [Type]+netlistTypes = \case+ NetlistId _ t -> [t]+ CoreId i -> [varType i]+ MultiId is -> map varType is++-- | Return the type of a 'NetlistId', fails on 'MultiId'+netlistTypes1+ :: HasCallStack+ => NetlistId+ -> Type+netlistTypes1 = \case+ NetlistId _ t -> t+ CoreId i -> varType i+ m -> error ("netlistTypes1 MultiId: " ++ show m)++-- | Type of declaration, concurrent or sequential+data DeclarationType+ = Concurrent+ | Sequential+ emptyBBContext :: Text -> BlackBoxContext emptyBBContext n = Context@@ -415,6 +538,7 @@ , bbQsysIncName = [] , bbLevel = (-1) , bbCompName = pack "__NOCOMPNAME__"+ , bbCtxName = Nothing } makeLenses ''NetlistEnv
src/Clash/Netlist/Util.hs view
@@ -8,17 +8,15 @@ Utilities for converting Core Type/Term to Netlist datatypes -} -{-# LANGUAGE CPP #-}-{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleContexts #-} #if !MIN_VERSION_ghc(8,8,0) {-# LANGUAGE MonadFailDesugaring #-} #endif-{-# LANGUAGE MultiWayIf #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-} module Clash.Netlist.Util where @@ -28,6 +26,7 @@ import qualified Control.Lens as Lens import Control.Monad (unless, when, zipWithM, join) import Control.Monad.Reader (ask, local)+import qualified Control.Monad.State as State import Control.Monad.State.Strict (State, evalState, get, modify, runState) import Control.Monad.Trans.Except@@ -36,7 +35,7 @@ import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HashMap import Data.String (fromString)-import Data.List (intersperse, unzip4, sort, intercalate)+import Data.List (intersperse, unzip4, intercalate) import qualified Data.List as List import Data.Maybe (catMaybes,fromMaybe,isNothing) import Data.Monoid (First (..))@@ -46,6 +45,7 @@ #endif import Data.Text (Text) import qualified Data.Text as Text+import Data.Text.Lazy (toStrict) import Data.Text.Prettyprint.Doc (Doc) import Outputable (ppr, showSDocUnsafe)@@ -53,11 +53,12 @@ import Clash.Annotations.BitRepresentation.ClashLib (coreToType') import Clash.Annotations.BitRepresentation.Internal- (CustomReprs, ConstrRepr'(..), DataRepr'(..), getDataRepr, getConstrRepr)+ (CustomReprs, ConstrRepr'(..), DataRepr'(..), getDataRepr,+ uncheckedGetConstrRepr) import Clash.Annotations.TopEntity (PortName (..), TopEntity (..)) import Clash.Driver.Types (Manifest (..), ClashOpts (..)) import Clash.Core.DataCon (DataCon (..))-import Clash.Core.FreeVars (freeLocalIds, typeFreeVars)+import Clash.Core.FreeVars (localIdOccursIn, typeFreeVars) import qualified Clash.Core.Literal as C import Clash.Core.Name (Name (..), appendToName, nameOcc)@@ -66,19 +67,23 @@ (Subst (..), extendIdSubst, extendIdSubstList, extendInScopeId, extendInScopeIdList, mkSubst, substTm) import Clash.Core.Term- (Alt, LetBinding, Pat (..), Term (..), TickInfo (..), NameMod (..))+ (Alt, LetBinding, Pat (..), Term (..), TickInfo (..), NameMod (..),+ collectArgsTicks, collectTicks, PrimInfo(primName)) import Clash.Core.TyCon (TyConName, TyConMap, tyConDataCons) import Clash.Core.Type (Type (..), TypeView (..), coreView1, splitTyConAppM, tyView, TyVar) import Clash.Core.Util- (collectBndrs, stripTicks, substArgTys, termType, tySym)+ (collectBndrs, stripTicks, substArgTys, termType, tyLitShow, mkTicks) import Clash.Core.Var (Id, Var (..), mkLocalId, modifyVarName, Attr') import Clash.Core.VarEnv (InScopeSet, extendInScopeSetList, uniqAway)+import {-# SOURCE #-} Clash.Netlist.BlackBox+import {-# SOURCE #-} Clash.Netlist.BlackBox.Util import Clash.Netlist.Id (IdType (..), stripDollarPrefixes) import Clash.Netlist.Types as HW+import Clash.Primitives.Types import Clash.Unique import Clash.Util @@ -87,9 +92,19 @@ stripFiltered :: FilteredHWType -> HWType stripFiltered (FilteredHWType hwty _filtered) = hwty +-- | Strip as many "Void" layers as possible. Might still return a Void if the+-- void doesn't contain a hwtype.+stripVoid :: HWType -> HWType+stripVoid (Void (Just e)) = stripVoid e+stripVoid e = e+ flattenFiltered :: FilteredHWType -> [[Bool]] flattenFiltered (FilteredHWType _hwty filtered) = map (map fst) filtered +isVoidMaybe :: Bool -> Maybe HWType -> Bool+isVoidMaybe dflt Nothing = dflt+isVoidMaybe _dflt (Just t) = isVoid t+ -- | Determines if type is a zero-width construct ("void") isVoid :: HWType -> Bool isVoid Void {} = True@@ -99,12 +114,6 @@ isFilteredVoid :: FilteredHWType -> Bool isFilteredVoid = isVoid . stripFiltered -isBiSignalOut :: HWType -> Bool-isBiSignalOut (Void (Just (BiDirectional Out _))) = True-isBiSignalOut (Vector n ty) | n /= 0 = isBiSignalOut ty-isBiSignalOut (RTree _ ty) = isBiSignalOut ty-isBiSignalOut _ = False- mkIdentifier :: IdType -> Identifier -> NetlistMonad Identifier mkIdentifier typ nm = Lens.use mkIdentifierFn <*> pure typ <*> pure nm @@ -124,9 +133,9 @@ -> Term -> (Either String ([Id],[LetBinding],Id)) splitNormalized tcm expr = case collectBndrs expr of- (args,Letrec xes e)+ (args, collectTicks -> (Letrec xes e, ticks)) | (tmArgs,[]) <- partitionEithers args -> case stripTicks e of- Var v -> Right (tmArgs,xes,v)+ Var v -> Right (tmArgs, fmap (second (`mkTicks` ticks)) xes,v) _ -> Left ($(curLoc) ++ "Not in normal form: res not simple var") | otherwise -> Left ($(curLoc) ++ "Not in normal form: tyArgs") _ ->@@ -163,7 +172,7 @@ -> State HWMap FilteredHWType unsafeCoreTypeToHWType sp loc builtInTranslation reprs m ty = either (\msg -> throw (ClashException sp (loc ++ msg) Nothing)) id <$>- coreTypeToHWType builtInTranslation reprs m ty+ coreTypeToHWType builtInTranslation reprs m ty -- | Same as @unsafeCoreTypeToHWTypeM@, but discards void filter information unsafeCoreTypeToHWTypeM'@@ -211,83 +220,102 @@ htyCache Lens..= htm1 return (hush hty) -packSP- :: CustomReprs- -> (Text, c)- -> (ConstrRepr', Text, c)-packSP reprs (name, tys) =- case getConstrRepr name reprs of- Just repr -> (repr, name, tys)- Nothing -> error $ $(curLoc) ++ unwords- [ "Could not find custom representation for", Text.unpack name ]+-- | Constructs error message for unexpected projections out of a type annotated+-- with a custom bit representation.+unexpectedProjectionErrorMsg+ :: DataRepr'+ -> Int+ -- ^ Constructor index+ -> Int+ -- ^ Field index+ -> String+unexpectedProjectionErrorMsg dataRepr cI fI =+ "Unexpected projection of zero-width type: " ++ show (drType dataRepr)+ ++ ". Tried to make a projection of field " ++ show fI ++ " of "+ ++ constrNm ++ ". Did you try to project a field marked as zero-width"+ ++ " by a custom bit representation annotation?"+ where+ constrNm = show (crName (drConstrs dataRepr !! cI)) -packSum- :: CustomReprs- -> Text- -> (ConstrRepr', Text)-packSum reprs name =- case getConstrRepr name reprs of- Just repr -> (repr, name)- Nothing -> error $ $(curLoc) ++ unwords- [ "Could not find custom representation for", Text.unpack name ]+-- | Helper function of 'maybeConvertToCustomRepr'+convertToCustomRepr+ :: HasCallStack+ => CustomReprs+ -> DataRepr'+ -> HWType+ -> HWType+convertToCustomRepr reprs dRepr@(DataRepr' name' size constrs) hwTy =+ if length constrs == nConstrs then+ if size <= 0 then+ Void (Just cs)+ else+ cs+ else+ error (unwords+ [ "Type", show name', "has", show nConstrs, "constructor(s), "+ , "but the custom bit representation only specified", show (length constrs)+ , "constructors."+ ])+ where+ cs = insertVoids $ case hwTy of+ Sum name conIds ->+ CustomSum name dRepr size (map packSum conIds)+ SP name conIdsAndFieldTys ->+ CustomSP name dRepr size (map packSP conIdsAndFieldTys)+ Product name maybeFieldNames fieldTys+ | [ConstrRepr' _cName _pos _mask _val fieldAnns] <- constrs ->+ CustomProduct name dRepr size maybeFieldNames (zip fieldAnns fieldTys)+ _ ->+ error+ ( "Found a custom bit representation annotation " ++ show dRepr ++ ", "+ ++ "but it was applied to an unsupported HWType: " ++ show hwTy ++ ".") -fixCustomRepr+ nConstrs :: Int+ nConstrs = case hwTy of+ (Sum _name conIds) -> length conIds+ (SP _name conIdsAndFieldTys) -> length conIdsAndFieldTys+ (Product {}) -> 1+ _ -> error ("Unexpected HWType: " ++ show hwTy)++ packSP (name, tys) = (uncheckedGetConstrRepr name reprs, name, tys)+ packSum name = (uncheckedGetConstrRepr name reprs, name)++ -- Replace some "hwTy" with "Void (Just hwTy)" if the custom bit+ -- representation indicated that field is represented by zero bits. We can't+ -- simply remove them, as we'll later have to deal with an "overapplied"+ -- constructor. If we remove the arguments altogether, we wouldn't know which+ -- - on their own potentially non-void! - arguments to ignore.+ insertVoids :: HWType -> HWType+ insertVoids (CustomSP i d s constrs0) =+ CustomSP i d s (map go0 constrs0)+ where+ go0 (con@(ConstrRepr' _ _ _ _ fieldAnns), i0, hwTys) =+ (con, i0, zipWith go1 fieldAnns hwTys)+ go1 0 hwTy0 = Void (Just hwTy0)+ go1 _ hwTy0 = hwTy0+ insertVoids (CustomProduct i d s f fieldAnns) =+ CustomProduct i d s f (map go fieldAnns)+ where+ go (0, hwTy0) = (0, Void (Just hwTy0))+ go (n, hwTy0) = (n, hwTy0)+ insertVoids hwTy0 = hwTy0++-- | Given a map containing custom bit representation, a type, and the same+-- type represented as HWType, convert the HWType to a CustomSP/CustomSum if+-- it has a custom bit representation.+maybeConvertToCustomRepr :: CustomReprs+ -- ^ Map containing all custom representations index on its type -> Type+ -- ^ Custom reprs are index on type, so we need the clash core type to look+ -- it up. -> HWType+ -- ^ Type of previous argument represented as a HWType -> HWType-fixCustomRepr reprs (coreToType' -> Right tyName) sum_@(Sum name subtys) =- case getDataRepr tyName reprs of- Just dRepr@(DataRepr' name' size constrs) ->- if length constrs == length subtys then- CustomSum- name- dRepr- (fromIntegral size)- [packSum reprs ty | ty <- subtys]- else- error $ $(curLoc) ++ (Text.unpack $ Text.unwords- [ "Type "- , Text.pack $ show name'- , "has"- , Text.pack $ show $ length subtys- , "constructors: \n\n"- , Text.intercalate "\n" $ sort [Text.append " * " id_ | id_ <- subtys]- , "\n\nBut the custom bit representation only specified"- , Text.pack $ show $ length constrs- , "constructors:\n\n"- , Text.intercalate "\n" $ sort [Text.append " * " id_ | (ConstrRepr' id_ _ _ _ _) <- constrs]- ])- Nothing ->- -- No custom representation found- sum_--fixCustomRepr reprs (coreToType' -> Right tyName) sp@(SP name subtys) =- case getDataRepr tyName reprs of- Just dRepr@(DataRepr' name' size constrs) ->- if length constrs == length subtys then- CustomSP- name- dRepr- (fromIntegral size)- [packSP reprs ty | ty <- subtys]- else- error $ $(curLoc) ++ (Text.unpack $ Text.unwords- [ "Type "- , Text.pack $ show $ name'- , "has"- , Text.pack $ show $ length subtys- , "constructors: \n\n"- , Text.intercalate "\n" $ sort [Text.append " * " id_ | (id_, _) <- subtys]- , "\n\nBut the custom bit representation only specified"- , Text.pack $ show $ length constrs, "constructors:\n\n"- , Text.intercalate "\n" $ sort [Text.append " * " id_ | (ConstrRepr' id_ _ _ _ _) <- constrs]- ])- Nothing ->- -- No custom representation found- sp--fixCustomRepr _ _ typ = typ+maybeConvertToCustomRepr reprs (coreToType' -> Right tyName) hwTy+ | Just dRepr <- getDataRepr tyName reprs =+ convertToCustomRepr reprs dRepr hwTy+maybeConvertToCustomRepr _reprs _ty hwTy = hwTy -- | Same as @coreTypeToHWType@, but discards void filter information coreTypeToHWType'@@ -330,14 +358,14 @@ (Either String FilteredHWType) go (Just hwtyE) _ = pure $ (\(FilteredHWType hwty filtered) ->- (FilteredHWType (fixCustomRepr reprs ty hwty) filtered)) <$> hwtyE+ (FilteredHWType (maybeConvertToCustomRepr reprs ty hwty) filtered)) <$> hwtyE -- Strip transparant types: go _ (coreView1 m -> Just ty') = coreTypeToHWType builtInTranslation reprs m ty' -- Try to create hwtype based on AST: go _ (tyView -> TyConApp tc args) = runExceptT $ do FilteredHWType hwty filtered <- mkADT builtInTranslation reprs m (showPpr ty) tc args- return (FilteredHWType (fixCustomRepr reprs ty hwty) filtered)+ return (FilteredHWType (maybeConvertToCustomRepr reprs ty hwty) filtered) -- All methods failed: go _ _ = return $ Left $ "Can't translate non-tycon type: " ++ showPpr ty @@ -434,8 +462,14 @@ -- If none of the dataconstructors have fields, and there are 1 or less -- of them, this type only has one inhabitant. It can therefore be -- represented by zero bits, and is therefore empty:- | length dcs <= 1 ->- return (FilteredHWType (Void Nothing) argHTyss1)+ | length dcs <= 1 -> case argHTyss0 of+ [argHTys0] ->+ -- We need this to preserve constraint-tuples of `KnownDomains`+ let argHTys1 = map (stripVoid . stripFiltered) argHTys0+ in return (FilteredHWType+ (Void (Just (Product tcName Nothing argHTys1)))+ argHTyss1)+ _ -> return (FilteredHWType (Void Nothing) argHTyss1) -- None of the dataconstructors have fields. This type is therefore a -- simple Sum type. | otherwise ->@@ -489,6 +523,7 @@ typeSize :: HWType -> Int typeSize (Void {}) = 0+typeSize FileType = 32 -- (ref. page 287 of IEEE 1364-2005) typeSize String = 0 typeSize Integer = 0 typeSize (KnownDomain {}) = 0@@ -512,6 +547,7 @@ typeSize (BiDirectional Out _) = 0 typeSize (CustomSP _ _ size _) = fromIntegral size typeSize (CustomSum _ _ size _) = fromIntegral size+typeSize (CustomProduct _ _ size _ _) = fromIntegral size typeSize (Annotated _ ty) = typeSize ty -- | Determines the bitsize of the constructor of a type@@ -609,7 +645,8 @@ -- | Uniquely rename all the variables and their references in a normalized -- term mkUniqueNormalized- :: InScopeSet+ :: HasCallStack+ => InScopeSet -> Maybe (Maybe TopEntity) -- ^ Top entity annotation where: --@@ -642,7 +679,7 @@ -- Make arguments unique let is1 = is0 `extendInScopeSetList` (args ++ bndrs)- (wereVoids, iports,iwrappers,substArgs) <- mkUniqueArguments (mkSubst is1) topMM args+ (wereVoids,iports,iwrappers,substArgs) <- mkUniqueArguments (mkSubst is1) topMM args -- Make result unique. This might yield 'Nothing' in which case the result -- was a single BiSignalOut. This is superfluous in the HDL, as the argument@@ -650,35 +687,90 @@ resM <- mkUniqueResult substArgs topMM res case resM of Just (oports,owrappers,res1,substRes) -> do- let usesOutput = concatMap (filter ( == res)- . Lens.toListOf freeLocalIds- ) exprs- -- If the let-binder carrying the result is used in a feedback loop- -- rename the let-binder to "<X>_rec", and assign the "<X>_rec" to- -- "<X>". We do this because output ports in most HDLs cannot be read.- (res2,subst'',extraBndr) <- case usesOutput of- [] -> return (varName res1- ,substRes- ,[] :: [(Id, Term)])- _ -> do- ([res3],substRes') <- mkUnique substRes [modifyVarName (`appendToName` "_rec") res]- return (varName res3,substRes'- ,[(res1, Var res3)])- -- Replace occurences of "<X>" by "<X>_rec"- let resN = varName res- bndrs' = map (\i -> if varName i == resN then modifyVarName (const res2) i else i) bndrs- (bndrsL,r:bndrsR) = break ((== res2).varName) bndrs'- -- Make let-binders unique- (bndrsL',substL) <- mkUnique subst'' bndrsL- (bndrsR',substR) <- mkUnique substL bndrsR+ -- Check whether any of the binders reference the result+ let resRead = any (localIdOccursIn res) exprs+ -- Rename some of the binders, see 'setBinderName' when this happens.+ ((res2,subst1,extraBndr),bndrs1) <-+ mapAccumLM (setBinderName substRes res resRead) (res1,substRes,[]) binds+ -- Make let-binders unique, the result binder is already unique, so we+ -- can skip it.+ let (bndrsL,r:bndrsR) = break ((== res2)) bndrs1+ (bndrsL1,substL) <- mkUnique subst1 bndrsL+ (bndrsR1,substR) <- mkUnique substL bndrsR -- Replace old IDs by updated unique IDs in the RHSs of the let-binders- let exprs' = map (substTm ("mkUniqueNormalized1" :: Doc ()) substR) exprs+ let exprs1 = map (substTm ("mkUniqueNormalized1" :: Doc ()) substR) exprs -- Return the uniquely named arguments, let-binders, and result- return (wereVoids,iports,iwrappers,oports,owrappers,zip (bndrsL' ++ r:bndrsR') exprs' ++ extraBndr,Just res1)+ return ( wereVoids+ , iports+ , iwrappers+ , oports+ , owrappers+ , zip (bndrsL1 ++ r:bndrsR1) exprs1 ++ extraBndr+ , Just res1) Nothing -> do- (bndrs', substArgs') <- mkUnique substArgs bndrs- return (wereVoids,iports,iwrappers,[],[],zip bndrs' (map (substTm ("mkUniqueNormalized2" :: Doc ()) substArgs') exprs),Nothing)+ (bndrs1, substArgs1) <- mkUnique substArgs bndrs+ return ( wereVoids+ , iports+ , iwrappers+ , []+ , []+ , zip bndrs1+ (map (substTm ("mkUniqueNormalized2" :: Doc ()) substArgs1) exprs)+ ,Nothing) +-- | Set the name of the binder+--+-- Normally, it just keeps the existing name, but there are two exceptions:+--+-- 1. It's the binding for the result which is also referenced by another binding;+-- in this case it's suffixed with `_rec`+-- 2. The binding binds a primitive that has a name control field+--+-- 2. takes priority over 1. Additionally, we create an additional binder when+-- the return value gets a new name.+setBinderName+ :: Subst+ -- ^ Current substitution+ -> Id+ -- ^ The binder for the result+ -> Bool+ -- ^ Whether the result binder is referenced by another binder+ -> (Id, Subst, [(Id,Term)])+ -- ^ * The (renamed) binder for the result+ -- * The updated substitution in case the result binder is renamed+ -- * A new binding, to assign the result in case the original binder for+ -- the result got renamed.+ -> (Id,Term)+ -- ^ The binding+ -> NetlistMonad ((Id, Subst, [(Id,Term)]),Id)+setBinderName subst res resRead m@(resN,_,_) (i,collectArgsTicks -> (k,args,ticks)) = case k of+ Prim p -> let nm = primName p in extractPrimWarnOrFail nm >>= go nm+ _ -> goDef+ where+ go nm (BlackBox {resultName = Just (BBTemplate nmD)}) = withTicks ticks $ \_ -> do+ (bbCtx,_) <- preserveVarEnv (mkBlackBoxContext nm i args)+ be <- Lens.use backend+ let bbRetValName = case be of+ SomeBackend s -> toStrict ((State.evalState (renderTemplate bbCtx nmD) s) 0)+ i1 = modifyVarName (\n -> n {nameOcc = bbRetValName}) i+ if res == i1 then do+ ([i2],subst1) <- mkUnique subst [i1]+ return ((i2,subst1,[(resN,Var i2)]),i2)+ else+ return (m,i1)++ go _ _ = goDef++ goDef+ | i == res && resRead+ = do+ ([i1],subst1) <- mkUnique subst [modifyVarName (`appendToName` "_rec") res]+ return ((i1, subst1, [(resN,Var i1)]),i1)+ | i == res+ = return (m,resN)+ | otherwise+ = return (m,i)+ mkUniqueArguments :: Subst -> Maybe (Maybe TopEntity)@@ -851,9 +943,22 @@ seenIds %= HashMap.insert i' 0 return i' --- | Preserve the Netlist '_varCount','_curCompNm','_seenIds' when executing a monadic action-preserveVarEnv :: NetlistMonad a- -> NetlistMonad a+-- | Preserve the complete state before running an action, and restore it+-- afterwards.+preserveState+ :: NetlistMonad a+ -> NetlistMonad a+preserveState action = do+ state <- State.get+ val <- action+ State.put state+ pure val++-- | Preserve the Netlist '_varCount','_curCompNm','_seenIds' when executing+-- a monadic action+preserveVarEnv+ :: NetlistMonad a+ -> NetlistMonad a preserveVarEnv action = do -- store state vCnt <- Lens.use varCount@@ -1068,14 +1173,14 @@ :: Bool -> HashMap Identifier Word -> (IdType -> Identifier -> Identifier)- -> (Maybe Identifier,Maybe Identifier)+ -> ComponentPrefix -> Id -> Identifier genComponentName newInlineStrat seen mkIdFn prefixM nm = let nm' = Text.splitOn (Text.pack ".") (nameOcc (varName nm)) fn = mkIdFn Basic (stripDollarPrefixes (last nm')) fn' = if Text.null fn then Text.pack "Component" else fn- prefix = maybe id (:) (snd prefixM) (if newInlineStrat then [] else init nm')+ prefix = maybe id (:) (componentPrefixOther prefixM) (if newInlineStrat then [] else init nm') nm2 = Text.concat (intersperse (Text.pack "_") (prefix ++ [fn'])) nm3 = mkIdFn Basic nm2 in case HashMap.lookup nm3 seen of@@ -1092,16 +1197,19 @@ genTopComponentName :: Bool -> (IdType -> Identifier -> Identifier)- -> (Maybe Identifier,Maybe Identifier)+ -> ComponentPrefix -> Maybe TopEntity -> Id -> Identifier-genTopComponentName _oldInlineStrat _mkIdFn prefixM (Just ann) _nm =- case prefixM of- (Just p,_) -> p `Text.append` Text.pack ('_':t_name ann)- _ -> Text.pack (t_name ann)-genTopComponentName oldInlineStrat mkIdFn prefixM Nothing nm =- genComponentName oldInlineStrat HashMap.empty mkIdFn prefixM nm+genTopComponentName _newInlineStrat _mkIdFn prefixM (Just ann) _nm =+ case componentPrefixTop prefixM of+ Just p -> p `Text.append` Text.pack ('_':t_name ann)+ _ -> Text.pack (t_name ann)+genTopComponentName newInlineStrat mkIdFn prefixM Nothing nm =+ genComponentName newInlineStrat HashMap.empty mkIdFn prefixM' nm+ where+ -- use the prefix for top-level components+ prefixM' = prefixM{componentPrefixOther = componentPrefixTop prefixM} -- | Strips one or more layers of attributes from a HWType; stops at first@@ -1179,8 +1287,8 @@ pN <- uniquePortName p o return ([(pN,hwty)],[],pN) - go' (PortProduct p ps) (o,hwty) = do- pN <- uniquePortName p o+ go' (PortProduct p ps) (_,hwty) = do+ pN <- mkUniqueIdentifier Basic (Text.pack p) let (attrs, hwty') = stripAttributes hwty case hwty' of Vector sz hwty'' -> do@@ -1205,12 +1313,12 @@ results <- zipWithM appendIdentifier (map (pN,) hwtys) [0..] let ps' = extendPorts $ map (prefixParent p) ps (ports,decls,ids) <- unzip3 <$> uncurry (zipWithM mkOutput') (ps', results)+ let netdecl = NetDecl Nothing pN hwty' case ids of- [i] -> let netdecl = NetDecl Nothing pN hwty'- assign = Assignment i (Identifier pN Nothing)+ [i] -> let assign = Assignment i (Identifier pN Nothing) in return (concat ports,netdecl:assign:concat decls,pN)- _ -> let netdecl = NetDecl Nothing pN hwty'- assigns = zipWith (assignId pN hwty' 0) ids [0..]++ _ -> let assigns = zipWith (assignId pN hwty' 0) ids [0..] in if null attrs then return (concat ports,netdecl:assigns ++ concat decls,pN) else@@ -1292,28 +1400,29 @@ let iResult = inpAssigns ++ concat wrappers result = ("result",snd dstId) - topOutputM <- mkTopOutput- topM- (zip outNames outTys)- (head oPortSupply)- result+ instLabel0 <- extendIdentifier Basic topName ("_" `Text.append` fst dstId)+ instLabel1 <- fromMaybe instLabel0 <$> Lens.view setName+ instLabel2 <- affixName instLabel1+ instLabel3 <- mkUniqueIdentifier Basic instLabel2+ topOutputM <- mkTopOutput topM (zip outNames outTys) (head oPortSupply) result - (iResult ++) <$> case topOutputM of- Nothing -> return []+ let+ topCompDecl oports =+ InstDecl+ Entity+ (Just topName)+ topName+ instLabel3+ []+ ( map (\(p,i,t) -> (Identifier p Nothing,In, t,Identifier i Nothing)) (concat iports) +++ map (\(p,o,t) -> (Identifier p Nothing,Out,t,Identifier o Nothing)) oports)++ case topOutputM of+ Nothing ->+ pure (topCompDecl [] : iResult) Just (_, (oports, unwrappers, idsO)) -> do- instLabel0 <- extendIdentifier Basic topName ("_" `Text.append` fst dstId)- instLabel1 <- mkUniqueIdentifier Basic instLabel0 let outpAssign = Assignment (fst dstId) (resBV topM idsO)- let topCompDecl = InstDecl- Entity- (Just topName)- topName- instLabel1- []- ( map (\(p,i,t) -> (Identifier p Nothing,In, t,Identifier i Nothing)) (concat iports) ++- map (\(p,o,t) -> (Identifier p Nothing,Out,t,Identifier o Nothing)) oports)-- return $ tickDecls ++ (topCompDecl:unwrappers) ++ [outpAssign]+ pure (iResult ++ tickDecls ++ (topCompDecl oports:unwrappers) ++ [outpAssign]) -- | Convert between BitVector for an argument argBV@@ -1439,7 +1548,7 @@ go' (PortName _) ((iN,iTy):inps') (_,hwty) = do iN' <- mkUniqueIdentifier Extended iN return (inps',([(iN,iN',hwty)]- ,[NetDecl' Nothing Wire iN' (Left iTy)]+ ,[NetDecl' Nothing Wire iN' (Left iTy) Nothing] ,Right (iN',hwty))) go' (PortName _) [] _ = error "This shouldnt happen"@@ -1630,7 +1739,7 @@ go' (PortName _) ((oN,oTy):outps') (_,hwty) = do oN' <- mkUniqueIdentifier Extended oN return (outps',([(oN,oN',hwty)]- ,[NetDecl' Nothing Wire oN' (Left oTy)]+ ,[NetDecl' Nothing Wire oN' (Left oTy) Nothing] ,Right (oN',hwty))) go' (PortName _) [] _ = error "This shouldnt happen"@@ -1807,12 +1916,16 @@ where go decls [] = k (reverse decls) + go decls (DeDup:ticks) = go decls ticks++ go decls (NoDeDup:ticks) = go decls ticks+ go decls (SrcSpan sp:ticks) = go (TickDecl (Text.pack (showSDocUnsafe (ppr sp))):decls) ticks go decls (NameMod m nm0:ticks) = do tcm <- Lens.use tcCache- case runExcept (tySym tcm nm0) of+ case runExcept (tyLitShow tcm nm0) of Right nm1 -> local (modName m nm1) (go decls ticks) _ -> go decls ticks @@ -1822,6 +1935,9 @@ modName SuffixName (Text.pack -> s2) env@(NetlistEnv {_suffixName = s1}) | Text.null s1 = env {_suffixName = s2} | otherwise = env {_suffixName = s2 <> "_" <> s1}+ modName SuffixNameP (Text.pack -> s2) env@(NetlistEnv {_suffixName = s1})+ | Text.null s1 = env {_suffixName = s2}+ | otherwise = env {_suffixName = s1 <> "_" <> s2} modName SetName (Text.pack -> s) env = env {_setName = Just s} -- | Add the pre- and suffix names in the current environment to the given
src/Clash/Normalize.hs view
@@ -8,82 +8,83 @@ Turn CoreHW terms into normalized CoreHW Terms -} -{-# LANGUAGE CPP #-}+{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE ViewPatterns #-}-{-# LANGUAGE BangPatterns #-}--{-# OPTIONS_GHC -Wno-unused-imports #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-} module Clash.Normalize where -import Data.Either- import Control.Concurrent.Supply (Supply)-import Control.Lens ((.=),(^.),_1,_4)+import Control.Exception (throw) import qualified Control.Lens as Lens+import Control.Monad (when) import Control.Monad.State.Strict (State)-import Data.Binary (encode)-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as BL-import Data.Either (partitionEithers)+import Data.Default (def)+import Data.Either (lefts,partitionEithers) import qualified Data.IntMap as IntMap import Data.IntMap.Strict (IntMap) import Data.List- (groupBy, intersect, mapAccumL, sortBy)+ (intersect, mapAccumL) import qualified Data.Map as Map import qualified Data.Maybe as Maybe import qualified Data.Set as Set import qualified Data.Set.Lens as Lens-import Data.Semigroup ((<>)) import Data.Text.Prettyprint.Doc (vcat)-import System.IO.Unsafe (unsafePerformIO) import BasicTypes (InlineSpec (..))-import SrcLoc (SrcSpan,noSrcSpan) import Clash.Annotations.BitRepresentation.Internal (CustomReprs)-import Clash.Core.Evaluator (PrimEvaluator)+import Clash.Core.Evaluator.Types (PrimStep, PrimUnwind) import Clash.Core.FreeVars (freeLocalIds, globalIds, globalIdOccursIn, localIdDoesNotOccurIn)-import Clash.Core.Pretty (showPpr, ppr)+import Clash.Core.Pretty (PrettyOptions(..), showPpr, showPpr', ppr) import Clash.Core.Subst- (deShadowTerm, extendGblSubstList, mkSubst, substTm)+ (extendGblSubstList, mkSubst, substTm) import Clash.Core.Term (Term (..), collectArgsTicks) import Clash.Core.Type (Type, splitCoreFunForallTy) import Clash.Core.TyCon (TyConMap, TyConName)-import Clash.Core.Util (mkApps, mkTicks, termType)+import Clash.Core.Type (isPolyTy)+import Clash.Core.Util (mkApps, mkTicks) import Clash.Core.Var (Id, varName, varType) import Clash.Core.VarEnv (VarEnv, elemVarSet, eltsVarEnv, emptyInScopeSet, emptyVarEnv,- extendVarEnv, lookupVarEnv, mapVarEnv, mapMaybeVarEnv, mkInScopeSet,+ extendVarEnv, lookupVarEnv, mapVarEnv, mapMaybeVarEnv, mkVarEnv, mkVarSet, notElemVarEnv, notElemVarSet, nullVarEnv, unionVarEnv) import Clash.Driver.Types- (BindingMap, ClashOpts (..), DebugLevel (..))+ (BindingMap, Binding(..), ClashOpts (..), DebugLevel (..)) import Clash.Netlist.Types- (HWType (..), HWMap, FilteredHWType(..))+ (HWMap, FilteredHWType(..)) import Clash.Netlist.Util- (splitNormalized, coreTypeToHWType')+ (splitNormalized) import Clash.Normalize.Strategy import Clash.Normalize.Transformations- (appProp, bindConstantVar, caseCon, flattenLet, reduceConst, topLet,+ (appPropFast, bindConstantVar, caseCon, flattenLet, reduceConst, topLet, reduceNonRepPrim, removeUnusedExpr) import Clash.Normalize.Types import Clash.Normalize.Util import Clash.Primitives.Types (CompiledPrimMap)-import Clash.Rewrite.Combinators ((>->),(!->))+import Clash.Rewrite.Combinators ((>->),(!->),repeatR,topdownR) import Clash.Rewrite.Types- (RewriteEnv (..), RewriteState (..), bindings, curFun, dbgLevel, extra,- tcCache, topEntities, typeTranslator, customReprs, RewriteStep (..))+ (RewriteEnv (..), RewriteState (..), bindings, dbgLevel, extra,+ tcCache, topEntities) import Clash.Rewrite.Util- (apply, isUntranslatableType, runRewrite, runRewriteSession)-import Clash.Signal.Internal (ResetKind (..))+ (apply, isUntranslatableType, runRewriteSession) import Clash.Util+import Clash.Util.Interpolate (i) +#ifdef HISTORY+import Data.Binary (encode)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as BL++import System.IO.Unsafe (unsafePerformIO)+import Clash.Rewrite.Types (RewriteStep(..))+#endif++ -- | Run a NormalizeSession in a given environment runNormalization :: ClashOpts@@ -100,7 +101,7 @@ -- ^ TyCon cache -> IntMap TyConName -- ^ Tuple TyCon cache- -> PrimEvaluator+ -> (PrimStep, PrimUnwind) -- ^ Hardcoded evaluator (delta-reduction) -> CompiledPrimMap -- ^ Primitive Definitions@@ -116,6 +117,8 @@ where rwEnv = RewriteEnv (opt_dbgLevel opts)+ (opt_dbgTransformations opts)+ (opt_aggressiveXOpt opts) typeTrans tcm tupTcm@@ -146,6 +149,7 @@ rcsMap (opt_newInlineStrat opts) (opt_ultra opts)+ (opt_inlineWFCacheLimit opts) normalize@@ -157,45 +161,74 @@ newNormalized <- normalize (concat new) return (unionVarEnv (mkVarEnv topNormalized) newNormalized) -normalize'- :: Id- -> NormalizeSession ([Id],(Id,(Id,SrcSpan,InlineSpec,Term)))+normalize' :: Id -> NormalizeSession ([Id], (Id, Binding)) normalize' nm = do exprM <- lookupVarEnv nm <$> Lens.use bindings let nmS = showPpr (varName nm) case exprM of- Just (nm',sp,inl,tm) -> do+ Just (Binding nm' sp inl tm) -> do tcm <- Lens.view tcCache- let (_,resTy) = splitCoreFunForallTy tcm (varType nm')+ topEnts <- Lens.view topEntities+ let isTop = nm `elemVarSet` topEnts+ ty0 = varType nm'+ ty1 = if isTop then tvSubstWithTyEq ty0 else ty0++ -- check for polymorphic types+ when (isPolyTy ty1) $+ let msg = $curLoc ++ [i|+ Clash can only normalize monomorphic functions, but this is polymorphic:+ #{showPpr' def{displayUniques=False\} nm'}+ |]+ msgExtra | ty0 == ty1 = Nothing+ | otherwise = Just $ [i|+ Even after applying type equality constraints it remained polymorphic:+ #{showPpr' def{displayUniques=False\} nm'{varType=ty1\}}+ |]+ in throw (ClashException sp msg msgExtra)++ -- check for unrepresentable result type+ let (args,resTy) = splitCoreFunForallTy tcm ty1+ isTopEnt = nm `elemVarSet` topEnts+ isFunction = not $ null $ lefts args resTyRep <- not <$> isUntranslatableType False resTy if resTyRep then do- tmNorm <- normalizeTopLvlBndr nm (nm',sp,inl,tm)- let usedBndrs = Lens.toListOf globalIds (tmNorm ^. _4)+ tmNorm <- normalizeTopLvlBndr isTopEnt nm (Binding nm' sp inl tm)+ let usedBndrs = Lens.toListOf globalIds (bindingTerm tmNorm) traceIf (nm `elem` usedBndrs) (concat [ $(curLoc),"Expr belonging to bndr: ",nmS ," (:: "- , showPpr (varType (tmNorm ^. _1))+ , showPpr (varType (bindingId tmNorm)) , ") remains recursive after normalization:\n"- , showPpr (tmNorm ^. _4) ])+ , showPpr (bindingTerm tmNorm) ]) (return ())- prevNorm <- mapVarEnv (Lens.view _1) <$> Lens.use (extra.normalized)- topEnts <- Lens.view topEntities+ prevNorm <- mapVarEnv bindingId <$> Lens.use (extra.normalized) let toNormalize = filter (`notElemVarSet` topEnts) $ filter (`notElemVarEnv` (extendVarEnv nm nm prevNorm)) usedBndrs return (toNormalize,(nm,tmNorm))- else do- let usedBndrs = Lens.toListOf globalIds tm- prevNorm <- mapVarEnv (Lens.view _1) <$> Lens.use (extra.normalized)- topEnts <- Lens.view topEntities- let toNormalize = filter (`notElemVarSet` topEnts)- $ filter (`notElemVarEnv` (extendVarEnv nm nm prevNorm)) usedBndrs+ else+ do+ -- Throw an error for unrepresentable topEntities and functions+ when (isTopEnt || isFunction) $+ let msg = $(curLoc) ++ [i|+ This bndr has a non-representable return type and can't be normalized:+ #{showPpr' def{displayUniques=False\} nm'}+ |]+ in throw (ClashException sp msg Nothing)++ -- But allow the compilation to proceed for nonrepresentable values.+ -- This can happen for example when GHC decides to create a toplevel binder+ -- for the ByteArray# inside of a Natural constant.+ -- (GHC-8.4 does this with tests/shouldwork/Numbers/Exp.hs)+ -- It will later be inlined by flattenCallTree. lvl <- Lens.view dbgLevel- traceIf (lvl >= DebugFinal)+ traceIf (lvl > DebugNone) (concat [$(curLoc), "Expr belonging to bndr: ", nmS, " (:: " , showPpr (varType nm') , ") has a non-representable return type." , " Not normalising:\n", showPpr tm] )- (return (toNormalize,(nm,(nm',sp,inl,tm))))+ (return ([],(nm,(Binding nm' sp inl tm))))++ Nothing -> error $ $(curLoc) ++ "Expr belonging to bndr: " ++ nmS ++ " not found" -- | Check whether the normalized bindings are non-recursive. Errors when one@@ -206,12 +239,12 @@ -> BindingMap checkNonRecursive norm = case mapMaybeVarEnv go norm of rcs | nullVarEnv rcs -> norm- rcs -> error $ $(curLoc) ++ "Callgraph after normalisation contains following recursive components: "+ rcs -> error $ $(curLoc) ++ "Callgraph after normalization contains following recursive components: " ++ show (vcat [ ppr a <> ppr b | (a,b) <- eltsVarEnv rcs ]) where- go (nm,_,_,tm) =+ go (Binding nm _ _ tm) = if nm `globalIdOccursIn` tm then Just (nm,tm) else Nothing@@ -231,8 +264,12 @@ return (mkVarEnv $ snd $ callTreeToList [] ctFlat) cleanupGraph _ norm = return norm -data CallTree = CLeaf (Id,(Id,SrcSpan,InlineSpec,Term))- | CBranch (Id,(Id,SrcSpan,InlineSpec,Term)) [CallTree]+-- | A tree of identifiers and their bindings, with branches containing+-- additional bindings which are used. See "Clash.Driver.Types.Binding".+--+data CallTree+ = CLeaf (Id, Binding)+ | CBranch (Id, Binding) [CallTree] mkCallTree :: [Id]@@ -244,7 +281,7 @@ -> Maybe CallTree mkCallTree visited bindingMap root | Just rootTm <- lookupVarEnv root bindingMap- = let used = Set.toList $ Lens.setOf globalIds $ (rootTm ^. _4)+ = let used = Set.toList $ Lens.setOf globalIds $ (bindingTerm rootTm) other = Maybe.mapMaybe (mkCallTree (root:visited) bindingMap) (filter (`notElem` visited) used) in case used of [] -> Just (CLeaf (root,rootTm))@@ -273,8 +310,8 @@ flattenNode :: CallTree -> NormalizeSession (Either CallTree ((Id,Term),[CallTree]))-flattenNode c@(CLeaf (_,(_,_,NoInline,_))) = return (Left c)-flattenNode c@(CLeaf (nm,(_,_,_,e))) = do+flattenNode c@(CLeaf (_,(Binding _ _ NoInline _))) = return (Left c)+flattenNode c@(CLeaf (nm,(Binding _ _ _ e))) = do isTopEntity <- elemVarSet nm <$> Lens.view topEntities if isTopEntity then return (Left c) else do tcm <- Lens.view tcCache@@ -287,9 +324,9 @@ return (Right ((nm,mkApps (mkTicks fun ticks) (reverse remainder)),[])) _ -> return (Right ((nm,e),[])) _ -> return (Right ((nm,e),[]))-flattenNode b@(CBranch (_,(_,_,NoInline,_)) _) =+flattenNode b@(CBranch (_,(Binding _ _ NoInline _)) _) = return (Left b)-flattenNode b@(CBranch (nm,(_,_,_,e)) us) = do+flattenNode b@(CBranch (nm,(Binding _ _ _ e)) us) = do isTopEntity <- elemVarSet nm <$> Lens.view topEntities if isTopEntity then return (Left b) else do tcm <- Lens.view tcCache@@ -311,7 +348,7 @@ :: CallTree -> NormalizeSession CallTree flattenCallTree c@(CLeaf _) = return c-flattenCallTree (CBranch (nm,(nm',sp,inl,tm)) used) = do+flattenCallTree (CBranch (nm,(Binding nm' sp inl tm)) used) = do flattenedUsed <- mapM flattenCallTree used (newUsed,il_ct) <- partitionEithers <$> mapM flattenNode flattenedUsed let (toInline,il_used) = unzip il_ct@@ -319,9 +356,7 @@ newExpr <- case toInline of [] -> return tm _ -> do- -- To have a cheap `appProp` transformation we need to- -- deshadow, see also Note [AppProp no-shadow invariant]- let tm1 = deShadowTerm emptyInScopeSet (substTm "flattenCallTree.flattenExpr" subst tm)+ let tm1 = substTm "flattenCallTree.flattenExpr" subst tm #ifdef HISTORY -- NB: When HISTORY is on, emit binary data holding the recorded rewrite steps let !_ = unsafePerformIO@@ -345,34 +380,29 @@ let (toInline',allUsed') = unzip (map goCheap allUsed) subst' = extendGblSubstList (mkSubst emptyInScopeSet) (Maybe.catMaybes toInline')- -- To have a cheap `appProp` transformation we need to- -- deshadow, see also Note [AppProp no-shadow invariant]- let tm1 = deShadowTerm emptyInScopeSet (substTm "flattenCallTree.flattenCheap" subst' newExpr)+ let tm1 = substTm "flattenCallTree.flattenCheap" subst' newExpr newExpr' <- rewriteExpr ("flattenCheap",flatten) (showPpr nm, tm1) (nm', sp)- return (CBranch (nm,(nm',sp,inl,newExpr')) (concat allUsed'))- else return (CBranch (nm,(nm',sp,inl,newExpr)) allUsed)+ return (CBranch (nm,(Binding nm' sp inl newExpr')) (concat allUsed'))+ else return (CBranch (nm,(Binding nm' sp inl newExpr)) allUsed) where flatten =- innerMost (apply "appProp" appProp >->+ repeatR (topdownR (apply "appPropFast" appPropFast >-> apply "bindConstantVar" bindConstantVar >-> apply "caseCon" caseCon >-> apply "reduceConst" reduceConst >-> apply "reduceNonRepPrim" reduceNonRepPrim >-> apply "removeUnusedExpr" removeUnusedExpr >->- apply "flattenLet" flattenLet) !->+ apply "flattenLet" flattenLet)) !-> topdownSucR (apply "topLet" topLet) - goCheap c@(CLeaf (nm2,(_,_,inl2,e)))+ goCheap c@(CLeaf (nm2,(Binding _ _ inl2 e))) | inl2 == NoInline = (Nothing ,[c]) | otherwise = (Just (nm2,e),[])- goCheap c@(CBranch (nm2,(_,_,inl2,e)) us)+ goCheap c@(CBranch (nm2,(Binding _ _ inl2 e)) us) | inl2 == NoInline = (Nothing, [c]) | otherwise = (Just (nm2,e),us) -callTreeToList- :: [Id]- -> CallTree- -> ([Id],[(Id,(Id,SrcSpan,InlineSpec,Term))])+callTreeToList :: [Id] -> CallTree -> ([Id], [(Id, Binding)]) callTreeToList visited (CLeaf (nm,bndr)) | nm `elem` visited = (visited,[]) | otherwise = (nm:visited,[(nm,bndr)])
src/Clash/Normalize/DEC.hs view
@@ -26,13 +26,9 @@ > C -> h x -} -{-# LANGUAGE DeriveFoldable #-}-{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecursiveDo #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE RecursiveDo #-}+{-# LANGUAGE TemplateHaskell #-} module Clash.Normalize.DEC (collectGlobals@@ -62,7 +58,8 @@ (termFreeVars', typeFreeVars', localVarsDoNotOccurIn) import Clash.Core.Literal (Literal (..)) import Clash.Core.Term- (LetBinding, Pat (..), PrimInfo (..), Term (..), collectArgs, collectArgsTicks)+ (LetBinding, Pat (..), PrimInfo (..), Term (..), TickInfo (..), collectArgs,+ collectArgsTicks) import Clash.Core.TyCon (tyConDataCons) import Clash.Core.Type (Type, isPolyFunTy, mkTyConApp, splitFunForallTy) import Clash.Core.Util (mkApps, mkTicks, patIds, termType)@@ -142,19 +139,19 @@ | not eIsconstant = do tcm <- Lens.view tcCache bndrs <- Lens.use bindings- primEval <- Lens.view evaluator+ (primEval, primUnwind) <- Lens.view evaluator gh <- Lens.use globalHeap ids <- Lens.use uniqSupply let (ids1,ids2) = splitSupply ids uniqSupply Lens..= ids2- let eval = (Lens.view Lens._3) . whnf' primEval bndrs tcm gh ids1 inScope False+ let eval = (Lens.view Lens._3) . whnf' primEval primUnwind bndrs tcm gh ids1 inScope False eTy = termType tcm e untran <- isUntranslatableType False eTy case untran of -- Don't lift out non-representable values, because they cannot be let-bound -- in our desired normal form. False -> do- isInteresting <- interestingToLift inScope eval fun args+ isInteresting <- interestingToLift inScope eval fun args ticks case isInteresting of Just fun' | fun' `notElem` seen -> do (args',collected) <- collectGlobalsArgs inScope substitution@@ -183,6 +180,10 @@ ,map (second (second (LB lbs'))) (collected ++ collected') ) +collectGlobals' inScope substitution seen (Tick t e) eIsConstant = do+ (e',collected) <- collectGlobals' inScope substitution seen e eIsConstant+ return (Tick t e',collected)+ collectGlobals' _ _ _ e _ = return (e,[]) -- | Collect 'CaseTree's for (potentially) disjoint applications of globals out@@ -442,17 +443,21 @@ -- ^ Term in function position -> [Either Term Type] -- ^ Arguments+ -> [TickInfo]+ -- ^ Tick annoations -> RewriteMonad extra (Maybe Term)-interestingToLift inScope _ e@(Var v) _ =- if isGlobalId v || v `elemInScopeSet` inScope+interestingToLift inScope _ e@(Var v) _ ticks =+ if NoDeDup `notElem` ticks && (isGlobalId v || v `elemInScopeSet` inScope) then pure (Just e) else pure Nothing-interestingToLift inScope eval e@(Prim nm pInfo) args = do+interestingToLift inScope eval e@(Prim pInfo) args ticks+ | NoDeDup `notElem` ticks = do let anyArgNotConstant = any (not . isConstant) lArgs- case List.lookup nm interestingPrims of+ case List.lookup (primName pInfo) interestingPrims of Just t | t || anyArgNotConstant -> pure (Just e)+ _ | DeDup `elem` ticks -> pure (Just e) _ -> do- let isInteresting = uncurry (interestingToLift inScope eval) . collectArgs+ let isInteresting = uncurry3 (interestingToLift inScope eval) . collectArgsTicks if isHOTy (primType pInfo) then do anyInteresting <- anyM (fmap Maybe.isJust . isInteresting) lArgs if anyInteresting then pure (Just e) else pure Nothing@@ -507,12 +512,12 @@ termIsPow2 e' = case eval e' of Literal (IntegerLiteral n) -> isPow2 n a -> case collectArgs a of- (Prim nm' _,[Right _,Left _,Left (Literal (IntegerLiteral n))])- | isFromInteger nm' -> isPow2 n- (Prim nm' _,[Right _,Left _,Left _,Left (Literal (IntegerLiteral n))])- | nm' == "Clash.Sized.Internal.BitVector.fromInteger#" -> isPow2 n- (Prim nm' _,[Right _, Left _,Left (Literal (IntegerLiteral n))])- | nm' == "Clash.Sized.Internal.BitVector.fromInteger##" -> isPow2 n+ (Prim p,[Right _,Left _,Left (Literal (IntegerLiteral n))])+ | isFromInteger (primName p) -> isPow2 n+ (Prim p,[Right _,Left _,Left _,Left (Literal (IntegerLiteral n))])+ | primName p == "Clash.Sized.Internal.BitVector.fromInteger#" -> isPow2 n+ (Prim p,[Right _, Left _,Left (Literal (IntegerLiteral n))])+ | primName p == "Clash.Sized.Internal.BitVector.fromInteger##" -> isPow2 n _ -> False @@ -527,4 +532,4 @@ isHOTy t = case splitFunForallTy t of (args',_) -> any isPolyFunTy (Either.rights args') -interestingToLift _ _ _ _ = pure Nothing+interestingToLift _ _ _ _ _ = pure Nothing
src/Clash/Normalize/PrimitiveReductions.hs view
@@ -30,10 +30,9 @@ * Clash.Sized.Vector.transpose -} -{-# LANGUAGE CPP #-}+{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE TemplateHaskell #-} module Clash.Normalize.PrimitiveReductions where @@ -49,7 +48,8 @@ import Clash.Core.Name (nameOcc) import Clash.Core.Pretty (showPpr) import Clash.Core.Term- (CoreContext (..), PrimInfo (..), Term (..), WorkInfo (..), Pat (..))+ (CoreContext (..), PrimInfo (..), Term (..), WorkInfo (..), Pat (..),+ collectTermIds) import Clash.Core.Type (LitTy (..), Type (..), TypeView (..), coreView1, mkFunTy, mkTyConApp,@@ -129,8 +129,9 @@ = do uniqs0 <- Lens.use uniqSupply fun1 <- constantPropagation (TransformContext is0 (AppArg Nothing:ctx)) fun- let (uniqs1,(varsL,elemsL)) = second (second concat . unzip)- $ extractElems uniqs0 is0 consCon lhsElTy 'L' n lhsArg+ let is1 = extendInScopeSetList is0 (collectTermIds fun1)+ (uniqs1,(varsL,elemsL)) = second (second concat . unzip)+ $ extractElems uniqs0 is1 consCon lhsElTy 'L' n lhsArg is2 = extendInScopeSetList is0 (map fst elemsL) (uniqs2,(varsR,elemsR)) = second (second concat . unzip) $ extractElems uniqs1 is2 consCon rhsElTy 'R' n rhsArg@@ -165,8 +166,9 @@ = do uniqs0 <- Lens.use uniqSupply fun1 <- constantPropagation (TransformContext is0 (AppArg Nothing:ctx)) fun- let (uniqs1,(vars,elems)) = second (second concat . unzip)- $ extractElems uniqs0 is0 consCon argElTy 'A' n arg+ let is1 = extendInScopeSetList is0 (collectTermIds fun1)+ (uniqs1,(vars,elems)) = second (second concat . unzip)+ $ extractElems uniqs0 is1 consCon argElTy 'A' n arg funApps = map (fun1 `App`) vars lbody = mkVec nilCon consCon resElTy n funApps lb = Letrec (init elems) lbody@@ -198,7 +200,8 @@ = do uniqs0 <- Lens.use uniqSupply fun1 <- constantPropagation (TransformContext is0 (AppArg Nothing:ctx)) fun- let (uniqs1,nTv) = mkUniqSystemTyVar (uniqs0,is0) ("n",typeNatKind)+ let is1 = extendInScopeSetList is0 (collectTermIds fun1)+ (uniqs1,nTv) = mkUniqSystemTyVar (uniqs0,is1) ("n",typeNatKind) (uniqs2,(vars,elems)) = second (second concat . unzip) $ uncurry extractElems uniqs1 consCon argElTy 'I' n arg (Right idxTy:_,_) = splitFunForallTy (termType tcm fun)@@ -209,8 +212,7 @@ (mkTyConApp idxTcNm [VarTy nTv]) [integerPrimTy,integerPrimTy])- idxFromInteger = Prim "Clash.Sized.Internal.Index.fromInteger#"- (PrimInfo idxFromIntegerTy WorkNever)+ idxFromInteger = Prim (PrimInfo "Clash.Sized.Internal.Index.fromInteger#" idxFromIntegerTy WorkNever) idxs = map (App (App (TyApp idxFromInteger (LitTy (NumTy n))) (Literal (IntegerLiteral (toInteger n)))) . Literal . IntegerLiteral . toInteger) [0..(n-1)]@@ -248,11 +250,12 @@ = do uniqs0 <- Lens.use uniqSupply fun1 <- constantPropagation (TransformContext is0 (AppArg Nothing:ctx)) fun- let (Just apDictTc) = lookupUniqMap apDictTcNm tcm+ let is1 = extendInScopeSetList is0 (collectTermIds fun1)+ (Just apDictTc) = lookupUniqMap apDictTcNm tcm [apDictCon] = tyConDataCons apDictTc (Just apDictIdTys) = dataConInstArgTys apDictCon [fTy] (uniqs1,apDictIds@[functorDictId,pureId,apId,_,_]) =- mapAccumR mkUniqInternalId (uniqs0,is0)+ mapAccumR mkUniqInternalId (uniqs0,is1) (zip ["functorDict","pure","ap","apConstL","apConstR"] apDictIdTys) @@ -381,8 +384,9 @@ = do uniqs0 <- Lens.use uniqSupply fun1 <- constantPropagation (TransformContext is0 (AppArg Nothing:ctx)) fun- let (uniqs1,(vars,elems)) = second (second concat . unzip)- $ extractElems uniqs0 is0 consCon aTy 'G' n arg+ let is1 = extendInScopeSetList is0 (collectTermIds fun1)+ (uniqs1,(vars,elems)) = second (second concat . unzip)+ $ extractElems uniqs0 is1 consCon aTy 'G' n arg lbody = foldr (\l r -> mkApps fun1 [Left l,Left r]) start vars lb = Letrec (init elems) lbody uniqSupply Lens..= uniqs1@@ -416,8 +420,9 @@ = do uniqs0 <- Lens.use uniqSupply fun1 <- constantPropagation (TransformContext is0 (AppArg Nothing:ctx)) fun- let (uniqs1,(vars,elems)) = second (second concat . unzip)- $ extractElems uniqs0 is0 consCon aTy 'F' n arg+ let is1 = extendInScopeSetList is0 (collectTermIds fun1)+ (uniqs1,(vars,elems)) = second (second concat . unzip)+ $ extractElems uniqs0 is1 consCon aTy 'F' n arg lbody = foldV fun1 vars lb = Letrec (init elems) lbody uniqSupply Lens..= uniqs1@@ -447,7 +452,7 @@ -- ^ The vector to fold -> NormalizeSession Term reduceDFold _ 0 _ _ start _ = changed start-reduceDFold inScope n aTy fun start arg = do+reduceDFold is0 n aTy fun start arg = do tcm <- Lens.view tcCache let ty = termType tcm arg go tcm ty@@ -459,8 +464,11 @@ , [_,consCon] <- tyConDataCons vecTc = do uniqs0 <- Lens.use uniqSupply- let (uniqs1,(vars,elems)) = second (second concat . unzip)- $ extractElems uniqs0 inScope consCon aTy 'D' n arg+ let is1 = extendInScopeSetList is0 (collectTermIds fun)+ -- TODO: Should 'constantPropagation' be used on 'fun'? It seems to+ -- TOOD: be used for every other function in this module.+ (uniqs1,(vars,elems)) = second (second concat . unzip)+ $ extractElems uniqs0 is1 consCon aTy 'D' n arg (_ltv:Right snTy:_,_) = splitFunForallTy (termType tcm fun) (TyConApp snatTcNm _) = tyView snTy (Just snatTc) = lookupUniqMap snatTcNm tcm@@ -769,8 +777,7 @@ -> Type -> Term eqIntPrim intTy boolTy =- Prim "Clash.Transformations.eqInt"- (PrimInfo (mkFunTy intTy (mkFunTy intTy boolTy)) WorkVariable)+ Prim (PrimInfo "Clash.Transformations.eqInt" (mkFunTy intTy (mkFunTy intTy boolTy)) WorkVariable) go tcm (coreView1 tcm -> Just ty') = go tcm ty' go tcm (tyView -> TyConApp vecTcNm _)@@ -870,8 +877,7 @@ -> Type -> Term eqIntPrim intTy boolTy =- Prim "Clash.Transformations.eqInt"- (PrimInfo (mkFunTy intTy (mkFunTy intTy boolTy)) WorkVariable)+ Prim (PrimInfo "Clash.Transformations.eqInt" (mkFunTy intTy (mkFunTy intTy boolTy)) WorkVariable) go tcm (coreView1 tcm -> Just ty') = go tcm ty' go tcm (tyView -> TyConApp vecTcNm _)
src/Clash/Normalize/Strategy.hs view
@@ -28,10 +28,11 @@ -- | Normalisation transformation normalization :: NormRewrite-normalization = rmDeadcode >-> constantPropagation >-> etaTL >-> rmUnusedExpr >-!-> anf >-!-> rmDeadcode >->- bindConst >-> letTL >-> evalConst >-!-> cse >-!-> cleanup >-> recLetRec+normalization = rmDeadcode >-> constantPropagation >-> rmUnusedExpr >-!-> anf >-!-> rmDeadcode >->+ bindConst >-> letTL >-> evalConst >-!-> cse >-!-> cleanup >->+ xOptim >-> rmDeadcode >->+ cleanup >-> recLetRec >-> splitArgs where- etaTL = apply "etaTL" etaExpansionTL !-> topdownR (apply "applicationPropagation" appPropFast) anf = topdownR (apply "nonRepANF" nonRepANF) >-> apply "ANF" makeANF >-> topdownR (apply "caseCon" caseCon) letTL = topdownSucR (apply "topLet" topLet) recLetRec = apply "recToLetRec" recToLetRec@@ -41,19 +42,23 @@ -- See [Note] bottomup traversal evalConst: evalConst = bottomupR (apply "evalConst" reduceConst) cse = topdownR (apply "CSE" simpleCSE)+ xOptim = bottomupR (apply "xOptimize" xOptimize) cleanup = topdownR (apply "etaExpandSyn" etaExpandSyn) >-> topdownSucR (apply "inlineCleanup" inlineCleanup) !-> innerMost (applyMany [("caseCon" , caseCon) ,("bindConstantVar", bindConstantVar) ,("letFlat" , flattenLet)]) >-> rmDeadcode >-> letTL+ splitArgs = topdownR (apply "separateArguments" separateArguments) !->+ topdownR (apply "caseCon" caseCon) constantPropagation :: NormRewrite constantPropagation = inlineAndPropagate >->- caseFlattening >-> dec >-> spec >-> dec >->+ caseFlattening >-> etaTL >-> dec >-> spec >-> dec >-> conSpec where+ etaTL = apply "etaTL" etaExpansionTL !-> topdownR (apply "applicationPropagation" appPropFast) inlineAndPropagate = repeatR (topdownR (applyMany transPropagateAndInline) >-> inlineNR) spec = bottomupR (applyMany specTransformations) caseFlattening = repeatR (topdownR (apply "caseFlat" caseFlat))
src/Clash/Normalize/Transformations.hs view
@@ -8,2308 +8,2867 @@ Transformations of the Normalization process -} -{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE MagicHash #-}-{-# LANGUAGE MultiWayIf #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE ViewPatterns #-}--module Clash.Normalize.Transformations- ( appProp- , caseLet- , caseCon- , caseCase- , caseElemNonReachable- , elemExistentials- , inlineNonRep- , inlineOrLiftNonRep- , typeSpec- , nonRepSpec- , etaExpansionTL- , nonRepANF- , bindConstantVar- , constantSpec- , makeANF- , deadCode- , topLet- , recToLetRec- , inlineWorkFree- , inlineHO- , inlineSmall- , simpleCSE- , reduceConst- , reduceNonRepPrim- , caseFlat- , disjointExpressionConsolidation- , removeUnusedExpr- , inlineCleanup- , flattenLet- , splitCastWork- , inlineCast- , caseCast- , letCast- , eliminateCastCast- , argCastSpec- , etaExpandSyn- , appPropFast- )-where--import Control.Concurrent.Supply (splitSupply)-import Control.Exception (throw)-import Control.Lens (_2)-import qualified Control.Lens as Lens-import qualified Control.Monad as Monad-import Control.Monad.State (StateT (..), modify)-import Control.Monad.State.Strict (evalState)-import Control.Monad.Writer (lift, listen)-import Control.Monad.Trans.Except (runExcept)-import Data.Bits ((.&.), complement)-import Data.Coerce (coerce)-import qualified Data.Either as Either-import qualified Data.HashMap.Lazy as HashMap-import qualified Data.HashMap.Strict as HashMapS-import qualified Data.List as List-import qualified Data.Maybe as Maybe-import qualified Data.Monoid as Monoid-import qualified Data.Primitive.ByteArray as BA-import qualified Data.Text as Text-import qualified Data.Vector.Primitive as PV-import Debug.Trace (trace)-import GHC.Integer.GMP.Internals (Integer (..), BigNat (..))--import BasicTypes (InlineSpec (..))--import Clash.Annotations.Primitive (extractPrim)-import Clash.Core.DataCon (DataCon (..))-import Clash.Core.Evaluator (PureHeap, whnf')-import Clash.Core.Name- (Name (..), NameSort (..), mkUnsafeSystemName)-import Clash.Core.FreeVars- (localIdOccursIn, localIdsDoNotOccurIn, freeLocalIds, termFreeTyVars, typeFreeVars, localVarsDoNotOccurIn)-import Clash.Core.Literal (Literal (..))-import Clash.Core.Pretty (showPpr)-import Clash.Core.Subst- (substTm, mkSubst, extendIdSubst, extendIdSubstList, extendTvSubst,- extendTvSubstList, freshenTm, substTyInVar, deShadowTerm)-import Clash.Core.Term- (LetBinding, Pat (..), Term (..), CoreContext (..), PrimInfo (..), TickInfo,- isLambdaBodyCtx, isTickCtx, collectArgs, collectArgsTicks, collectTicks,- partitionTicks)-import Clash.Core.Type (Type, TypeView (..), applyFunTy,- isPolyFunCoreTy, isClassTy,- normalizeType, splitFunForallTy,- splitFunTy,- tyView)-import Clash.Core.TyCon (TyConMap, tyConDataCons)-import Clash.Core.Util- (isCon, isFun, isLet, isPolyFun, isPrim,- isSignalType, isVar, mkApps, mkLams, mkVec, piResultTy, termSize, termType,- tyNatSize, patVars, isAbsurdAlt, altEqs, substInExistentialsList,- solveNonAbsurds, patIds, isLocalVar, undefinedTm, stripTicks, mkTicks)-import Clash.Core.Var- (Id, Var (..), isGlobalId, isLocalId, mkLocalId)-import Clash.Core.VarEnv- (InScopeSet, VarEnv, VarSet, elemVarSet,- emptyVarEnv, emptyVarSet, extendInScopeSet, extendInScopeSetList, lookupVarEnv,- notElemVarSet, unionVarEnvWith, unionVarSet, unionInScope, unitVarEnv,- unitVarSet, mkVarSet, mkInScopeSet, uniqAway)-import Clash.Driver.Types (DebugLevel (..))-import Clash.Netlist.BlackBox.Util (usedArguments)-import Clash.Netlist.Types (HWType (..), FilteredHWType(..))-import Clash.Netlist.Util- (coreTypeToHWType, representableType, splitNormalized, bindsExistentials)-import Clash.Normalize.DEC-import Clash.Normalize.PrimitiveReductions-import Clash.Normalize.Types-import Clash.Normalize.Util-import Clash.Primitives.Types- (Primitive(..), TemplateKind(TExpr), CompiledPrimMap)-import Clash.Rewrite.Combinators-import Clash.Rewrite.Types-import Clash.Rewrite.Util-import Clash.Unique- (Unique, lookupUniqMap, toListUniqMap)-import Clash.Util--inlineOrLiftNonRep :: HasCallStack => NormRewrite-inlineOrLiftNonRep = inlineOrLiftBinders nonRepTest inlineTest- where- nonRepTest :: (Id, Term) -> RewriteMonad extra Bool- nonRepTest (Id {varType = ty}, _)- = not <$> (representableType <$> Lens.view typeTranslator- <*> Lens.view customReprs- <*> pure False- <*> Lens.view tcCache- <*> pure ty)- nonRepTest _ = return False-- inlineTest :: Term -> (Id, Term) -> RewriteMonad extra Bool- inlineTest e (id_, e')- = not . or <$> sequence -- We do __NOT__ inline:- [ -- 1. recursive let-binders- pure (id_ `localIdOccursIn` e')- -- 2. join points (which are not void-wrappers)- , pure (isJoinPointIn id_ e && not (isVoidWrapper e'))- -- 3. binders that are used more than once in the body, because- -- it makes CSE a whole lot more difficult.- , pure (freeOccurances > 1)- ]- where- -- The number of free occurrences of the binder in the entire- -- let-expression- freeOccurances :: Int- freeOccurances = case e of- Letrec _ res -> do- Monoid.getSum- (Lens.foldMapOf freeLocalIds- (\i -> if i == id_- then Monoid.Sum 1- else Monoid.Sum 0)- res)- _ -> 0--{- [Note] join points and void wrappers-Join points are functions that only occur in tail-call positions within an-expression, and only when they occur in a tail-call position more than once.--Normally bindNonRep binds/inlines all non-recursive local functions. However,-doing so for join points would significantly increase compilation time, so we-avoid it. The only exception to this rule are so-called void wrappers. Void-wrappers are functions of the form:--> \(w :: Void) -> f a b c--i.e. a wrapper around the function 'f' where the argument 'w' is not used. We-do bind/line these join-points because these void-wrappers interfere with the-'disjoint expression consolidation' (DEC) and 'common sub-expression elimination'-(CSE) transformation, sometimes resulting in circuits that are twice as big-as they'd need to be.--}---- | Specialize functions on their type-typeSpec :: HasCallStack => NormRewrite-typeSpec ctx e@(TyApp e1 ty)- | (Var {}, args) <- collectArgs e1- , null $ Lens.toListOf typeFreeVars ty- , (_, []) <- Either.partitionEithers args- = specializeNorm ctx e--typeSpec _ e = return e---- | Specialize functions on their non-representable argument-nonRepSpec :: HasCallStack => NormRewrite-nonRepSpec ctx@(TransformContext is0 _) e@(App e1 e2)- | (Var {}, args) <- collectArgs e1- , (_, []) <- Either.partitionEithers args- , null $ Lens.toListOf termFreeTyVars e2- = do tcm <- Lens.view tcCache- let e2Ty = termType tcm e2- let localVar = isLocalVar e2- nonRepE2 <- not <$> (representableType <$> Lens.view typeTranslator- <*> Lens.view customReprs- <*> pure False- <*> Lens.view tcCache- <*> pure e2Ty)- if nonRepE2 && not localVar- then do- e2' <- inlineInternalSpecialisationArgument e2- specializeNorm ctx (App e1 e2')- else return e- where- -- | If the argument on which we're specialising ia an internal function,- -- one created by the compiler, then inline that function before we- -- specialise.- --- -- We need to do this because otherwise the specialisation history won't- -- recognize the new specialisation argument as something the function has- -- already been specialized on- inlineInternalSpecialisationArgument- :: Term- -> NormalizeSession Term- inlineInternalSpecialisationArgument app- | (Var f,fArgs,ticks) <- collectArgsTicks app- = do- fTmM <- lookupVarEnv f <$> Lens.use bindings- case fTmM of- Just (fNm,_,_,tm)- | nameSort (varName fNm) == Internal- -> do- tm' <- censor (const mempty)- (bottomupR appProp ctx- (mkApps (mkTicks tm ticks) fArgs))- -- See Note [AppProp no-shadow invariant]- return (deShadowTerm is0 tm')- _ -> return app- | otherwise = return app--nonRepSpec _ e = return e---- | Lift the let-bindings out of the subject of a Case-decomposition-caseLet :: HasCallStack => NormRewrite-caseLet _ (Case (Letrec xes e) ty alts) =- changed (Letrec xes (Case e ty alts))--caseLet _ e = return e---- | Remove non-reachable alternatives. For example, consider:------ data STy ty where--- SInt :: Int -> STy Int--- SBool :: Bool -> STy Bool------ f :: STy ty -> ty--- f (SInt b) = b + 1--- f (SBool True) = False--- f (SBool False) = True--- {-# NOINLINE f #-}------ g :: STy Int -> Int--- g = f------ @f@ is always specialized on @STy Int@. The SBool alternatives are therefore--- unreachable. Additional information can be found at:--- https://github.com/clash-lang/clash-compiler/pull/465-caseElemNonReachable :: HasCallStack => NormRewrite-caseElemNonReachable _ case0@(Case scrut altsTy alts0) = do- tcm <- Lens.view tcCache-- let (altsAbsurd, altsOther) = List.partition (isAbsurdAlt tcm) alts0- case altsAbsurd of- [] -> return case0- _ -> changed =<< caseOneAlt (Case scrut altsTy altsOther)--caseElemNonReachable _ e = return e---- | Tries to eliminate existentials by using heuristics to determine what the--- existential should be. For example, consider Vec:------ data Vec :: Nat -> Type -> Type where--- Nil :: Vec 0 a--- Cons x xs :: a -> Vec n a -> Vec (n + 1) a------ Thus, 'null' (annotated with existentials) could look like:------ null :: forall n . Vec n Bool -> Bool--- null v =--- case v of--- Nil {n ~ 0} -> True--- Cons {n1:Nat} {n~n1+1} (x :: a) (xs :: Vec n1 a) -> False------ When it's applied to a vector of length 5, this becomes:------ null :: Vec 5 Bool -> Bool--- null v =--- case v of--- Nil {5 ~ 0} -> True--- Cons {n1:Nat} {5~n1+1} (x :: a) (xs :: Vec n1 a) -> False------ This function solves 'n1' and replaces every occurrence with its solution. A--- very limited number of solutions are currently recognized: only adds (such--- as in the example) will be solved.-elemExistentials :: HasCallStack => NormRewrite-elemExistentials (TransformContext is0 _) (Case scrut altsTy alts0) = do- tcm <- Lens.view tcCache-- alts1 <- mapM (go is0 tcm) alts0- caseOneAlt (Case scrut altsTy alts1)-- where- -- Eliminate free type variables if possible- go :: InScopeSet -> TyConMap -> (Pat, Term) -> NormalizeSession (Pat, Term)- go is2 tcm alt@(DataPat dc exts0 xs0, term0) =- case solveNonAbsurds tcm (altEqs tcm alt) of- -- No equations solved:- [] -> return alt- -- One or more equations solved:- sols ->- changed =<< go is2 tcm (DataPat dc exts1 xs1, term1)- where- -- Substitute solution in existentials and applied types- is3 = extendInScopeSetList is2 exts0- xs1 = map (substTyInVar (extendTvSubstList (mkSubst is3) sols)) xs0- exts1 = substInExistentialsList is2 exts0 sols-- -- Substitute solution in term.- is4 = extendInScopeSetList is3 xs1- subst = extendTvSubstList (mkSubst is4) sols- term1 = substTm "Replacing tyVar due to solved eq" subst term0-- go _ _ alt = return alt--elemExistentials _ e = return e---- | Move a Case-decomposition from the subject of a Case-decomposition to the alternatives-caseCase :: HasCallStack => NormRewrite-caseCase _ e@(Case (stripTicks -> Case scrut alts1Ty alts1) alts2Ty alts2)- = do- ty1Rep <- representableType <$> Lens.view typeTranslator- <*> Lens.view customReprs- <*> pure False- <*> Lens.view tcCache- <*> pure alts1Ty- if not ty1Rep- then let newAlts = map (second (\altE -> Case altE alts2Ty alts2)) alts1- in changed $ Case scrut alts2Ty newAlts- else return e--caseCase _ e = return e---- | Inline function with a non-representable result if it's the subject--- of a Case-decomposition-inlineNonRep :: HasCallStack => NormRewrite-inlineNonRep (TransformContext localScope _) e@(Case scrut altsTy alts)- | (Var f, args,ticks) <- collectArgsTicks scrut- , isGlobalId f- = do- (cf,_) <- Lens.use curFun- isInlined <- zoomExtra (alreadyInlined f cf)- limit <- Lens.use (extra.inlineLimit)- tcm <- Lens.view tcCache- let scrutTy = termType tcm scrut- noException = not (exception tcm scrutTy)- if noException && (Maybe.fromMaybe 0 isInlined) > limit- then do- traceIf True (concat [$(curLoc) ++ "InlineNonRep: " ++ showPpr (varName f)- ," already inlined " ++ show limit ++ " times in:"- , showPpr (varName cf)- , "\nType of the subject is: " ++ showPpr scrutTy- , "\nFunction " ++ showPpr (varName cf)- , " will not reach a normal form, and compilation"- , " might fail."- , "\nRun with '-fclash-inline-limit=N' to increase"- , " the inlining limit to N."- ])- (return e)- else do- bodyMaybe <- lookupVarEnv f <$> Lens.use bindings- nonRepScrut <- not <$> (representableType <$> Lens.view typeTranslator- <*> Lens.view customReprs- <*> pure False- <*> Lens.view tcCache- <*> pure scrutTy)- case (nonRepScrut, bodyMaybe) of- (True,Just (_,_,_,scrutBody0)) -> do- Monad.when noException (zoomExtra (addNewInline f cf))- -- See Note [AppProp no-shadow invariant]- let scrutBody1 = deShadowTerm localScope scrutBody0- changed $ Case (mkApps (mkTicks scrutBody1 ticks) args) altsTy alts- _ -> return e- where- exception = isClassTy--inlineNonRep _ e = return e---- | Specialize a Case-decomposition (replace by the RHS of an alternative) if--- the subject is (an application of) a DataCon; or if there is only a single--- alternative that doesn't reference variables bound by the pattern.------ Note [CaseCon deshadow]------ Imagine:------ @--- case D (f a b) (g x y) of--- D a b -> h a--- @------ rewriting this to:------ @--- let a = f a b--- in h a--- @------ is very bad because the newly introduced let-binding now captures the free--- variable 'a' in 'f a b'.------ instead me must rewrite to:------ @--- let a1 = f a b--- in h a1--- @-caseCon :: HasCallStack => NormRewrite-caseCon (TransformContext is0 _) (Case scrut ty alts)- | (Data dc, args) <- collectArgs scrut- = case List.find (equalCon dc . fst) alts of- Just (DataPat _ tvs xs, e) -> do- let is1 = extendInScopeSetList (extendInScopeSetList is0 tvs) xs- let fvs = Lens.foldMapOf freeLocalIds unitVarSet e- (binds,_) = List.partition ((`elemVarSet` fvs) . fst)- $ zip xs (Either.lefts args)- e' = case binds of- [] -> e- _ ->- -- See Note [CaseCon deshadow]- let ((is3,substIds),binds') = List.mapAccumL newBinder- (is1,[]) binds- subst = extendIdSubstList (mkSubst is3) substIds- in Letrec binds' (substTm "caseCon0" subst e)- let subst = extendTvSubstList (mkSubst is1)- $ zip tvs (drop (length (dcUnivTyVars dc)) (Either.rights args))- changed (substTm "caseCon1" subst e')- _ -> case alts of- ((DefaultPat,e):_) -> changed e- _ -> changed (undefinedTm ty)- where- equalCon dc (DataPat dc' _ _) = dcTag dc == dcTag dc'- equalCon _ _ = False-- newBinder (isN0,substN) (x,arg) =- let x' = uniqAway isN0 x- isN1 = extendInScopeSet isN0 x'- in ((isN1,(x,Var x'):substN),(x',arg))--caseCon _ c@(Case (stripTicks -> Literal l) _ alts) = case List.find (equalLit . fst) alts of- Just (LitPat _,e) -> changed e- _ -> matchLiteralContructor c l alts- where- equalLit (LitPat l') = l == l'- equalLit _ = False--caseCon ctx@(TransformContext is0 _) e@(Case subj ty alts)- | (Prim _ _,_) <- collectArgs subj = do- reprs <- Lens.view customReprs- tcm <- Lens.view tcCache- bndrs <- Lens.use bindings- primEval <- Lens.view evaluator- ids <- Lens.use uniqSupply- let (ids1,ids2) = splitSupply ids- uniqSupply Lens..= ids2- gh <- Lens.use globalHeap- lvl <- Lens.view dbgLevel- case whnf' primEval bndrs tcm gh ids1 is0 True subj of- (gh',ph',v) -> do- globalHeap Lens..= gh'- bindPureHeap ctx tcm ph' $ \ctx' -> case stripTicks v of- Literal l -> caseCon ctx' (Case (Literal l) ty alts)- subj' -> case collectArgsTicks subj' of- (Data _,_,_) -> caseCon ctx' (Case subj' ty alts)-#if MIN_VERSION_ghc(8,2,2)- (Prim nm ty',_:msgOrCallStack:_,ticks)- | nm == "Control.Exception.Base.absentError" ->- let e' = mkApps (mkTicks (Prim nm ty') ticks)- [Right ty,msgOrCallStack]- in changed e'-#endif-- (Prim nm ty',repTy:_:msgOrCallStack:_,ticks)- | nm `elem` ["Control.Exception.Base.patError"-#if !MIN_VERSION_ghc(8,2,2)- ,"Control.Exception.Base.absentError"-#endif- ,"GHC.Err.undefined"] ->- let e' = mkApps (mkTicks (Prim nm ty') ticks)- [repTy,Right ty,msgOrCallStack]- in changed e'- (Prim nm ty',[_],ticks)- | nm `elem` [ "Clash.Transformations.undefined"- , "Clash.GHC.Evaluator.undefined"- , "EmptyCase"] ->- let e' = mkApps (mkTicks (Prim nm ty') ticks) [Right ty]- in changed e'- _ -> do- let subjTy = termType tcm subj- tran <- Lens.view typeTranslator- case (`evalState` HashMapS.empty) (coreTypeToHWType tran reprs tcm subjTy) of- Right (FilteredHWType (Void (Just hty)) _areVoids)- | hty `elem` [BitVector 0, Unsigned 0, Signed 0, Index 1]- -> caseCon ctx' (Case (Literal (IntegerLiteral 0)) ty alts)- _ -> do- let ret = caseOneAlt e- if lvl > DebugNone then do- let subjIsConst = isConstant subj- traceIf (lvl > DebugNone && subjIsConst) ("Irreducible constant as case subject: " ++ showPpr subj ++ "\nCan be reduced to: " ++ showPpr subj') ret- else- ret--caseCon ctx e@(Case subj ty alts) = do- reprs <- Lens.view customReprs- tcm <- Lens.view tcCache- let subjTy = termType tcm subj- tran <- Lens.view typeTranslator- case (`evalState` HashMapS.empty) (coreTypeToHWType tran reprs tcm subjTy) of- Right (FilteredHWType (Void (Just hty)) _areVoids)- | hty `elem` [BitVector 0, Unsigned 0, Signed 0, Index 1]- -> caseCon ctx (Case (Literal (IntegerLiteral 0)) ty alts)- _ -> caseOneAlt e--caseCon _ e = return e----- | Binds variables on the PureHeap over the result of the rewrite------ To prevent unnecessary rewrites only do this when rewrite changed something.-bindPureHeap- :: TransformContext- -> TyConMap- -> PureHeap- -> (TransformContext -> RewriteMonad extra Term)- -> RewriteMonad extra Term-bindPureHeap (TransformContext is0 ctxs) tcm heap rw = do- (e, Monoid.getAny -> hasChanged) <- listen $ rw ctx'- if hasChanged && not (null bndrs)- then return $ Letrec bndrs e- else return e- where- bndrs = map toLetBinding $ toListUniqMap heap- heapIds = map fst bndrs- is1 = extendInScopeSetList is0 heapIds- ctx' = TransformContext is1 (LetBody heapIds : ctxs)-- toLetBinding :: (Unique,Term) -> LetBinding- toLetBinding (uniq,term) = (nm, term)- where- ty = termType tcm term- nm = mkLocalId ty (mkUnsafeSystemName "x" uniq) -- See [Note: Name re-creation]--{- [Note: Name re-creation]-The names of heap bound variables are safely generate with mkUniqSystemId in Clash.Core.Evaluator.newLetBinding.-But only their uniqs end up in the heap, not the complete names.-So we use mkUnsafeSystemName to recreate the same Name.--}--matchLiteralContructor- :: Term- -> Literal- -> [(Pat,Term)]- -> NormalizeSession Term-matchLiteralContructor c (IntegerLiteral l) alts = go (reverse alts)- where- go [(DefaultPat,e)] = changed e- go ((DataPat dc [] xs,e):alts')- | dcTag dc == 1- , l >= ((-2)^(63::Int)) && l < 2^(63::Int)- = let fvs = Lens.foldMapOf freeLocalIds unitVarSet e- (binds,_) = List.partition ((`elemVarSet` fvs) . fst)- $ zip xs [Literal (IntLiteral l)]- e' = case binds of- [] -> e- _ -> Letrec binds e- in changed e'- | dcTag dc == 2- , l >= 2^(63::Int)- = let !(Jp# !(BN# ba)) = l- ba' = BA.ByteArray ba- bv = PV.Vector 0 (BA.sizeofByteArray ba') ba'- fvs = Lens.foldMapOf freeLocalIds unitVarSet e- (binds,_) = List.partition ((`elemVarSet` fvs) . fst)- $ zip xs [Literal (ByteArrayLiteral bv)]- e' = case binds of- [] -> e- _ -> Letrec binds e- in changed e'- | dcTag dc == 3- , l < ((-2)^(63::Int))- = let !(Jn# !(BN# ba)) = l- ba' = BA.ByteArray ba- bv = PV.Vector 0 (BA.sizeofByteArray ba') ba'- fvs = Lens.foldMapOf freeLocalIds unitVarSet e- (binds,_) = List.partition ((`elemVarSet` fvs) . fst)- $ zip xs [Literal (ByteArrayLiteral bv)]- e' = case binds of- [] -> e- _ -> Letrec binds e- in changed e'- | otherwise- = go alts'- go ((LitPat l', e):alts')- | IntegerLiteral l == l'- = changed e- | otherwise- = go alts'- go _ = error $ $(curLoc) ++ "Report as bug: caseCon error: " ++ showPpr c--matchLiteralContructor c (NaturalLiteral l) alts = go (reverse alts)- where- go [(DefaultPat,e)] = changed e- go ((DataPat dc [] xs,e):alts')- | dcTag dc == 1- , l >= 0 && l < 2^(64::Int)- = let fvs = Lens.foldMapOf freeLocalIds unitVarSet e- (binds,_) = List.partition ((`elemVarSet` fvs) . fst)- $ zip xs [Literal (WordLiteral l)]- e' = case binds of- [] -> e- _ -> Letrec binds e- in changed e'- | dcTag dc == 2- , l >= 2^(64::Int)- = let !(Jp# !(BN# ba)) = l- ba' = BA.ByteArray ba- bv = PV.Vector 0 (BA.sizeofByteArray ba') ba'- fvs = Lens.foldMapOf freeLocalIds unitVarSet e- (binds,_) = List.partition ((`elemVarSet` fvs) . fst)- $ zip xs [Literal (ByteArrayLiteral bv)]- e' = case binds of- [] -> e- _ -> Letrec binds e- in changed e'- | otherwise- = go alts'- go ((LitPat l', e):alts')- | NaturalLiteral l == l'- = changed e- | otherwise- = go alts'- go _ = error $ $(curLoc) ++ "Report as bug: caseCon error: " ++ showPpr c--matchLiteralContructor _ _ ((DefaultPat,e):_) = changed e-matchLiteralContructor c _ _ =- error $ $(curLoc) ++ "Report as bug: caseCon error: " ++ showPpr c--caseOneAlt :: Term -> RewriteMonad extra Term-caseOneAlt e@(Case _ _ [(pat,altE)]) = case pat of- DefaultPat -> changed altE- LitPat _ -> changed altE- DataPat _ tvs xs- | (coerce tvs ++ coerce xs) `localVarsDoNotOccurIn` altE- -> changed altE- | otherwise- -> return e--caseOneAlt e = return e---- | Bring an application of a DataCon or Primitive in ANF, when the argument is--- is considered non-representable-nonRepANF :: HasCallStack => NormRewrite-nonRepANF ctx e@(App appConPrim arg)- | (conPrim, _) <- collectArgs e- , isCon conPrim || isPrim conPrim- = do- untranslatable <- isUntranslatable False arg- case (untranslatable,stripTicks arg) of- (True,Letrec binds body) -> changed (Letrec binds (App appConPrim body))- (True,Case {}) -> specializeNorm ctx e- (True,Lam {}) -> specializeNorm ctx e- (True,TyLam {}) -> specializeNorm ctx e- _ -> return e--nonRepANF _ e = return e---- | Ensure that top-level lambda's eventually bind a let-expression of which--- the body is a variable-reference.-topLet :: HasCallStack => NormRewrite-topLet (TransformContext is0 ctx) e- | all (\c -> isLambdaBodyCtx c || isTickCtx c) ctx && not (isLet e)- = do- untranslatable <- isUntranslatable False e- if untranslatable- then return e- else do tcm <- Lens.view tcCache- argId <- mkTmBinderFor is0 tcm (mkUnsafeSystemName "result" 0) e- changed (Letrec [(argId, e)] (Var argId))--topLet (TransformContext is0 ctx) e@(Letrec binds body)- | all (\c -> isLambdaBodyCtx c || isTickCtx c) ctx- = do- let localVar = isLocalVar body- untranslatable <- isUntranslatable False body- if localVar || untranslatable- then return e- else do- tcm <- Lens.view tcCache- let is2 = extendInScopeSetList is0 (map fst binds)- argId <- mkTmBinderFor is2 tcm (mkUnsafeSystemName "result" 0) body- changed (Letrec (binds ++ [(argId,body)]) (Var argId))--topLet _ e = return e---- Misc rewrites---- | Remove unused let-bindings-deadCode :: HasCallStack => NormRewrite-deadCode _ e@(Letrec xes body) = do- let bodyFVs = Lens.foldMapOf freeLocalIds unitVarSet body- (xesUsed,xesOther) = List.partition((`elemVarSet` bodyFVs) . fst) xes- xesUsed' = findUsedBndrs [] xesUsed xesOther- if length xesUsed' /= length xes- then case xesUsed' of- [] -> changed body- _ -> changed (Letrec xesUsed' body)- else return e- where- findUsedBndrs :: [(Id, Term)] -> [(Id, Term)]- -> [(Id, Term)] -> [(Id, Term)]- findUsedBndrs used [] _ = used- findUsedBndrs used explore other =- let fvsUsed = List.foldl' unionVarSet- emptyVarSet- (map (Lens.foldMapOf freeLocalIds unitVarSet . snd) explore)- (explore',other') = List.partition- ((`elemVarSet` fvsUsed) . fst) other- in findUsedBndrs (used ++ explore) explore' other'--deadCode _ e = return e--removeUnusedExpr :: HasCallStack => NormRewrite-removeUnusedExpr _ e@(collectArgsTicks -> (p@(Prim nm pInfo),args,ticks)) = do- bbM <- HashMap.lookup nm <$> Lens.use (extra.primitives)- case bbM of- Just (extractPrim -> Just (BlackBox pNm _ _ _ _ _ _ inc templ)) -> do- let usedArgs | isFromInt pNm- = [0,1,2]- | nm `elem` ["Clash.Annotations.BitRepresentation.Deriving.dontApplyInHDL"- ]- = [0,1]- | otherwise- = usedArguments templ ++ concatMap (usedArguments . snd) inc- tcm <- Lens.view tcCache- args' <- go tcm 0 usedArgs args- if args == args'- then return e- else changed (mkApps (mkTicks p ticks) args')- _ -> return e- where- arity = length . Either.rights . fst $ splitFunForallTy (primType pInfo)-- go _ _ _ [] = return []- go tcm n used (Right ty:args') = do- args'' <- go tcm n used args'- return (Right ty : args'')- go tcm n used (Left tm : args') = do- args'' <- go tcm (n+1) used args'- let ty = termType tcm tm- p' = removedTm ty- if n < arity && n `notElem` used- then return (Left p' : args'')- else return (Left tm : args'')--removeUnusedExpr _ e@(Case _ _ [(DataPat _ [] xs,altExpr)]) =- if xs `localIdsDoNotOccurIn` altExpr- then changed altExpr- else return e---- Replace any expression that creates a Vector of size 0 within the application--- of the Cons constructor, by the Nil constructor.-removeUnusedExpr _ e@(collectArgsTicks -> (Data dc, [_,Right aTy,Right nTy,_,Left a,Left nil],ticks))- | nameOcc (dcName dc) == "Clash.Sized.Vector.Cons"- = do- tcm <- Lens.view tcCache- case runExcept (tyNatSize tcm nTy) of- Right 0- | (con, _) <- collectArgs nil- , not (isCon con)- -> let eTy = termType tcm e- (TyConApp vecTcNm _) = tyView eTy- (Just vecTc) = lookupUniqMap vecTcNm tcm- [nilCon,consCon] = tyConDataCons vecTc- v = mkTicks (mkVec nilCon consCon aTy 1 [a]) ticks- in changed v- _ -> return e--removeUnusedExpr _ e = return e---- | Inline let-bindings when the RHS is either a local variable reference or--- is constant (except clock or reset generators)-bindConstantVar :: HasCallStack => NormRewrite-bindConstantVar = inlineBinders test- where- test _ (_,stripTicks -> e) = case isLocalVar e of- True -> return True- _ -> isWorkFreeIsh e >>= \case- True -> Lens.use (extra.inlineConstantLimit) >>= \case- 0 -> return True- n -> return (termSize e <= n)- _ -> return False- -- test _ _ = return False---- | Push a cast over a case into it's alternatives.-caseCast :: HasCallStack => NormRewrite-caseCast _ (Cast (stripTicks -> Case subj ty alts) ty1 ty2) = do- let alts' = map (\(p,e) -> (p, Cast e ty1 ty2)) alts- changed (Case subj ty alts')-caseCast _ e = return e---- | Push a cast over a Letrec into it's body-letCast :: HasCallStack => NormRewrite-letCast _ (Cast (stripTicks -> Letrec binds body) ty1 ty2) =- changed $ Letrec binds (Cast body ty1 ty2)-letCast _ e = return e----- | Push cast over an argument to a function into that function------ This is done by specializing on the casted argument.--- Example:--- @--- y = f (cast a)--- where f x = g x--- @--- transforms to:--- @--- y = f' a--- where f' x' = (\x -> g x) (cast x')--- @------ The reason d'etre for this transformation is that we hope to end up with--- and expression where two casts are "back-to-back" after which we can--- eliminate them in 'eliminateCastCast'.-argCastSpec :: HasCallStack => NormRewrite-argCastSpec ctx e@(App _ (stripTicks -> Cast e' _ _)) =- if isWorkFree e' then- go- else- warn go- where- go = specializeNorm ctx e- warn = trace (unwords- [ "WARNING:", $(curLoc), "specializing a function on a non work-free"- , "cast. Generated HDL implementation might contain duplicate work."- , "Please report this as a bug.", "\n\nExpression where this occured:"- , "\n\n" ++ showPpr e- ])-argCastSpec _ e = return e---- | Only inline casts that just contain a 'Var', because these are guaranteed work-free.--- These are the result of the 'splitCastWork' transformation.-inlineCast :: HasCallStack => NormRewrite-inlineCast = inlineBinders test- where- test _ (_, (Cast (stripTicks -> Var {}) _ _)) = return True- test _ _ = return False---- | Eliminate two back to back casts where the type going in and coming out are the same------ @--- (cast :: b -> a) $ (cast :: a -> b) x ==> x--- @-eliminateCastCast :: HasCallStack => NormRewrite-eliminateCastCast _ c@(Cast (stripTicks -> Cast e tyA tyB) tyB' tyC) = do- tcm <- Lens.view tcCache- let ntyA = normalizeType tcm tyA- ntyB = normalizeType tcm tyB- ntyB' = normalizeType tcm tyB'- ntyC = normalizeType tcm tyC- if ntyB == ntyB' && ntyA == ntyC then changed e- else throwError- where throwError = do- (nm,sp) <- Lens.use curFun- throw (ClashException sp ($(curLoc) ++ showPpr nm- ++ ": Found 2 nested casts whose types don't line up:\n"- ++ showPpr c)- Nothing)--eliminateCastCast _ e = return e---- | Make a cast work-free by splitting the work of to a separate binding------ @--- let x = cast (f a b)--- ==>--- let x = cast x'--- x' = f a b--- @-splitCastWork :: HasCallStack => NormRewrite-splitCastWork ctx@(TransformContext is0 _) unchanged@(Letrec vs e') = do- (vss', Monoid.getAny -> hasChanged) <- listen (mapM (splitCastLetBinding is0) vs)- let vs' = concat vss'- if hasChanged then changed (Letrec vs' e')- else return unchanged- where- splitCastLetBinding- :: InScopeSet- -> LetBinding- -> RewriteMonad extra [LetBinding]- splitCastLetBinding isN x@(nm, e) = case stripTicks e of- Cast (Var {}) _ _ -> return [x] -- already work-free- Cast (Cast {}) _ _ -> return [x] -- casts will be eliminated- Cast e0 ty1 ty2 -> do- tcm <- Lens.view tcCache- nm' <- mkTmBinderFor isN tcm (mkDerivedName ctx (nameOcc $ varName nm)) e0- changed [(nm',e0)- ,(nm, Cast (Var nm') ty1 ty2)- ]- _ -> return [x]--splitCastWork _ e = return e----- | Inline work-free functions, i.e. fully applied functions that evaluate to--- a constant-inlineWorkFree :: HasCallStack => NormRewrite-inlineWorkFree (TransformContext localScope _) e@(collectArgsTicks -> (Var f,args@(_:_),ticks))- = do- tcm <- Lens.view tcCache- let eTy = termType tcm e- argsHaveWork <- or <$> mapM (either expressionHasWork- (const (pure False)))- args- untranslatable <- isUntranslatableType True eTy- let isSignal = isSignalType tcm eTy- let lv = isLocalId f- if untranslatable || isSignal || argsHaveWork || lv- then return e- else do- bndrs <- Lens.use bindings- case lookupVarEnv f bndrs of- -- Don't inline recursive expressions- Just (_,_,_,body) -> do- isRecBndr <- isRecursiveBndr f- if isRecBndr- then return e- else do- -- See Note [AppProp no-shadow invariant]- changed (mkApps (mkTicks (deShadowTerm localScope body) ticks) args)- _ -> return e- where- -- an expression is has work when it contains free local variables,- -- or has a Signal type, i.e. it does not evaluate to a work-free- -- constant.- expressionHasWork e' = do- let fvIds = Lens.toListOf freeLocalIds e'- tcm <- Lens.view tcCache- let e'Ty = termType tcm e'- isSignal = isSignalType tcm e'Ty- return (not (null fvIds) || isSignal)--inlineWorkFree (TransformContext localScope _) e@(Var f) = do- tcm <- Lens.view tcCache- let fTy = varType f- closed = not (isPolyFunCoreTy tcm fTy)- isSignal = isSignalType tcm fTy- untranslatable <- isUntranslatableType True fTy- let gv = isGlobalId f- if closed && not untranslatable && not isSignal && gv- then do- bndrs <- Lens.use bindings- case lookupVarEnv f bndrs of- -- Don't inline recursive expressions- Just top -> do- isRecBndr <- isRecursiveBndr f- if isRecBndr- then return e- else do- (_,_,_,body) <- normalizeTopLvlBndr f top- -- See Note [AppProp no-shadow invariant]- changed (deShadowTerm localScope body)- _ -> return e- else return e--inlineWorkFree _ e = return e---- | Inline small functions-inlineSmall :: HasCallStack => NormRewrite-inlineSmall (TransformContext localScope _) e@(collectArgsTicks -> (Var f,args,ticks)) = do- untranslatable <- isUntranslatable True e- topEnts <- Lens.view topEntities- let lv = isLocalId f- if untranslatable || f `elemVarSet` topEnts || lv- then return e- else do- bndrs <- Lens.use bindings- sizeLimit <- Lens.use (extra.inlineFunctionLimit)- case lookupVarEnv f bndrs of- -- Don't inline recursive expressions- Just (_,_,inl,body) -> do- isRecBndr <- isRecursiveBndr f- if not isRecBndr && inl /= NoInline && termSize body < sizeLimit- then do- -- See Note [AppProp no-shadow invariant]- changed (mkApps (mkTicks (deShadowTerm localScope body) ticks) args)- else return e- _ -> return e--inlineSmall _ e = return e---- | Specialise functions on arguments which are constant, except when they--- are clock, reset generators.-constantSpec :: HasCallStack => NormRewrite-constantSpec ctx@(TransformContext is0 tfCtx) e@(App e1 e2)- | (Var {}, args) <- collectArgs e1- , (_, []) <- Either.partitionEithers args- , null $ Lens.toListOf termFreeTyVars e2- = do specInfo<- constantSpecInfo ctx e2- if csrFoundConstant specInfo then- let newBindings = csrNewBindings specInfo in- if null newBindings then- -- Whole of e2 is constant- specializeNorm ctx (App e1 e2)- else do- -- Parts of e2 are constant- let is1 = extendInScopeSetList is0 (fst <$> csrNewBindings specInfo)- -- Deshadow because appPropFast will be called after constantSpec- deShadowTerm is0- <$> Letrec newBindings- <$> specializeNorm- (TransformContext is1 tfCtx)- (App e1 (csrNewTerm specInfo))-- else- -- e2 has no constant parts- return e-constantSpec _ e = return e----- Experimental---- | Propagate arguments of application inwards; except for 'Lam' where the--- argument becomes let-bound.------ Note [AppProp deshadow]------ Imagine:------ @--- (case x of--- D a b -> h a) (f x y)--- @------ rewriting this to:------ @--- let b = f x y--- in case x of--- D a b -> h a b--- @------ is very bad because 'b' in 'h a b' is now bound by the pattern instead of the--- newly introduced let-binding------ instead me must rewrite to:------ @--- let b1 = f x y--- in case x of--- D a b -> h a b1--- @------ Note [AppProp no-shadow invariant]------ Imagine------ @--- (\x -> e) u--- @------ where @u@ has a free variable named @x@, rewriting this to:------ @--- let x = u--- in e--- @------ would be very bad, because the let-binding suddenly captures the free--- variable in @u@. The same for:------ @--- (let x = w in e) u--- @------ where @u@ again has a free variable @x@, rewriting this to:------ @--- let x = w in (e u)--- @------ would be bad because the let-binding now captures the free variable in @u@.------ To prevent this from happening, we can either:------ 1. Rename the bindings, so that they cannot capture--- 2. Ensure that @AppProp@ is only called in a context where there is no--- shadowing, i.e. the bindings can never never collide with the current--- inScopeSet.------ We have gone for option 2 so that AppProp requires less computation and--- because AppProp is such a commonly applied transformation. This--- means that when normalisation starts we deshadow the expression, and when--- we inline global binders, we ensure that inlined expression is deshadowed--- taking the InScopeSet of the context into account.-appProp :: HasCallStack => NormRewrite-appProp (TransformContext is0 _) (App (collectTicks -> (Lam v e,ticks)) arg) =- if isWorkFree arg || isVar arg- then do- let subst = extendIdSubst (mkSubst is0) v arg- changed $ mkTicks (substTm "appProp.AppLam" subst e) ticks- else changed $ Letrec [(v, arg)] (mkTicks e ticks)--appProp _ (App (collectTicks -> (Letrec v e, ticks)) arg) = do- changed (Letrec v (App (mkTicks e ticks) arg))--appProp ctx@(TransformContext is0 _) (App (collectTicks -> (Case scrut ty alts,ticks)) arg) = do- tcm <- Lens.view tcCache- let argTy = termType tcm arg- ty' = applyFunTy tcm ty argTy- if isWorkFree arg || isVar arg- then do- let alts' = map (second (`App` arg)) alts- changed $ mkTicks (Case scrut ty' alts') ticks- else do- -- See Note [AppProp deshadow]- let is2 = unionInScope is0 ((mkInScopeSet . mkVarSet . concatMap (patVars . fst)) alts)- boundArg <- mkTmBinderFor is2 tcm (mkDerivedName ctx "app_arg") arg- let alts' = map (second (`App` (Var boundArg))) alts- changed (Letrec [(boundArg, arg)] (mkTicks (Case scrut ty' alts') ticks))--appProp (TransformContext is0 _) (TyApp (collectTicks -> (TyLam tv e,ticks)) t) = do- let subst = extendTvSubst (mkSubst is0) tv t- changed $ mkTicks (substTm "appProp.TyAppTyLam" subst e) ticks--appProp _ (TyApp (collectTicks -> (Letrec v e,ticks)) t) = do- changed (Letrec v (mkTicks (TyApp e t) ticks))--appProp _ (TyApp (collectTicks -> (Case scrut altsTy alts,ticks)) ty) = do- let alts' = map (second (`TyApp` ty)) alts- tcm <- Lens.view tcCache- let ty' = piResultTy tcm altsTy ty- changed (mkTicks (Case scrut ty' alts') ticks)--appProp _ e = return e---- | Unlike 'appProp', which propagates a single argument in an application one--- level down (and should be called in an innermost traversal), 'appPropFast'--- tries to propagate as many arguments as possible, down as many levels as--- possible; and should be called in a top-down traversal.------ The idea is that this reduces the number of traversals, which hopefully leads--- to shorter compile times.------ Implementation only works if terms are fully deshadowed, see--- Note [AppProp deshadow]-appPropFast :: HasCallStack => NormRewrite-appPropFast ctx@(TransformContext is _) = \case- e@App {} -> uncurry3 (go is) (collectArgsTicks e)- e@TyApp {} -> uncurry3 (go is) (collectArgsTicks e)- e -> return e- where- go :: InScopeSet -> Term -> [Either Term Type] -> [TickInfo]- -> NormalizeSession Term- go is0 (collectArgsTicks -> (fun,args0@(_:_),ticks0)) args1 ticks1 =- go is0 fun (args0 ++ args1) (ticks0 ++ ticks1)-- go is0 (Lam v e) (Left arg:args) ticks = do- setChanged- if isWorkFree arg || isVar arg- then do- let subst = extendIdSubst (mkSubst is0) v arg- (`mkTicks` ticks) <$> go is0 (substTm "appPropFast.AppLam" subst e) args []- else do- let is1 = extendInScopeSet is0 v- Letrec [(v, arg)] <$> go is1 e args ticks-- go is0 (Letrec vs e) args@(_:_) ticks = do- setChanged- let vbs = map fst vs- is1 = extendInScopeSetList is0 vbs- Letrec vs <$> go is1 e args ticks-- go is0 (TyLam tv e) (Right t:args) ticks = do- setChanged- let subst = extendTvSubst (mkSubst is0) tv t- (`mkTicks` ticks) <$> go is0 (substTm "appPropFast.TyAppTyLam" subst e) args []-- go is0 (Case scrut ty0 alts) args0@(_:_) ticks = do- setChanged- let isA1 = unionInScope- is0- ((mkInScopeSet . mkVarSet . concatMap (patVars . fst)) alts)- (ty1,vs,args1) <- goCaseArg isA1 ty0 [] args0- case vs of- [] -> (`mkTicks` ticks) . Case scrut ty1 <$> mapM (goAlt is0 args1) alts- _ -> do- let vbs = map fst vs- is1 = extendInScopeSetList is0 vbs- Letrec vs . (`mkTicks` ticks) . Case scrut ty1 <$> mapM (goAlt is1 args1) alts-- go is0 (Tick sp e) args ticks = do- setChanged- go is0 e args (sp:ticks)-- go _ fun args ticks = return (mkApps (mkTicks fun ticks) args)-- goAlt is0 args0 (p,e) = do- let (tvs,ids) = patIds p- is1 = extendInScopeSetList (extendInScopeSetList is0 tvs) ids- (p,) <$> go is1 e args0 []-- goCaseArg isA ty0 ls0 (Right t:args0) = do- tcm <- Lens.view tcCache- let ty1 = piResultTy tcm ty0 t- (ty2,ls1,args1) <- goCaseArg isA ty1 ls0 args0- return (ty2,ls1,Right t:args1)-- goCaseArg isA0 ty0 ls0 (Left arg:args0) = do- tcm <- Lens.view tcCache- let argTy = termType tcm arg- ty1 = applyFunTy tcm ty0 argTy- case isWorkFree arg || isVar arg of- True -> do- (ty2,ls1,args1) <- goCaseArg isA0 ty1 ls0 args0- return (ty2,ls1,Left arg:args1)- False -> do- boundArg <- mkTmBinderFor isA0 tcm (mkDerivedName ctx "app_arg") arg- let isA1 = extendInScopeSet isA0 boundArg- (ty2,ls1,args1) <- goCaseArg isA1 ty1 ls0 args0- return (ty2,(boundArg,arg):ls1,Left (Var boundArg):args1)-- goCaseArg _ ty ls [] = return (ty,ls,[])---- | Flatten ridiculous case-statements generated by GHC------ For case-statements in haskell of the form:------ @--- f :: Unsigned 4 -> Unsigned 4--- f x = case x of--- 0 -> 3--- 1 -> 2--- 2 -> 1--- 3 -> 0--- @------ GHC generates Core that looks like:------ @--- f = \(x :: Unsigned 4) -> case x == fromInteger 3 of--- False -> case x == fromInteger 2 of--- False -> case x == fromInteger 1 of--- False -> case x == fromInteger 0 of--- False -> error "incomplete case"--- True -> fromInteger 3--- True -> fromInteger 2--- True -> fromInteger 1--- True -> fromInteger 0--- @------ Which would result in a priority decoder circuit where a normal decoder--- circuit was desired.------ This transformation transforms the above Core to the saner:------ @--- f = \(x :: Unsigned 4) -> case x of--- _ -> error "incomplete case"--- 0 -> fromInteger 3--- 1 -> fromInteger 2--- 2 -> fromInteger 1--- 3 -> fromInteger 0--- @-caseFlat :: HasCallStack => NormRewrite-caseFlat _ e@(Case (collectEqArgs -> Just (scrut',_)) ty _)- = do- case collectFlat scrut' e of- Just alts' -> changed (Case scrut' ty (last alts' : init alts'))- Nothing -> return e--caseFlat _ e = return e--collectFlat :: Term -> Term -> Maybe [(Pat,Term)]-collectFlat scrut (Case (collectEqArgs -> Just (scrut', val)) _ty [lAlt,rAlt])- | scrut' == scrut- = case collectArgs val of- (Prim nm' _,args') | isFromInt nm' ->- go (last args')- (Data dc,args') | nameOcc (dcName dc) == "GHC.Types.I#" ->- go (last args')- _ -> Nothing- where- go (Left (Literal i)) = case (lAlt,rAlt) of- ((pl,el),(pr,er))- | isFalseDcPat pl || isTrueDcPat pr ->- case collectFlat scrut el of- Just alts' -> Just ((LitPat i, er) : alts')- Nothing -> Just [(LitPat i, er)- ,(DefaultPat, el)- ]- | otherwise ->- case collectFlat scrut er of- Just alts' -> Just ((LitPat i, el) : alts')- Nothing -> Just [(LitPat i, el)- ,(DefaultPat, er)- ]- go _ = Nothing-- isFalseDcPat (DataPat p _ _)- = ((== "GHC.Types.False") . nameOcc . dcName) p- isFalseDcPat _ = False-- isTrueDcPat (DataPat p _ _)- = ((== "GHC.Types.True") . nameOcc . dcName) p- isTrueDcPat _ = False--collectFlat _ _ = Nothing--collectEqArgs :: Term -> Maybe (Term,Term)-collectEqArgs (collectArgsTicks -> (Prim nm _, args, ticks))- | nm == "Clash.Sized.Internal.BitVector.eq#"- = let [_,_,Left scrut,Left val] = args- in Just (mkTicks scrut ticks,val)- | nm == "Clash.Sized.Internal.Index.eq#" ||- nm == "Clash.Sized.Internal.Signed.eq#" ||- nm == "Clash.Sized.Internal.Unsigned.eq#"- = let [_,Left scrut,Left val] = args- in Just (mkTicks scrut ticks,val)- | nm == "Clash.Transformations.eqInt"- = let [Left scrut,Left val] = args- in Just (mkTicks scrut ticks,val)-collectEqArgs _ = Nothing--type NormRewriteW = Transform (StateT ([LetBinding],InScopeSet) (RewriteMonad NormalizeState))---- | See Note [ANF InScopeSet]-tellBinders :: Monad m => [LetBinding] -> StateT ([LetBinding],InScopeSet) m ()-tellBinders bs = modify ((bs ++) *** (`extendInScopeSetList` (map fst bs)))---- | Turn an expression into a modified ANF-form. As opposed to standard ANF,--- constants do not become let-bound.-makeANF :: HasCallStack => NormRewrite-makeANF (TransformContext is0 ctx) (Lam bndr e) = do- e' <- makeANF (TransformContext (extendInScopeSet is0 bndr)- (LamBody bndr:ctx))- e- return (Lam bndr e')--makeANF _ e@(TyLam {}) = return e--makeANF ctx@(TransformContext is0 _) e0- = do- -- We need to freshen all binders in `e` because we're shuffling them around- -- into a single let-binder, because even when binders don't shadow, they- -- don't have to be unique within an expression. And so lifting them all- -- to a single let-binder will cause issues when they're not unique.- --- -- We cannot make freshening part of collectANF, because when we generate- -- new binders, we need to make sure those names do not conflict with _any_- -- of the existing binders in the expression.- --- -- See also Note [ANF InScopeSet]- let (is2,e1) = freshenTm is0 e0- (e2,(bndrs,_)) <- runStateT (bottomupR collectANF ctx e1) ([],is2)- case bndrs of- [] -> return e0- _ -> do- let (e3,ticks) = collectTicks e2- (srcTicks,nmTicks) = partitionTicks ticks- -- Ensure that `AppendName` ticks still scope over the entire expression- changed (mkTicks (Letrec bndrs (mkTicks e3 srcTicks)) nmTicks)---- | Note [ANF InScopeSet]------ The InScopeSet contains:------ 1. All the free variables of the expression we are traversing------ 2. All the bound variables of the expression we are traversing------ 3. The newly created let-bindings as we recurse back up the traversal------ All of these are needed to created let-bindings that------ * Do not shadow--- * Are not shadowed--- * Nor conflict with each other (i.e. have the same unique)------ Initially we start with the local InScopeSet and add the global variables:------ @--- is1 <- unionInScope is0 <$> Lens.use globalInScope--- @------ Which will gives us the (superset of) free variables of the expression. Then--- we call 'freshenTm'------ @--- let (is2,e1) = freshenTm is1 e0--- @------ Which extends the InScopeSet with all the bound variables in 'e1', the--- version of 'e0' where all binders are unique (not just deshadowed).------ So we start out with an InScopeSet that satisfies points 1 and 2, now every--- time we create a new binder we must add it to the InScopeSet to satisfy--- point 3.-collectANF :: HasCallStack => NormRewriteW-collectANF ctx e@(App appf arg)- | (conVarPrim, _) <- collectArgs e- , isCon conVarPrim || isPrim conVarPrim || isVar conVarPrim- = do- untranslatable <- lift (isUntranslatable False arg)- let localVar = isLocalVar arg- constantNoCR <- lift (isConstantNotClockReset arg)- case (untranslatable,localVar || constantNoCR,arg) of- (False,False,_) -> do- tcm <- Lens.view tcCache- -- See Note [ANF InScopeSet]- is1 <- Lens.use _2- argId <- lift (mkTmBinderFor is1 tcm (mkDerivedName ctx "app_arg") arg)- -- See Note [ANF InScopeSet]- tellBinders [(argId,arg)]- return (App appf (Var argId))- (True,False,Letrec binds body) -> do- tellBinders binds- return (App appf body)- _ -> return e--collectANF _ (Letrec binds body) = do- tellBinders binds- untranslatable <- lift (isUntranslatable False body)- let localVar = isLocalVar body- if localVar || untranslatable- then return body- else do- tcm <- Lens.view tcCache- -- See Note [ANF InScopeSet]- is1 <- Lens.use _2- argId <- lift (mkTmBinderFor is1 tcm (mkUnsafeSystemName "result" 0) body)- -- See Note [ANF InScopeSet]- tellBinders [(argId,body)]- return (Var argId)---- TODO: The code below special-cases ANF for the ':-' constructor for the--- 'Signal' type. The 'Signal' type is essentially treated as a "transparent"--- type by the Clash compiler, so observing its constructor leads to all kinds--- of problems. In this case that "Clash.Rewrite.Util.mkSelectorCase" will--- try to project the LHS and RHS of the ':-' constructor, however,--- 'mkSelectorCase' uses 'coreView1' to find the "real" data-constructor.--- 'coreView1' however looks through the 'Signal' type, and hence 'mkSelector'--- finds the data constructors for the element type of Signal. This resulted in--- error #24 (https://github.com/christiaanb/clash2/issues/24), where we--- try to get the first field out of the 'Vec's 'Nil' constructor.------ Ultimately we should stop treating Signal as a "transparent" type and deal--- handling of the Signal type, and the involved co-recursive functions,--- properly. At the moment, Clash cannot deal with this recursive type and the--- recursive functions involved, hence the need for special-casing code. After--- everything is done properly, we should remove the two lines below.-collectANF _ e@(Case _ _ [(DataPat dc _ _,_)])- | nameOcc (dcName dc) == "Clash.Signal.Internal.:-" = return e--collectANF ctx (Case subj ty alts) = do- let localVar = isLocalVar subj- let isConstantSubj = isConstant subj-- subj' <- if localVar || isConstantSubj- then return subj- else do- tcm <- Lens.view tcCache- -- See Note [ANF InScopeSet]- is1 <- Lens.use _2- argId <- lift (mkTmBinderFor is1 tcm (mkDerivedName ctx "case_scrut") subj)- -- See Note [ANF InScopeSet]- tellBinders [(argId,subj)]- return (Var argId)-- alts' <- mapM (doAlt subj') alts-- case alts' of- [(DataPat _ [] xs,altExpr)]- | xs `localIdsDoNotOccurIn` altExpr- -> return altExpr- _ -> return (Case subj' ty alts')- where- doAlt- :: Term -> (Pat,Term)- -> StateT ([LetBinding],InScopeSet) (RewriteMonad NormalizeState)- (Pat,Term)- doAlt subj' alt@(DataPat dc exts xs,altExpr) | not (bindsExistentials exts xs) = do- let lv = isLocalVar altExpr- patSels <- Monad.zipWithM (doPatBndr subj' dc) xs [0..]- let altExprIsConstant = isConstant altExpr- let usesXs (Var n) = any (== n) xs- usesXs _ = False- if (lv && (not (usesXs altExpr) || length alts == 1)) || altExprIsConstant- then do- -- See Note [ANF InScopeSet]- tellBinders patSels- return alt- else do- tcm <- Lens.view tcCache- -- See Note [ANF InScopeSet]- is1 <- Lens.use _2- altId <- lift (mkTmBinderFor is1 tcm (mkDerivedName ctx "case_alt") altExpr)- -- See Note [ANF InScopeSet]- tellBinders ((altId,altExpr):patSels)- return (DataPat dc exts xs,Var altId)- doAlt _ alt@(DataPat {}, _) = return alt- doAlt _ alt@(pat,altExpr) = do- let lv = isLocalVar altExpr- let altExprIsConstant = isConstant altExpr- if lv || altExprIsConstant- then return alt- else do- tcm <- Lens.view tcCache- -- See Note [ANF InScopeSet]- is1 <- Lens.use _2- altId <- lift (mkTmBinderFor is1 tcm (mkDerivedName ctx "case_alt") altExpr)- tellBinders [(altId,altExpr)]- return (pat,Var altId)-- doPatBndr- :: Term -> DataCon -> Id -> Int- -> StateT ([LetBinding],InScopeSet) (RewriteMonad NormalizeState)- LetBinding- doPatBndr subj' dc pId i- = do- tcm <- Lens.view tcCache- -- See Note [ANF InScopeSet]- is1 <- Lens.use _2- patExpr <- lift (mkSelectorCase ($(curLoc) ++ "doPatBndr") is1 tcm subj' (dcTag dc) i)- -- No need to 'tellBinders' here because 'pId' is already in the ANF- -- InScopeSet.- --- -- See also Note [ANF InScopeSet]- return (pId,patExpr)--collectANF _ e = return e---- | Eta-expand top-level lambda's (DON'T use in a traversal!)-etaExpansionTL :: HasCallStack => NormRewrite-etaExpansionTL (TransformContext is0 ctx) (Lam bndr e) = do- e' <- etaExpansionTL- (TransformContext (extendInScopeSet is0 bndr) (LamBody bndr:ctx))- e- return $ Lam bndr e'--etaExpansionTL (TransformContext is0 ctx) (Letrec xes e) = do- let bndrs = map fst xes- e' <- etaExpansionTL- (TransformContext (extendInScopeSetList is0 bndrs)- (LetBody bndrs:ctx))- e- case stripLambda e' of- (bs@(_:_),e2) -> do- let e3 = Letrec xes e2- changed (mkLams e3 bs)- _ -> return (Letrec xes e')- where- stripLambda :: Term -> ([Id],Term)- stripLambda (Lam bndr e0) =- let (bndrs,e1) = stripLambda e0- in (bndr:bndrs,e1)- stripLambda e' = ([],e')--etaExpansionTL (TransformContext is0 ctx) e- = do- tcm <- Lens.view tcCache- if isFun tcm e- then do- let argTy = ( fst- . Maybe.fromMaybe (error $ $(curLoc) ++ "etaExpansion splitFunTy")- . splitFunTy tcm- . termType tcm- ) e- newId <- mkInternalVar is0 "arg" argTy- e' <- etaExpansionTL (TransformContext (extendInScopeSet is0 newId)- (LamBody newId:ctx))- (App e (Var newId))- changed (Lam newId e')- else return e---- | Eta-expand functions with a Synthesize annotation, needed to allow such--- functions to appear as arguments to higher-order primitives.-etaExpandSyn :: HasCallStack => NormRewrite-etaExpandSyn (TransformContext is0 ctx) e@(collectArgs -> (Var f, _)) = do- topEnts <- Lens.view topEntities- tcm <- Lens.view tcCache- let isTopEnt = f `elemVarSet` topEnts- isAppFunCtx =- \case- AppFun:_ -> True- TickC _:c -> isAppFunCtx c- _ -> False- argTyM = fmap fst (splitFunTy tcm (termType tcm e))- case argTyM of- Just argTy | isTopEnt && not (isAppFunCtx ctx) -> do- newId <- mkInternalVar is0 "arg" argTy- changed (Lam newId (App e (Var newId)))- _ -> return e--etaExpandSyn _ e = return e--isClassConstraint :: Type -> Bool-isClassConstraint (tyView -> TyConApp nm0 _) =- if -- Constraint tuple:- | "GHC.Classes.(%" `Text.isInfixOf` nm1 -> True- -- Constraint class:- | "C:" `Text.isInfixOf` nm2 -> True- | otherwise -> False- where- nm1 = nameOcc nm0- nm2 = snd (Text.breakOnEnd "." nm1)--isClassConstraint _ = False----- | Turn a normalized recursive function, where the recursive calls only pass--- along the unchanged original arguments, into let-recursive function. This--- means that all recursive calls are replaced by the same variable reference as--- found in the body of the top-level let-expression.-recToLetRec :: HasCallStack => NormRewrite-recToLetRec (TransformContext is0 []) e = do- (fn,_) <- Lens.use curFun- tcm <- Lens.view tcCache- case splitNormalized tcm e of- Right (args,bndrs,res) -> do- let args' = map Var args- (toInline,others) = List.partition (eqApp tcm fn args' . snd) bndrs- resV = Var res- case (toInline,others) of- (_:_,_:_) -> do- let is1 = extendInScopeSetList is0 (args ++ map fst bndrs)- let substsInline = extendIdSubstList (mkSubst is1)- $ map (second (const resV)) toInline- others' = map (second (substTm "recToLetRec" substsInline))- others- changed $ mkLams (Letrec others' resV) args- _ -> return e- _ -> return e- where- -- This checks whether things are semantically equal. For example, say we- -- have:- --- -- x :: (a, (b, c))- --- -- and- --- -- y :: (a, (b, c))- --- -- If we can determine that 'y' is constructed solely using the- -- corresponding fields in 'x', then we can say they are semantically- -- equal. The algorithm below keeps track of what (sub)field it is- -- constructing, and checks if the field-expression projects the- -- corresponding (sub)field from the target variable.- --- -- TODO: See [Note: Breaks on constants and predetermined equality]- eqApp tcm v args (collectArgs -> (Var v',args'))- | isGlobalId v'- , v == v'- , let args2 = Either.lefts args'- , length args == length args2- = and (zipWith (eqArg tcm) args args2)- eqApp _ _ _ _ = False-- eqArg _ v1 v2@(Var {})- = v1 == v2- eqArg tcm v1 v2@(collectArgs -> (Data _, args'))- | let t1 = termType tcm v1- , let t2 = termType tcm v2- , t1 == t2- = if isClassConstraint t1 then- -- Class constraints are equal if their types are equal, so we can- -- take a shortcut here.- True- else- -- Check whether all arguments to the data constructor are projections- --- and (zipWith (eqDat v1) (map pure [0..]) (Either.lefts args'))- eqArg _ _ _- = False-- -- Recursively check whether a term /e/ is semantically equal to some variable /v/.- -- Currently it can only assert equality when /e/ is syntactically equal- -- to /v/, or is constructed out of projections of /v/, importantly:- --- -- [Note: Breaks on constants and predetermined equality]- -- This function currently breaks if:- --- -- * One or more subfields are constants. Constants might have been- -- inlined for the construction, instead of being a projection of the- -- target variable.- --- -- * One or more subfields are determined to be equal and one is simply- -- swapped / replaced by the other. For example, say we have- -- `x :: (a, a)`. If GHC determines that both elements of the tuple will- -- always be the same, it might replace the (semantically equal to 'x')- -- construction of `y` with `(fst x, fst x)`.- --- eqDat :: Term -> [Int] -> Term -> Bool- eqDat v fTrace (collectArgs -> (Data _, args)) =- and (zipWith (eqDat v) (map (:fTrace) [0..]) (Either.lefts args))- eqDat v1 fTrace v2 =- case stripProjection (reverse fTrace) v1 v2 of- Just [] -> True- _ -> False-- stripProjection :: [Int] -> Term -> Term -> Maybe [Int]- stripProjection fTrace0 vTarget0 (Case v _ [(DataPat _ _ xs, r)]) = do- -- Get projection made in subject of case:- fTrace1 <- stripProjection fTrace0 vTarget0 v-- -- Extract projection of this case statement. Subsequent calls to- -- 'stripProjection' will check if new target is actually used.- n <- headMaybe fTrace1- vTarget1 <- indexMaybe xs n- fTrace2 <- tailMaybe fTrace1-- stripProjection fTrace2 (Var vTarget1) r-- stripProjection fTrace (Var sTarget) (Var s) =- if sTarget == s then Just fTrace else Nothing-- stripProjection _fTrace _vTarget _v =- Nothing--recToLetRec _ e = return e---- | Inline a function with functional arguments-inlineHO :: HasCallStack => NormRewrite-inlineHO (TransformContext is0 _) e@(App _ _)- | (Var f, args, ticks) <- collectArgsTicks e- = do- tcm <- Lens.view tcCache- let hasPolyFunArgs = or (map (either (isPolyFun tcm) (const False)) args)- if hasPolyFunArgs- then do (cf,_) <- Lens.use curFun- isInlined <- zoomExtra (alreadyInlined f cf)- limit <- Lens.use (extra.inlineLimit)- if (Maybe.fromMaybe 0 isInlined) > limit- then do- lvl <- Lens.view dbgLevel- traceIf (lvl > DebugNone) ($(curLoc) ++ "InlineHO: " ++ show f ++ " already inlined " ++ show limit ++ " times in:" ++ show cf) (return e)- else do- bodyMaybe <- lookupVarEnv f <$> Lens.use bindings- case bodyMaybe of- Just (_,_,_,body) -> do- zoomExtra (addNewInline f cf)- -- See Note [AppProp no-shadow invariant]- changed (mkApps (mkTicks (deShadowTerm is0 body) ticks) args)- _ -> return e- else return e--inlineHO _ e = return e---- | Simplified CSE, only works on let-bindings, works from top to bottom-simpleCSE :: HasCallStack => NormRewrite-simpleCSE (TransformContext is0 _) e@(Letrec binders body) = do- let is1 = extendInScopeSetList is0 (map fst binders)- let (reducedBindings,body') = reduceBindersFix is1 binders body- if length binders /= length reducedBindings- then changed (Letrec reducedBindings body')- else return e--simpleCSE _ e = return e--reduceBindersFix- :: InScopeSet- -> [LetBinding]- -> Term- -> ([LetBinding],Term)-reduceBindersFix is binders body =- if length binders /= length reduced- then reduceBindersFix is reduced body'- else (binders,body)- where- (reduced,body') = reduceBinders is [] body binders--reduceBinders- :: InScopeSet- -> [LetBinding]- -> Term- -> [LetBinding]- -> ([LetBinding],Term)-reduceBinders _ processed body [] = (processed,body)-reduceBinders is processed body ((id_,expr):binders) = case List.find ((== expr) . snd) processed of- Just (id2,_) ->- let subst = extendIdSubst (mkSubst is) id_ (Var id2)- processed' = map (second (substTm "reduceBinders.processed" subst)) processed- binders' = map (second (substTm "reduceBinders.binders" subst)) binders- body' = substTm "reduceBinders.body" subst body- in reduceBinders is processed' body' binders'- Nothing -> reduceBinders is ((id_,expr):processed) body binders--reduceConst :: HasCallStack => NormRewrite-reduceConst ctx@(TransformContext is0 _) e@(App _ _)- | (Prim nm0 _, _) <- collectArgs e- = do- tcm <- Lens.view tcCache- bndrs <- Lens.use bindings- primEval <- Lens.view evaluator- ids <- Lens.use uniqSupply- let (ids1,ids2) = splitSupply ids- uniqSupply Lens..= ids2- gh <- Lens.use globalHeap- case whnf' primEval bndrs tcm gh ids1 is0 False e of- (gh',ph',e') -> do- globalHeap Lens..= gh'- bindPureHeap ctx tcm ph' $ \_ctx' -> case e' of- (collectArgs -> (Prim nm1 _, _)) | nm0 == nm1 -> return e- _ -> changed e'--reduceConst _ e = return e---- | Replace primitives by their "definition" if they would lead to let-bindings--- with a non-representable type when a function is in ANF. This happens for--- example when Clash.Size.Vector.map consumes or produces a vector of--- non-representable elements.------ Basically what this transformation does is replace a primitive the completely--- unrolled recursive definition that it represents. e.g.------ > zipWith ($) (xs :: Vec 2 (Int -> Int)) (ys :: Vec 2 Int)------ is replaced by:------ > let (x0 :: (Int -> Int)) = case xs of (:>) _ x xr -> x--- > (xr0 :: Vec 1 (Int -> Int)) = case xs of (:>) _ x xr -> xr--- > (x1 :: (Int -> Int)( = case xr0 of (:>) _ x xr -> x--- > (y0 :: Int) = case ys of (:>) _ y yr -> y--- > (yr0 :: Vec 1 Int) = case ys of (:>) _ y yr -> xr--- > (y1 :: Int = case yr0 of (:>) _ y yr -> y--- > in (($) x0 y0 :> ($) x1 y1 :> Nil)------ Currently, it only handles the following functions:------ * Clash.Sized.Vector.zipWith--- * Clash.Sized.Vector.map--- * Clash.Sized.Vector.traverse#--- * Clash.Sized.Vector.fold--- * Clash.Sized.Vector.foldr--- * Clash.Sized.Vector.dfold--- * Clash.Sized.Vector.(++)--- * Clash.Sized.Vector.head--- * Clash.Sized.Vector.tail--- * Clash.Sized.Vector.last--- * Clash.Sized.Vector.init--- * Clash.Sized.Vector.unconcat--- * Clash.Sized.Vector.transpose--- * Clash.Sized.Vector.replicate--- * Clash.Sized.Vector.replace_int--- * Clash.Sized.Vector.imap--- * Clash.Sized.Vector.dtfold--- * Clash.Sized.RTree.tdfold--- * Clash.Sized.RTree.treplicate--- * Clash.Sized.Internal.BitVector.split#--- * Clash.Sized.Internal.BitVector.eq#-reduceNonRepPrim :: HasCallStack => NormRewrite-reduceNonRepPrim c@(TransformContext is0 ctx) e@(App _ _) | (Prim nm _, args, ticks) <- collectArgsTicks e = do- tcm <- Lens.view tcCache- shouldReduce1 <- shouldReduce ctx- ultra <- Lens.use (extra.normalizeUltra)- let eTy = termType tcm e- case tyView eTy of- (TyConApp vecTcNm@(nameOcc -> "Clash.Sized.Vector.Vec")- [runExcept . tyNatSize tcm -> Right 0, aTy]) -> do- let (Just vecTc) = lookupUniqMap vecTcNm tcm- [nilCon,consCon] = tyConDataCons vecTc- nilE = mkVec nilCon consCon aTy 0 []- changed (mkTicks nilE ticks)- tv -> case nm of- "Clash.Sized.Vector.zipWith" | length args == 7 -> do- let [lhsElTy,rhsElty,resElTy,nTy] = Either.rights args- case runExcept (tyNatSize tcm nTy) of- Right n -> do- untranslatableTys <- mapM isUntranslatableType_not_poly [lhsElTy,rhsElty,resElTy]- if or untranslatableTys || shouldReduce1 || ultra || n < 2- then let [fun,lhsArg,rhsArg] = Either.lefts args- in (`mkTicks` ticks) <$>- reduceZipWith c n lhsElTy rhsElty resElTy fun lhsArg rhsArg- else return e- _ -> return e- "Clash.Sized.Vector.map" | length args == 5 -> do- let [argElTy,resElTy,nTy] = Either.rights args- case runExcept (tyNatSize tcm nTy) of- Right n -> do- untranslatableTys <- mapM isUntranslatableType_not_poly [argElTy,resElTy]- if or untranslatableTys || shouldReduce1 || ultra || n < 2- then let [fun,arg] = Either.lefts args- in (`mkTicks` ticks) <$> reduceMap c n argElTy resElTy fun arg- else return e- _ -> return e- "Clash.Sized.Vector.traverse#" | length args == 7 ->- let [aTy,fTy,bTy,nTy] = Either.rights args- in case runExcept (tyNatSize tcm nTy) of- Right n ->- let [dict,fun,arg] = Either.lefts args- in (`mkTicks` ticks) <$> reduceTraverse c n aTy fTy bTy dict fun arg- _ -> return e- "Clash.Sized.Vector.fold" | length args == 4 -> do- let [aTy,nTy] = Either.rights args- isPow2 x = x /= 0 && (x .&. (complement x + 1)) == x- untranslatableTy <- isUntranslatableType_not_poly aTy- case runExcept (tyNatSize tcm nTy) of- Right n | not (isPow2 (n + 1)) || untranslatableTy || shouldReduce1 || ultra || n == 0 ->- let [fun,arg] = Either.lefts args- in (`mkTicks` ticks) <$> reduceFold c (n + 1) aTy fun arg- _ -> return e- "Clash.Sized.Vector.foldr" | length args == 6 ->- let [aTy,bTy,nTy] = Either.rights args- in case runExcept (tyNatSize tcm nTy) of- Right n -> do- untranslatableTys <- mapM isUntranslatableType_not_poly [aTy,bTy]- if or untranslatableTys || shouldReduce1 || ultra- then let [fun,start,arg] = Either.lefts args- in (`mkTicks` ticks) <$> reduceFoldr c n aTy fun start arg- else return e- _ -> return e- "Clash.Sized.Vector.dfold" | length args == 8 ->- let ([_kn,_motive,fun,start,arg],[_mTy,nTy,aTy]) = Either.partitionEithers args- in case runExcept (tyNatSize tcm nTy) of- Right n -> (`mkTicks` ticks) <$> reduceDFold is0 n aTy fun start arg- _ -> return e- "Clash.Sized.Vector.++" | length args == 5 ->- let [nTy,aTy,mTy] = Either.rights args- [lArg,rArg] = Either.lefts args- in case (runExcept (tyNatSize tcm nTy), runExcept (tyNatSize tcm mTy)) of- (Right n, Right m)- | n == 0 -> changed rArg- | m == 0 -> changed lArg- | otherwise -> do- untranslatableTy <- isUntranslatableType_not_poly aTy- if untranslatableTy || shouldReduce1- then (`mkTicks` ticks) <$> reduceAppend is0 n m aTy lArg rArg- else return e- _ -> return e- "Clash.Sized.Vector.head" | length args == 3 -> do- let [nTy,aTy] = Either.rights args- [vArg] = Either.lefts args- case runExcept (tyNatSize tcm nTy) of- Right n -> do- untranslatableTy <- isUntranslatableType_not_poly aTy- if untranslatableTy || shouldReduce1- then (`mkTicks` ticks) <$> reduceHead is0 (n+1) aTy vArg- else return e- _ -> return e- "Clash.Sized.Vector.tail" | length args == 3 -> do- let [nTy,aTy] = Either.rights args- [vArg] = Either.lefts args- case runExcept (tyNatSize tcm nTy) of- Right n -> do- untranslatableTy <- isUntranslatableType_not_poly aTy- if untranslatableTy || shouldReduce1- then (`mkTicks` ticks) <$> reduceTail is0 (n+1) aTy vArg- else return e- _ -> return e- "Clash.Sized.Vector.last" | length args == 3 -> do- let [nTy,aTy] = Either.rights args- [vArg] = Either.lefts args- case runExcept (tyNatSize tcm nTy) of- Right n -> do- untranslatableTy <- isUntranslatableType_not_poly aTy- if untranslatableTy || shouldReduce1- then (`mkTicks` ticks) <$> reduceLast is0 (n+1) aTy vArg- else return e- _ -> return e- "Clash.Sized.Vector.init" | length args == 3 -> do- let [nTy,aTy] = Either.rights args- [vArg] = Either.lefts args- case runExcept (tyNatSize tcm nTy) of- Right n -> do- untranslatableTy <- isUntranslatableType_not_poly aTy- if untranslatableTy || shouldReduce1- then (`mkTicks` ticks) <$> reduceInit is0 (n+1) aTy vArg- else return e- _ -> return e- "Clash.Sized.Vector.unconcat" | length args == 6 -> do- let ([_knN,_sm,arg],[mTy,nTy,aTy]) = Either.partitionEithers args- case (runExcept (tyNatSize tcm nTy), runExcept (tyNatSize tcm mTy)) of- (Right n, Right 0) -> (`mkTicks` ticks) <$> reduceUnconcat n 0 aTy arg- _ -> return e- "Clash.Sized.Vector.transpose" | length args == 5 -> do- let ([_knN,arg],[mTy,nTy,aTy]) = Either.partitionEithers args- case (runExcept (tyNatSize tcm nTy), runExcept (tyNatSize tcm mTy)) of- (Right n, Right 0) -> (`mkTicks` ticks) <$> reduceTranspose n 0 aTy arg- _ -> return e- "Clash.Sized.Vector.replicate" | length args == 4 -> do- let ([_sArg,vArg],[nTy,aTy]) = Either.partitionEithers args- case runExcept (tyNatSize tcm nTy) of- Right n -> do- untranslatableTy <- isUntranslatableType_not_poly aTy- if untranslatableTy || shouldReduce1- then (`mkTicks` ticks) <$> reduceReplicate n aTy eTy vArg- else return e- _ -> return e- -- replace_int :: KnownNat n => Vec n a -> Int -> a -> Vec n a- "Clash.Sized.Vector.replace_int" | length args == 6 -> do- let ([_knArg,vArg,iArg,aArg],[nTy,aTy]) = Either.partitionEithers args- case runExcept (tyNatSize tcm nTy) of- Right n -> do- untranslatableTy <- isUntranslatableType_not_poly aTy- if untranslatableTy || shouldReduce1 || ultra- then (`mkTicks` ticks) <$> reduceReplace_int is0 n aTy eTy vArg iArg aArg- else return e- _ -> return e-- "Clash.Sized.Vector.index_int" | length args == 5 -> do- let ([_knArg,vArg,iArg],[nTy,aTy]) = Either.partitionEithers args- case runExcept (tyNatSize tcm nTy) of- Right n -> do- untranslatableTy <- isUntranslatableType_not_poly aTy- if untranslatableTy || shouldReduce1 || ultra- then (`mkTicks` ticks) <$> reduceIndex_int is0 n aTy vArg iArg- else return e- _ -> return e-- "Clash.Sized.Vector.imap" | length args == 6 -> do- let [nTy,argElTy,resElTy] = Either.rights args- case runExcept (tyNatSize tcm nTy) of- Right n -> do- untranslatableTys <- mapM isUntranslatableType_not_poly [argElTy,resElTy]- if or untranslatableTys || shouldReduce1 || ultra || n < 2- then let [_,fun,arg] = Either.lefts args- in (`mkTicks` ticks) <$> reduceImap c n argElTy resElTy fun arg- else return e- _ -> return e- "Clash.Sized.Vector.dtfold" | length args == 8 ->- let ([_kn,_motive,lrFun,brFun,arg],[_mTy,nTy,aTy]) = Either.partitionEithers args- in case runExcept (tyNatSize tcm nTy) of- Right n -> (`mkTicks` ticks) <$> reduceDTFold is0 n aTy lrFun brFun arg- _ -> return e-- "Clash.Sized.Vector.reverse"- | ultra- , ([vArg],[nTy,aTy]) <- Either.partitionEithers args- , Right n <- runExcept (tyNatSize tcm nTy)- -> (`mkTicks` ticks) <$> reduceReverse is0 n aTy vArg-- "Clash.Sized.RTree.tdfold" | length args == 8 ->- let ([_kn,_motive,lrFun,brFun,arg],[_mTy,nTy,aTy]) = Either.partitionEithers args- in case runExcept (tyNatSize tcm nTy) of- Right n -> (`mkTicks` ticks) <$> reduceTFold is0 n aTy lrFun brFun arg- _ -> return e- "Clash.Sized.RTree.treplicate" | length args == 4 -> do- let ([_sArg,vArg],[nTy,aTy]) = Either.partitionEithers args- case runExcept (tyNatSize tcm nTy) of- Right n -> do- untranslatableTy <- isUntranslatableType False aTy- if untranslatableTy || shouldReduce1- then (`mkTicks` ticks) <$> reduceTReplicate n aTy eTy vArg- else return e- _ -> return e- "Clash.Sized.Internal.BitVector.split#" | length args == 4 -> do- let ([_knArg,bvArg],[nTy,mTy]) = Either.partitionEithers args- case (runExcept (tyNatSize tcm nTy), runExcept (tyNatSize tcm mTy), tv) of- (Right n, Right m, TyConApp tupTcNm [lTy,rTy])- | n == 0 -> do- let (Just tupTc) = lookupUniqMap tupTcNm tcm- [tupDc] = tyConDataCons tupTc- tup = mkApps (Data tupDc)- [Right lTy- ,Right rTy- ,Left bvArg- ,Left (removedTm rTy)- ]-- changed (mkTicks tup ticks)- | m == 0 -> do- let (Just tupTc) = lookupUniqMap tupTcNm tcm- [tupDc] = tyConDataCons tupTc- tup = mkApps (Data tupDc)- [Right lTy- ,Right rTy- ,Left (removedTm lTy)- ,Left bvArg- ]-- changed (mkTicks tup ticks)- _ -> return e- "Clash.Sized.Internal.BitVector.eq#"- | ([_,_],[nTy]) <- Either.partitionEithers args- , Right 0 <- runExcept (tyNatSize tcm nTy)- , TyConApp boolTcNm [] <- tv- -> let (Just boolTc) = lookupUniqMap boolTcNm tcm- [_falseDc,trueDc] = tyConDataCons boolTc- in changed (mkTicks (Data trueDc) ticks)- _ -> return e- where- isUntranslatableType_not_poly t = do- u <- isUntranslatableType False t- if u- then return (null $ Lens.toListOf typeFreeVars t)- else return False--reduceNonRepPrim _ e = return e---- | This transformation lifts applications of global binders out of--- alternatives of case-statements.------ e.g. It converts:------ @--- case x of--- A -> f 3 y--- B -> f x x--- C -> h x--- @------ into:------ @--- let f_arg0 = case x of {A -> 3; B -> x}--- f_arg1 = case x of {A -> y; B -> x}--- f_out = f f_arg0 f_arg1--- in case x of--- A -> f_out--- B -> f_out--- C -> h x--- @-disjointExpressionConsolidation :: HasCallStack => NormRewrite-disjointExpressionConsolidation ctx@(TransformContext is0 _) e@(Case _scrut _ty _alts@(_:_:_)) = do- (_,collected) <- collectGlobals is0 [] [] e- let disJoint = filter (isDisjoint . snd . snd) collected- if null disJoint- then return e- else do- exprs <- mapM (mkDisjointGroup is0) disJoint- tcm <- Lens.view tcCache- lids <- Monad.zipWithM (mkFunOut is0 tcm) disJoint exprs- let substitution = zip (map fst disJoint) (map Var lids)- subsMatrix = l2m substitution- (exprs',_) <- unzip <$> Monad.zipWithM- (\s (e',seen) -> collectGlobals is0 s seen e')- subsMatrix- exprs- (e',_) <- collectGlobals is0 substitution [] e- let lb = Letrec (zip lids exprs') e'- lb' <- bottomupR deadCode ctx lb- changed lb'- where- mkFunOut isN tcm (fun,_) (e',_) = do- let ty = termType tcm e'- nm = case collectArgs fun of- (Var v,_) -> nameOcc (varName v)- (Prim nm' _,_) -> nm'- _ -> "complex_expression_"- nm'' = last (Text.splitOn "." nm) `Text.append` "Out"- mkInternalVar isN nm'' ty-- l2m = go []- where- go _ [] = []- go xs (y:ys) = (xs ++ ys) : go (xs ++ [y]) ys--disjointExpressionConsolidation _ e = return e---- | Given a function in the desired normal form, inline all the following--- let-bindings:------ Let-bindings with an internal name that is only used once, where it binds:--- * a primitive that will be translated to an HDL expression (as opposed to--- a HDL declaration)--- * a projection case-expression (1 alternative)--- * a data constructor-inlineCleanup :: HasCallStack => NormRewrite-inlineCleanup (TransformContext is0 _) (Letrec binds body) = do- prims <- Lens.use (extra.primitives)- -- For all let-bindings, count the number of times they are referenced.- -- We only inline let-bindings which are referenced only once, otherwise- -- we would lose sharing.- -- let allOccs = List.foldl' (HashMap.unionWith (+)) HashMap.empty- -- $ map ( List.foldl' countOcc HashMap.empty- -- . Lens.toListOf termFreeIds . unembed . snd) binds- let is1 = extendInScopeSetList is0 (map fst binds)- let allOccs = List.foldl' (unionVarEnvWith (+)) emptyVarEnv- $ map (Lens.foldMapByOf freeLocalIds (unionVarEnvWith (+))- emptyVarEnv (`unitVarEnv` 1) . snd)- binds- bodyFVs = Lens.foldMapOf freeLocalIds unitVarSet body- (il,keep) = List.partition (isInteresting allOccs prims bodyFVs) binds- keep' = inlineBndrs is1 keep il- if null il then return (Letrec binds body)- else changed (Letrec keep' body)- where- -- Determine whether a let-binding is interesting to inline- isInteresting- :: VarEnv Int- -> CompiledPrimMap- -> VarSet- -> (Id, Term)- -> Bool- isInteresting allOccs prims bodyFVs (id_,(fst.collectArgs) -> tm)- | nameSort (varName id_) /= User- , id_ `notElemVarSet` bodyFVs- = case tm of- Prim nm _- | Just (extractPrim -> Just p@(BlackBox {})) <- HashMap.lookup nm prims- , TExpr <- kind p- , Just occ <- lookupVarEnv id_ allOccs- , occ < 2- -> True- Case _ _ [_] -> True- Data _ -> True- _ -> False- | id_ `notElemVarSet` bodyFVs- = case tm of- Case _ _ [(DataPat dcE _ _,_)]- -> let nm = (nameOcc (dcName dcE))- in -- Inlines WW projection that exposes internals of the BitVector types- nm == "Clash.Sized.Internal.BitVector.BV" ||- nm == "Clash.Sized.Internal.BitVector.Bit" ||- -- Inlines projections out of constraint-tuples (e.g. HiddenClockReset)- "GHC.Classes" `Text.isPrefixOf` nm- _ -> False-- isInteresting _ _ _ _ = False-- -- Inline let-bindings we want to inline into let-bindings we want to keep.- inlineBndrs- :: InScopeSet- -> [(Id, Term)]- -- let-bindings we keep- -> [(Id, Term)]- -- let-bindings we want to inline- -> [(Id, Term)]- inlineBndrs _ keep [] = keep- inlineBndrs isN keep ((v,e):il) =- let subst = extendIdSubst (mkSubst isN) v e- in if v `localIdOccursIn` e -- don't inline recursive binders- then inlineBndrs isN ((v,e):keep) il- else inlineBndrs isN- (map (second (substTm "inlineCleanup.inlineBndrs" subst)) keep)- (map (second (substTm "inlineCleanup.inlineBndrs" subst)) il)- -- We must not forget to inline the /current/ @to-inline@ let-binding into- -- the list of /remaining/ @to-inline@ let-bindings, because it might- -- only occur in /remaining/ @to-inline@ bindings. If we don't, we would- -- introduce free variables, because the @to-inline@ bindings are removed.--inlineCleanup _ e = return e---- | Flatten's letrecs after `inlineCleanup`------ `inlineCleanup` sometimes exposes additional possibilities for `caseCon`,--- which then introduces let-bindings in what should be ANF. This transformation--- flattens those nested let-bindings again.------ NB: must only be called in the cleaning up phase.-flattenLet :: HasCallStack => NormRewrite-flattenLet (TransformContext is0 _) letrec@(Letrec _ _) = do- let (is2, Letrec binds body) = freshenTm is0 letrec- bodyOccs = Lens.foldMapByOf- freeLocalIds (unionVarEnvWith (+))- emptyVarEnv (`unitVarEnv` (1 :: Int))- body- binds' <- concat <$> mapM (go is2) binds- case binds' of- -- inline binders into the body when there's only a single binder, and only- -- if that binder doesn't perform any work or is only used once in the body- [(id',e')] | Just occ <- lookupVarEnv id' bodyOccs, isWorkFree e' || occ < 2 ->- if id' `localIdOccursIn` e'- -- Except when the binder is recursive!- then return (Letrec binds' body)- else let subst = extendIdSubst (mkSubst is2) id' e'- in changed (substTm "flattenLet" subst body)- _ -> return (Letrec binds' body)- where- go :: InScopeSet -> LetBinding -> NormalizeSession [LetBinding]- go isN (id_,collectTicks -> (Letrec binds' body',ticks)) = do- let bodyOccs = Lens.foldMapByOf- freeLocalIds (unionVarEnvWith (+))- emptyVarEnv (`unitVarEnv` (1 :: Int))- body'- (srcTicks,nmTicks) = partitionTicks ticks- -- Distribute the name ticks of the let-expression over all the bindings- map (second (`mkTicks` nmTicks)) <$> case binds' of- -- inline binders into the body when there's only a single binder, and- -- only if that binder doesn't perform any work or is only used once in- -- the body- [(id',e')] | Just occ <- lookupVarEnv id' bodyOccs, isWorkFree e' || occ < 2 ->- if id' `localIdOccursIn` e'- -- Except when the binder is recursive!- then changed [(id',e'),(id_, body')]- else let subst = extendIdSubst (mkSubst isN) id' e'- in changed [(id_- -- Only apply srcTicks to the body- ,mkTicks (substTm "flattenLetGo" subst body')- srcTicks)]- bs -> changed (bs ++ [(id_- -- Only apply srcTicks to the body- ,mkTicks body' srcTicks)])- go _ b = return [b]--flattenLet _ e = return e+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-}++module Clash.Normalize.Transformations+ ( caseLet+ , caseCon+ , caseCase+ , caseElemNonReachable+ , elemExistentials+ , inlineNonRep+ , inlineOrLiftNonRep+ , typeSpec+ , nonRepSpec+ , etaExpansionTL+ , nonRepANF+ , bindConstantVar+ , constantSpec+ , makeANF+ , deadCode+ , topLet+ , recToLetRec+ , inlineWorkFree+ , inlineHO+ , inlineSmall+ , simpleCSE+ , reduceConst+ , reduceNonRepPrim+ , caseFlat+ , disjointExpressionConsolidation+ , removeUnusedExpr+ , inlineCleanup+ , flattenLet+ , splitCastWork+ , inlineCast+ , caseCast+ , letCast+ , eliminateCastCast+ , argCastSpec+ , etaExpandSyn+ , appPropFast+ , separateArguments+ , separateLambda+ , xOptimize+ )+where++import Control.Exception (throw)+import Control.Lens (_2)+import qualified Control.Lens as Lens+import qualified Control.Monad as Monad+import Control.Monad.State (StateT (..), modify)+import Control.Monad.State.Strict (evalState)+import Control.Monad.Writer (lift, listen)+import Control.Monad.Trans.Except (runExcept)+import Data.Coerce (coerce)+import qualified Data.Either as Either+import qualified Data.HashMap.Lazy as HashMap+import qualified Data.HashMap.Strict as HashMapS+import qualified Data.List as List+import Data.List ((\\))+import qualified Data.Maybe as Maybe+import qualified Data.Monoid as Monoid+import qualified Data.Primitive.ByteArray as BA+import qualified Data.Text as Text+import qualified Data.Vector.Primitive as PV+import Debug.Trace+import GHC.Integer.GMP.Internals (Integer (..), BigNat (..))++import BasicTypes (InlineSpec (..))++import Clash.Annotations.Primitive (extractPrim)+import Clash.Core.DataCon (DataCon (..))+import Clash.Core.Name+ (Name (..), NameSort (..), mkUnsafeSystemName, nameOcc)+import Clash.Core.FreeVars+ (localIdOccursIn, localIdsDoNotOccurIn, freeLocalIds, termFreeTyVars,+ typeFreeVars, localVarsDoNotOccurIn, localIdDoesNotOccurIn,+ countFreeOccurances)+import Clash.Core.Literal (Literal (..))+import Clash.Core.Pretty (showPpr)+import Clash.Core.Subst+ (Subst, substTm, mkSubst, extendIdSubst, extendIdSubstList, extendTvSubst,+ extendTvSubstList, freshenTm, substTyInVar, deShadowTerm, deShadowAlt,+ deshadowLetExpr)+import Clash.Core.Term+ ( LetBinding, Pat (..), Term (..), CoreContext (..), PrimInfo (..)+ , TickInfo(..) , WorkInfo(WorkConstant), Alt, TickInfo+ , isLambdaBodyCtx, isTickCtx, collectArgs+ , collectArgsTicks, collectTicks , partitionTicks+ )+import Clash.Core.Type (Type (..), TypeView (..), applyFunTy,+ isPolyFunCoreTy, isClassTy,+ normalizeType, splitFunForallTy,+ splitFunTy,+ tyView, mkPolyFunTy, coreView,+ LitTy (..), coreView1)+import Clash.Core.TyCon (TyConMap, tyConDataCons)+import Clash.Core.Util+ (isCon, isFun, isLet, isPolyFun, isPrim,+ isSignalType, isVar, mkApps, mkLams, mkVec, piResultTy, termSize, termType,+ tyNatSize, patVars, isAbsurdAlt, altEqs, substInExistentialsList,+ solveNonAbsurds, patIds, isLocalVar, undefinedTm, stripTicks, mkTicks,+ shouldSplit, inverseTopSortLetBindings)+import Clash.Core.Var+ (Id, TyVar, Var (..), isGlobalId, isLocalId, mkLocalId)+import Clash.Core.VarEnv+ (InScopeSet, VarEnv, VarSet, elemVarSet,+ emptyVarEnv, extendInScopeSet, extendInScopeSetList, lookupVarEnv,+ notElemVarSet, unionVarEnvWith, unionInScope, unitVarEnv,+ unitVarSet, mkVarSet, mkInScopeSet, uniqAway, elemInScopeSet, elemVarEnv,+ foldlWithUniqueVarEnv', lookupVarEnvDirectly, extendVarEnv, unionVarEnv,+ eltsVarEnv, mkVarEnv, eltsVarSet)+import Clash.Driver.Types (Binding(..), DebugLevel (..))+import Clash.Netlist.BlackBox.Types (Element(Err))+import Clash.Netlist.BlackBox.Util (getUsedArguments)+import Clash.Netlist.Types (BlackBox(..), HWType (..), FilteredHWType(..))+import Clash.Netlist.Util+ (coreTypeToHWType, representableType, splitNormalized, bindsExistentials)+import Clash.Normalize.DEC+import Clash.Normalize.PrimitiveReductions+import Clash.Normalize.Types+import Clash.Normalize.Util+import Clash.Primitives.Types+ (Primitive(..), TemplateKind(TExpr), CompiledPrimMap, UsedArguments(..))+import Clash.Rewrite.Combinators+import Clash.Rewrite.Types+import Clash.Rewrite.Util+import Clash.Unique (Unique, lookupUniqMap)+import Clash.Util++inlineOrLiftNonRep :: HasCallStack => NormRewrite+inlineOrLiftNonRep ctx eLet@(Letrec _ body) =+ inlineOrLiftBinders nonRepTest inlineTest ctx eLet+ where+ bodyFreeOccs = countFreeOccurances body++ nonRepTest :: (Id, Term) -> RewriteMonad extra Bool+ nonRepTest (Id {varType = ty}, _)+ = not <$> (representableType <$> Lens.view typeTranslator+ <*> Lens.view customReprs+ <*> pure False+ <*> Lens.view tcCache+ <*> pure ty)+ nonRepTest _ = return False++ inlineTest :: Term -> (Id, Term) -> Bool+ inlineTest e (id_, e') =+ -- We do __NOT__ inline:+ not $ or+ [ -- 1. recursive let-binders+ -- id_ `localIdOccursIn` e' -- <= already checked in inlineOrLiftBinders+ -- 2. join points (which are not void-wrappers)+ isJoinPointIn id_ e && not (isVoidWrapper e')+ -- 3. binders that are used more than once in the body, because+ -- it makes CSE a whole lot more difficult.+ --+ -- XXX: Check whether we can extend this to the binders as well+ , maybe False (>1) (lookupVarEnv id_ bodyFreeOccs)+ ]++inlineOrLiftNonRep _ e = return e+{-# SCC inlineOrLiftNonRep #-}++{- [Note] join points and void wrappers+Join points are functions that only occur in tail-call positions within an+expression, and only when they occur in a tail-call position more than once.++Normally bindNonRep binds/inlines all non-recursive local functions. However,+doing so for join points would significantly increase compilation time, so we+avoid it. The only exception to this rule are so-called void wrappers. Void+wrappers are functions of the form:++> \(w :: Void) -> f a b c++i.e. a wrapper around the function 'f' where the argument 'w' is not used. We+do bind/line these join-points because these void-wrappers interfere with the+'disjoint expression consolidation' (DEC) and 'common sub-expression elimination'+(CSE) transformation, sometimes resulting in circuits that are twice as big+as they'd need to be.+-}++-- | Specialize functions on their type+typeSpec :: HasCallStack => NormRewrite+typeSpec ctx e@(TyApp e1 ty)+ | (Var {}, args) <- collectArgs e1+ , null $ Lens.toListOf typeFreeVars ty+ , (_, []) <- Either.partitionEithers args+ = specializeNorm ctx e++typeSpec _ e = return e+{-# SCC typeSpec #-}++-- | Specialize functions on their non-representable argument+nonRepSpec :: HasCallStack => NormRewrite+nonRepSpec ctx e@(App e1 e2)+ | (Var {}, args) <- collectArgs e1+ , (_, []) <- Either.partitionEithers args+ , null $ Lens.toListOf termFreeTyVars e2+ = do tcm <- Lens.view tcCache+ let e2Ty = termType tcm e2+ let localVar = isLocalVar e2+ nonRepE2 <- not <$> (representableType <$> Lens.view typeTranslator+ <*> Lens.view customReprs+ <*> pure False+ <*> Lens.view tcCache+ <*> pure e2Ty)+ if nonRepE2 && not localVar+ then do+ e2' <- inlineInternalSpecialisationArgument e2+ specializeNorm ctx (App e1 e2')+ else return e+ where+ -- | If the argument on which we're specialising ia an internal function,+ -- one created by the compiler, then inline that function before we+ -- specialise.+ --+ -- We need to do this because otherwise the specialisation history won't+ -- recognize the new specialisation argument as something the function has+ -- already been specialized on+ inlineInternalSpecialisationArgument+ :: Term+ -> NormalizeSession Term+ inlineInternalSpecialisationArgument app+ | (Var f,fArgs,ticks) <- collectArgsTicks app+ = do+ fTmM <- lookupVarEnv f <$> Lens.use bindings+ case fTmM of+ Just b+ | nameSort (varName (bindingId b)) == Internal+ -> censor (const mempty)+ (topdownR appPropFast ctx+ (mkApps (mkTicks (bindingTerm b) ticks) fArgs))+ _ -> return app+ | otherwise = return app++nonRepSpec _ e = return e+{-# SCC nonRepSpec #-}++-- | Lift the let-bindings out of the subject of a Case-decomposition+caseLet :: HasCallStack => NormRewrite+caseLet (TransformContext is0 _) (Case (collectTicks -> (Letrec xes e,ticks)) ty alts) = do+ -- Note [CaseLet deshadow]+ -- Imagine+ --+ -- @+ -- case (let x = u in e) of {p -> a}+ -- @+ --+ -- where `a` has a free variable named `x`.+ --+ -- Simply transforming the above to:+ --+ -- @+ -- let x = u in case e of {p -> a}+ -- @+ --+ -- would be very bad, because now the let-binding captures the free x variable+ -- in a.+ --+ -- We must therefor rename `x` so that it doesn't capture the free variables+ -- in the alternative:+ --+ -- @+ -- let x1 = u[x:=x1] in case e[x:=x1] of {p -> a}+ -- @+ --+ -- It is safe to over-approximate the free variables in `a` by simply taking+ -- the current InScopeSet.+ let (xes1,e1) = deshadowLetExpr is0 xes e+ changed (Letrec (map (second (`mkTicks` ticks)) xes1)+ (Case (mkTicks e1 ticks) ty alts))++caseLet _ e = return e+{-# SCC caseLet #-}++-- | Remove non-reachable alternatives. For example, consider:+--+-- data STy ty where+-- SInt :: Int -> STy Int+-- SBool :: Bool -> STy Bool+--+-- f :: STy ty -> ty+-- f (SInt b) = b + 1+-- f (SBool True) = False+-- f (SBool False) = True+-- {-# NOINLINE f #-}+--+-- g :: STy Int -> Int+-- g = f+--+-- @f@ is always specialized on @STy Int@. The SBool alternatives are therefore+-- unreachable. Additional information can be found at:+-- https://github.com/clash-lang/clash-compiler/pull/465+caseElemNonReachable :: HasCallStack => NormRewrite+caseElemNonReachable _ case0@(Case scrut altsTy alts0) = do+ tcm <- Lens.view tcCache++ let (altsAbsurd, altsOther) = List.partition (isAbsurdAlt tcm) alts0+ case altsAbsurd of+ [] -> return case0+ _ -> changed =<< caseOneAlt (Case scrut altsTy altsOther)++caseElemNonReachable _ e = return e+{-# SCC caseElemNonReachable #-}++-- | Tries to eliminate existentials by using heuristics to determine what the+-- existential should be. For example, consider Vec:+--+-- data Vec :: Nat -> Type -> Type where+-- Nil :: Vec 0 a+-- Cons x xs :: a -> Vec n a -> Vec (n + 1) a+--+-- Thus, 'null' (annotated with existentials) could look like:+--+-- null :: forall n . Vec n Bool -> Bool+-- null v =+-- case v of+-- Nil {n ~ 0} -> True+-- Cons {n1:Nat} {n~n1+1} (x :: a) (xs :: Vec n1 a) -> False+--+-- When it's applied to a vector of length 5, this becomes:+--+-- null :: Vec 5 Bool -> Bool+-- null v =+-- case v of+-- Nil {5 ~ 0} -> True+-- Cons {n1:Nat} {5~n1+1} (x :: a) (xs :: Vec n1 a) -> False+--+-- This function solves 'n1' and replaces every occurrence with its solution. A+-- very limited number of solutions are currently recognized: only adds (such+-- as in the example) will be solved.+elemExistentials :: HasCallStack => NormRewrite+elemExistentials (TransformContext is0 _) (Case scrut altsTy alts0) = do+ tcm <- Lens.view tcCache++ alts1 <- mapM (go is0 tcm) alts0+ caseOneAlt (Case scrut altsTy alts1)++ where+ -- Eliminate free type variables if possible+ go :: InScopeSet -> TyConMap -> (Pat, Term) -> NormalizeSession (Pat, Term)+ go is2 tcm alt@(DataPat dc exts0 xs0, term0) =+ case solveNonAbsurds tcm (altEqs tcm alt) of+ -- No equations solved:+ [] -> return alt+ -- One or more equations solved:+ sols ->+ changed =<< go is2 tcm (DataPat dc exts1 xs1, term1)+ where+ -- Substitute solution in existentials and applied types+ is3 = extendInScopeSetList is2 exts0+ xs1 = map (substTyInVar (extendTvSubstList (mkSubst is3) sols)) xs0+ exts1 = substInExistentialsList is2 exts0 sols++ -- Substitute solution in term.+ is4 = extendInScopeSetList is3 xs1+ subst = extendTvSubstList (mkSubst is4) sols+ term1 = substTm "Replacing tyVar due to solved eq" subst term0++ go _ _ alt = return alt++elemExistentials _ e = return e+{-# SCC elemExistentials #-}++-- | Move a Case-decomposition from the subject of a Case-decomposition to the alternatives+caseCase :: HasCallStack => NormRewrite+caseCase (TransformContext is0 _) e@(Case (stripTicks -> Case scrut alts1Ty alts1) alts2Ty alts2)+ = do+ ty1Rep <- representableType <$> Lens.view typeTranslator+ <*> Lens.view customReprs+ <*> pure False+ <*> Lens.view tcCache+ <*> pure alts1Ty+ if not ty1Rep+ -- Deshadow to prevent accidental capture of free variables of inner+ -- case. Imagine:+ --+ -- case (case a of {x -> x}) of {_ -> x}+ --+ -- 'x' is introduced the inner 'case' and used (as a free variable) in+ -- the outer one. The goal of 'caseCase' is to rewrite cases such that+ -- their subjects aren't cases. This is achieved by 'pushing' the outer+ -- case to all the alternatives of the inner one. Naively doing so in+ -- this example would cause an accidental capture:+ --+ -- case a of {x -> case x of {_ -> x}}+ --+ -- Suddenly, the 'x' in the alternative of the inner case statement+ -- refers to the one introduced by the outer one, instead of being a+ -- free variable. To prevent this, we deshadow the alternatives of the+ -- original inner case. We now end up with:+ --+ -- case a of {x1 -> case x1 of {_ -> x}}+ --+ then let newAlts = map+ (second (\altE -> Case altE alts2Ty alts2))+ (map (deShadowAlt is0) alts1)+ in changed $ Case scrut alts2Ty newAlts+ else return e++caseCase _ e = return e+{-# SCC caseCase #-}++-- | Inline function with a non-representable result if it's the subject+-- of a Case-decomposition+inlineNonRep :: HasCallStack => NormRewrite+inlineNonRep _ e@(Case scrut altsTy alts)+ | (Var f, args,ticks) <- collectArgsTicks scrut+ , isGlobalId f+ = do+ (cf,_) <- Lens.use curFun+ isInlined <- zoomExtra (alreadyInlined f cf)+ limit <- Lens.use (extra.inlineLimit)+ tcm <- Lens.view tcCache+ let scrutTy = termType tcm scrut+ noException = not (exception tcm scrutTy)+ if noException && (Maybe.fromMaybe 0 isInlined) > limit+ then+ trace (concat [ $(curLoc) ++ "InlineNonRep: " ++ showPpr (varName f)+ ," already inlined " ++ show limit ++ " times in:"+ , showPpr (varName cf)+ , "\nType of the subject is: " ++ showPpr scrutTy+ , "\nFunction " ++ showPpr (varName cf)+ , " will not reach a normal form, and compilation"+ , " might fail."+ , "\nRun with '-fclash-inline-limit=N' to increase"+ , " the inlining limit to N."+ ])+ (return e)+ else do+ bodyMaybe <- lookupVarEnv f <$> Lens.use bindings+ nonRepScrut <- not <$> (representableType <$> Lens.view typeTranslator+ <*> Lens.view customReprs+ <*> pure False+ <*> Lens.view tcCache+ <*> pure scrutTy)+ case (nonRepScrut, bodyMaybe) of+ (True,Just b) -> do+ Monad.when noException (zoomExtra (addNewInline f cf))++ let scrutBody0 = mkTicks (bindingTerm b) (mkInlineTick f : ticks)+ let scrutBody1 = mkApps scrutBody0 args++ changed $ Case scrutBody1 altsTy alts++ _ -> return e+ where+ exception = isClassTy++inlineNonRep _ e = return e+{-# SCC inlineNonRep #-}++-- | Specialize a Case-decomposition (replace by the RHS of an alternative) if+-- the subject is (an application of) a DataCon; or if there is only a single+-- alternative that doesn't reference variables bound by the pattern.+--+-- Note [CaseCon deshadow]+--+-- Imagine:+--+-- @+-- case D (f a b) (g x y) of+-- D a b -> h a+-- @+--+-- rewriting this to:+--+-- @+-- let a = f a b+-- in h a+-- @+--+-- is very bad because the newly introduced let-binding now captures the free+-- variable 'a' in 'f a b'.+--+-- instead me must rewrite to:+--+-- @+-- let a1 = f a b+-- in h a1+-- @+caseCon :: HasCallStack => NormRewrite+caseCon ctx@(TransformContext is0 _) e@(Case subj ty alts) = do+ tcm <- Lens.view tcCache+ case collectArgsTicks subj of+ -- The subject is an applied data constructor+ (Data dc, args, ticks) -> case List.find (equalCon . fst) alts of+ Just (DataPat _ tvs xs, altE) -> do+ let is1 = extendInScopeSetList (extendInScopeSetList is0 tvs) xs+ let fvs = Lens.foldMapOf freeLocalIds unitVarSet altE+ (binds,_) = List.partition ((`elemVarSet` fvs) . fst)+ $ zip xs (Either.lefts args)+ binds1 = map (second (`mkTicks` ticks)) binds+ altE1 = case binds1 of+ [] -> altE+ _ ->+ -- See Note [CaseCon deshadow]+ let+ ((is3,substIds),binds2) = List.mapAccumL newBinder (is1,[]) binds1+ subst = extendIdSubstList (mkSubst is3) substIds+ body = substTm "caseCon0" subst altE+ in+ case Maybe.catMaybes binds2 of+ [] -> body+ binds3 -> Letrec binds3 body+ -- Use the original inScopeSet 'is0' here, not the extended inScopeSet+ -- 'is1', otherwise we'd make the "caseCon1" substitution substitute+ -- free variables that were shadowed by the pattern!+ let subst = extendTvSubstList (mkSubst is0)+ $ zip tvs (drop (length (dcUnivTyVars dc)) (Either.rights args))+ changed (substTm "caseCon1" subst altE1)+ _ -> case alts of+ -- In Core, default patterns always come first, so we match against+ -- that if there is one, and we couldn't match with any of the data+ -- patterns.+ ((DefaultPat,altE):_) -> changed altE+ _ -> changed (undefinedTm ty)+ where+ -- Check whether the pattern matches the data constructor+ equalCon (DataPat dcPat _ _) = dcTag dc == dcTag dcPat+ equalCon _ = False++ -- Decide whether the applied arguments of the data constructor should+ -- be let-bound, or substituted into the alternative. We decide this+ -- based on the fact on whether the argument has the potential to make+ -- the circuit larger than needed if we were to duplicate that argument.+ newBinder (isN0,substN) (x,arg)+ | isWorkFree arg+ = ((isN0,(x,arg):substN),Nothing)+ | otherwise+ = let x' = uniqAway isN0 x+ isN1 = extendInScopeSet isN0 x'+ in ((isN1,(x,Var x'):substN),Just (x',arg))+++ -- The subject is a literal+ (Literal l,_,_) -> case List.find (equalLit . fst) alts of+ Just (LitPat _,altE) -> changed altE+ _ -> matchLiteralContructor e l alts+ where+ equalLit (LitPat l') = l == l'+ equalLit _ = False+++ -- The subject is an applied primitive+ (Prim _,_,_) ->+ -- We try to reduce the applied primitive to WHNF+ whnfRW True ctx subj $ \ctx1 subj1 -> case collectArgsTicks subj1 of+ -- WHNF of subject is a literal, try `caseCon` with that+ (Literal l,_,_) -> caseCon ctx1 (Case (Literal l) ty alts)+ -- WHNF of subject is a data-constructor, try `caseCon` with that+ (Data _,_,_) -> caseCon ctx1 (Case subj1 ty alts)+#if MIN_VERSION_ghc(8,2,2)+ -- WHNF of subject is _|_, in the form of `absentError`: that means that+ -- the entire case-expression is evaluates to _|_+ (Prim pInfo,_:msgOrCallStack:_,ticks)+ | primName pInfo == "Control.Exception.Base.absentError" ->+ let e1 = mkApps (mkTicks (Prim pInfo) ticks)+ [Right ty,msgOrCallStack]+ in changed e1+#endif+ -- WHNF of subject is _|_, in the form of `absentError`, `patError`,+ -- or `undefined`: that means the entire case-expression is _|_+ (Prim pInfo,repTy:_:msgOrCallStack:_,ticks)+ | primName pInfo `elem` ["Control.Exception.Base.patError"+#if !MIN_VERSION_ghc(8,2,2)+ ,"Control.Exception.Base.absentError"+#endif+ ,"GHC.Err.undefined"] ->+ let e1 = mkApps (mkTicks (Prim pInfo) ticks)+ [repTy,Right ty,msgOrCallStack]+ in changed e1+ -- WHNF of subject is _|_, in the form of our internal _|_-values: that+ -- means the entire case-expression is _|_+ (Prim pInfo,[_],ticks)+ | primName pInfo `elem` [ "Clash.Transformations.undefined"+ , "Clash.GHC.Evaluator.undefined"+ , "EmptyCase"] ->+ let e1 = mkApps (mkTicks (Prim pInfo) ticks) [Right ty]+ in changed e1+ -- WHNF of subject is non of the above, so either a variable reference,+ -- or a primitive for which the evaluator doesn't have any evaluation+ -- rules.+ _ -> do+ let subjTy = termType tcm subj+ tran <- Lens.view typeTranslator+ reprs <- Lens.view customReprs+ case (`evalState` HashMapS.empty) (coreTypeToHWType tran reprs tcm subjTy) of+ Right (FilteredHWType (Void (Just hty)) _areVoids)+ | hty `elem` [BitVector 0, Unsigned 0, Signed 0, Index 1]+ -- If we know that the type of the subject is zero-bits wide and+ -- one of the Clash number types. Then the only valid alternative is+ -- the one that can match on the literal "0", so try 'caseCon' with+ -- that.+ -> caseCon ctx1 (Case (Literal (IntegerLiteral 0)) ty alts)+ _ -> do+ let ret = caseOneAlt e+ -- Otherwise check whether the entire case-expression has a single+ -- alternative, and pick that one.+ lvl <- Lens.view dbgLevel+ if lvl > DebugNone then do+ let subjIsConst = isConstant subj+ -- In debug mode we always report missing evaluation rules for the+ -- primitive evaluator+ traceIf (lvl > DebugNone && subjIsConst)+ ("Irreducible constant as case subject: " ++ showPpr subj +++ "\nCan be reduced to: " ++ showPpr subj1) ret+ else+ ret+++ -- The subject is a variable+ (Var v, [], _) | isNum0 (varType v) ->+ -- If we know that the type of the subject is zero-bits wide and+ -- one of the Clash number types. Then the only valid alternative is+ -- the one that can match on the literal "0", so try 'caseCon' with+ -- that.+ caseCon ctx (Case (Literal (IntegerLiteral 0)) ty alts)+ where+ isNum0 (tyView -> TyConApp (nameOcc -> tcNm) [arg])+ | tcNm `elem`+ ["Clash.Sized.Internal.BitVector.BitVector"+ ,"Clash.Sized.Internal.Unsigned.Unsigned"+ ,"Clash.Sized.Internal.Signed.Signed"+ ]+ = isLitX 0 arg+ | tcNm ==+ "Clash.Sized.Internal.Index.Index"+ = isLitX 1 arg+ isNum0 (coreView1 tcm -> Just t) = isNum0 t+ isNum0 _ = False++ isLitX n (LitTy (NumTy m)) = n == m+ isLitX n (coreView1 tcm -> Just t) = isLitX n t+ isLitX _ _ = False++ -- Otherwise check whether the entire case-expression has a single+ -- alternative, and pick that one.+ _ -> caseOneAlt e++caseCon _ e = return e+{-# SCC caseCon #-}++{- [Note: Name re-creation]+The names of heap bound variables are safely generate with mkUniqSystemId in Clash.Core.Evaluator.newLetBinding.+But only their uniqs end up in the heap, not the complete names.+So we use mkUnsafeSystemName to recreate the same Name.+-}++matchLiteralContructor+ :: Term+ -> Literal+ -> [(Pat,Term)]+ -> NormalizeSession Term+matchLiteralContructor c (IntegerLiteral l) alts = go (reverse alts)+ where+ go [(DefaultPat,e)] = changed e+ go ((DataPat dc [] xs,e):alts')+ | dcTag dc == 1+ , l >= ((-2)^(63::Int)) && l < 2^(63::Int)+ = let fvs = Lens.foldMapOf freeLocalIds unitVarSet e+ (binds,_) = List.partition ((`elemVarSet` fvs) . fst)+ $ zip xs [Literal (IntLiteral l)]+ e' = case binds of+ [] -> e+ _ -> Letrec binds e+ in changed e'+ | dcTag dc == 2+ , l >= 2^(63::Int)+ = let !(Jp# !(BN# ba)) = l+ ba' = BA.ByteArray ba+ bv = PV.Vector 0 (BA.sizeofByteArray ba') ba'+ fvs = Lens.foldMapOf freeLocalIds unitVarSet e+ (binds,_) = List.partition ((`elemVarSet` fvs) . fst)+ $ zip xs [Literal (ByteArrayLiteral bv)]+ e' = case binds of+ [] -> e+ _ -> Letrec binds e+ in changed e'+ | dcTag dc == 3+ , l < ((-2)^(63::Int))+ = let !(Jn# !(BN# ba)) = l+ ba' = BA.ByteArray ba+ bv = PV.Vector 0 (BA.sizeofByteArray ba') ba'+ fvs = Lens.foldMapOf freeLocalIds unitVarSet e+ (binds,_) = List.partition ((`elemVarSet` fvs) . fst)+ $ zip xs [Literal (ByteArrayLiteral bv)]+ e' = case binds of+ [] -> e+ _ -> Letrec binds e+ in changed e'+ | otherwise+ = go alts'+ go ((LitPat l', e):alts')+ | IntegerLiteral l == l'+ = changed e+ | otherwise+ = go alts'+ go _ = error $ $(curLoc) ++ "Report as bug: caseCon error: " ++ showPpr c++matchLiteralContructor c (NaturalLiteral l) alts = go (reverse alts)+ where+ go [(DefaultPat,e)] = changed e+ go ((DataPat dc [] xs,e):alts')+ | dcTag dc == 1+ , l >= 0 && l < 2^(64::Int)+ = let fvs = Lens.foldMapOf freeLocalIds unitVarSet e+ (binds,_) = List.partition ((`elemVarSet` fvs) . fst)+ $ zip xs [Literal (WordLiteral l)]+ e' = case binds of+ [] -> e+ _ -> Letrec binds e+ in changed e'+ | dcTag dc == 2+ , l >= 2^(64::Int)+ = let !(Jp# !(BN# ba)) = l+ ba' = BA.ByteArray ba+ bv = PV.Vector 0 (BA.sizeofByteArray ba') ba'+ fvs = Lens.foldMapOf freeLocalIds unitVarSet e+ (binds,_) = List.partition ((`elemVarSet` fvs) . fst)+ $ zip xs [Literal (ByteArrayLiteral bv)]+ e' = case binds of+ [] -> e+ _ -> Letrec binds e+ in changed e'+ | otherwise+ = go alts'+ go ((LitPat l', e):alts')+ | NaturalLiteral l == l'+ = changed e+ | otherwise+ = go alts'+ go _ = error $ $(curLoc) ++ "Report as bug: caseCon error: " ++ showPpr c++matchLiteralContructor _ _ ((DefaultPat,e):_) = changed e+matchLiteralContructor c _ _ =+ error $ $(curLoc) ++ "Report as bug: caseCon error: " ++ showPpr c+{-# SCC matchLiteralContructor #-}++caseOneAlt :: Term -> RewriteMonad extra Term+caseOneAlt e@(Case _ _ [(pat,altE)]) = case pat of+ DefaultPat -> changed altE+ LitPat _ -> changed altE+ DataPat _ tvs xs+ | (coerce tvs ++ coerce xs) `localVarsDoNotOccurIn` altE+ -> changed altE+ | otherwise+ -> return e++caseOneAlt (Case _ _ alts@((_,alt):_:_))+ | all ((== alt) . snd) (tail alts)+ = changed alt++caseOneAlt e = return e+{-# SCC caseOneAlt #-}++-- | Bring an application of a DataCon or Primitive in ANF, when the argument is+-- is considered non-representable+nonRepANF :: HasCallStack => NormRewrite+nonRepANF ctx@(TransformContext is0 _) e@(App appConPrim arg)+ | (conPrim, _) <- collectArgs e+ , isCon conPrim || isPrim conPrim+ = do+ untranslatable <- isUntranslatable False arg+ case (untranslatable,stripTicks arg) of+ (True,Letrec binds body) ->+ -- This is a situation similar to Note [CaseLet deshadow]+ let (binds1,body1) = deshadowLetExpr is0 binds body+ in changed (Letrec binds1 (App appConPrim body1))+ (True,Case {}) -> specializeNorm ctx e+ (True,Lam {}) -> specializeNorm ctx e+ (True,TyLam {}) -> specializeNorm ctx e+ _ -> return e++nonRepANF _ e = return e+{-# SCC nonRepANF #-}++-- | Ensure that top-level lambda's eventually bind a let-expression of which+-- the body is a variable-reference.+topLet :: HasCallStack => NormRewrite+topLet (TransformContext is0 ctx) e+ | all (\c -> isLambdaBodyCtx c || isTickCtx c) ctx && not (isLet e) && not (isTick e)+ = do+ untranslatable <- isUntranslatable False e+ if untranslatable+ then return e+ else do tcm <- Lens.view tcCache+ argId <- mkTmBinderFor is0 tcm (mkUnsafeSystemName "result" 0) e+ changed (Letrec [(argId, e)] (Var argId))+ where+ isTick Tick{} = True+ isTick _ = False++topLet (TransformContext is0 ctx) e@(Letrec binds body)+ | all (\c -> isLambdaBodyCtx c || isTickCtx c) ctx+ = do+ let localVar = isLocalVar body+ untranslatable <- isUntranslatable False body+ if localVar || untranslatable+ then return e+ else do+ tcm <- Lens.view tcCache+ let is2 = extendInScopeSetList is0 (map fst binds)+ argId <- mkTmBinderFor is2 tcm (mkUnsafeSystemName "result" 0) body+ changed (Letrec (binds ++ [(argId,body)]) (Var argId))++topLet _ e = return e+{-# SCC topLet #-}++-- Misc rewrites++-- | Remove unused let-bindings+deadCode :: HasCallStack => NormRewrite+deadCode _ e@(Letrec binds body) = do+ let bodyFVs = Lens.foldMapOf freeLocalIds unitVarSet body+ used = List.foldl' collectUsed emptyVarEnv (eltsVarSet bodyFVs)+ case eltsVarEnv used of+ [] -> changed body+ qqL | neLength qqL binds+ -> changed (Letrec qqL body)+ | otherwise+ -> return e+ where+ bindsEnv = mkVarEnv (map (\(x,e0) -> (x,(x,e0))) binds)++ collectUsed env v =+ if v `elemVarEnv` env then+ env+ else+ case lookupVarEnv v bindsEnv of+ Just (x,e0) ->+ let eFVs = Lens.foldMapOf freeLocalIds unitVarSet e0+ in List.foldl' collectUsed+ (extendVarEnv x (x,e0) env)+ (eltsVarSet eFVs)+ Nothing -> env++deadCode _ e = return e+{-# SCC deadCode #-}++removeUnusedExpr :: HasCallStack => NormRewrite+removeUnusedExpr _ e@(collectArgsTicks -> (p@(Prim pInfo),args,ticks)) = do+ bbM <- HashMap.lookup (primName pInfo) <$> Lens.use (extra.primitives)+ let+ usedArgs0 =+ case Monad.join (extractPrim <$> bbM) of+ Just (BlackBoxHaskell{usedArguments}) ->+ case usedArguments of+ UsedArguments used -> Just used+ IgnoredArguments ignored -> Just ([0..length args - 1] \\ ignored)+ Just (BlackBox pNm _ _ _ _ _ _ _ _ inc r ri templ) -> Just $+ if | isFromInt pNm -> [0,1,2]+ | primName pInfo `elem` [ "Clash.Annotations.BitRepresentation.Deriving.dontApplyInHDL"+ , "Clash.Sized.Vector.splitAt"+ ] -> [0,1]+ | otherwise -> concat [ maybe [] getUsedArguments r+ , maybe [] getUsedArguments ri+ , getUsedArguments templ+ , concatMap (getUsedArguments . snd) inc ]+ _ ->+ Nothing++ case usedArgs0 of+ Nothing ->+ return e+ Just usedArgs1 -> do+ tcm <- Lens.view tcCache+ (args1, Monoid.getAny -> hasChanged) <- listen (go tcm 0 usedArgs1 args)+ if hasChanged then+ return (mkApps (mkTicks p ticks) args1)+ else+ return e++ where+ arity = length . Either.rights . fst $ splitFunForallTy (primType pInfo)++ go _ _ _ [] = return []+ go tcm !n used (Right ty:args') = do+ args'' <- go tcm n used args'+ return (Right ty : args'')+ go tcm !n used (Left tm : args') = do+ args'' <- go tcm (n+1) used args'+ case tm of+ TyApp (Prim p0) _+ | primName p0 == "Clash.Transformations.removedArg"+ -> return (Left tm : args'')+ _ -> do+ let ty = termType tcm tm+ p' = removedTm ty+ if n < arity && n `notElem` used+ then changed (Left p' : args'')+ else return (Left tm : args'')++removeUnusedExpr _ e@(Case _ _ [(DataPat _ [] xs,altExpr)]) =+ if xs `localIdsDoNotOccurIn` altExpr+ then changed altExpr+ else return e++-- Replace any expression that creates a Vector of size 0 within the application+-- of the Cons constructor, by the Nil constructor.+removeUnusedExpr _ e@(collectArgsTicks -> (Data dc, [_,Right aTy,Right nTy,_,Left a,Left nil],ticks))+ | nameOcc (dcName dc) == "Clash.Sized.Vector.Cons"+ = do+ tcm <- Lens.view tcCache+ case runExcept (tyNatSize tcm nTy) of+ Right 0+ | (con, _) <- collectArgs nil+ , not (isCon con)+ -> let eTy = termType tcm e+ (TyConApp vecTcNm _) = tyView eTy+ (Just vecTc) = lookupUniqMap vecTcNm tcm+ [nilCon,consCon] = tyConDataCons vecTc+ v = mkTicks (mkVec nilCon consCon aTy 1 [a]) ticks+ in changed v+ _ -> return e++removeUnusedExpr _ e = return e+{-# SCC removeUnusedExpr #-}++-- | Inline let-bindings when the RHS is either a local variable reference or+-- is constant (except clock or reset generators)+bindConstantVar :: HasCallStack => NormRewrite+bindConstantVar = inlineBinders test+ where+ test _ (i,stripTicks -> e) = case isLocalVar e of+ -- Don't inline `let x = x in x`, it throws us in an infinite loop+ True -> return (i `localIdDoesNotOccurIn` e)+ _ -> isWorkFreeIsh e >>= \case+ True -> Lens.use (extra.inlineConstantLimit) >>= \case+ 0 -> return True+ n -> return (termSize e <= n)+ _ -> return False+{-# SCC bindConstantVar #-}++-- | Push a cast over a case into it's alternatives.+caseCast :: HasCallStack => NormRewrite+caseCast _ (Cast (stripTicks -> Case subj ty alts) ty1 ty2) = do+ let alts' = map (\(p,e) -> (p, Cast e ty1 ty2)) alts+ changed (Case subj ty alts')+caseCast _ e = return e+{-# SCC caseCast #-}+++-- | Push a cast over a Letrec into it's body+letCast :: HasCallStack => NormRewrite+letCast _ (Cast (stripTicks -> Letrec binds body) ty1 ty2) =+ changed $ Letrec binds (Cast body ty1 ty2)+letCast _ e = return e+{-# SCC letCast #-}+++-- | Push cast over an argument to a function into that function+--+-- This is done by specializing on the casted argument.+-- Example:+-- @+-- y = f (cast a)+-- where f x = g x+-- @+-- transforms to:+-- @+-- y = f' a+-- where f' x' = (\x -> g x) (cast x')+-- @+--+-- The reason d'etre for this transformation is that we hope to end up with+-- and expression where two casts are "back-to-back" after which we can+-- eliminate them in 'eliminateCastCast'.+argCastSpec :: HasCallStack => NormRewrite+argCastSpec ctx e@(App _ (stripTicks -> Cast e' _ _)) =+ if isWorkFree e' then+ go+ else+ warn go+ where+ go = specializeNorm ctx e+ warn = trace (unwords+ [ "WARNING:", $(curLoc), "specializing a function on a non work-free"+ , "cast. Generated HDL implementation might contain duplicate work."+ , "Please report this as a bug.", "\n\nExpression where this occured:"+ , "\n\n" ++ showPpr e+ ])+argCastSpec _ e = return e+{-# SCC argCastSpec #-}++-- | Only inline casts that just contain a 'Var', because these are guaranteed work-free.+-- These are the result of the 'splitCastWork' transformation.+inlineCast :: HasCallStack => NormRewrite+inlineCast = inlineBinders test+ where+ test _ (_, (Cast (stripTicks -> Var {}) _ _)) = return True+ test _ _ = return False+{-# SCC inlineCast #-}++-- | Eliminate two back to back casts where the type going in and coming out are the same+--+-- @+-- (cast :: b -> a) $ (cast :: a -> b) x ==> x+-- @+eliminateCastCast :: HasCallStack => NormRewrite+eliminateCastCast _ c@(Cast (stripTicks -> Cast e tyA tyB) tyB' tyC) = do+ tcm <- Lens.view tcCache+ let ntyA = normalizeType tcm tyA+ ntyB = normalizeType tcm tyB+ ntyB' = normalizeType tcm tyB'+ ntyC = normalizeType tcm tyC+ if ntyB == ntyB' && ntyA == ntyC then changed e+ else throwError+ where throwError = do+ (nm,sp) <- Lens.use curFun+ throw (ClashException sp ($(curLoc) ++ showPpr nm+ ++ ": Found 2 nested casts whose types don't line up:\n"+ ++ showPpr c)+ Nothing)++eliminateCastCast _ e = return e+{-# SCC eliminateCastCast #-}++-- | Make a cast work-free by splitting the work of to a separate binding+--+-- @+-- let x = cast (f a b)+-- ==>+-- let x = cast x'+-- x' = f a b+-- @+splitCastWork :: HasCallStack => NormRewrite+splitCastWork ctx@(TransformContext is0 _) unchanged@(Letrec vs e') = do+ (vss', Monoid.getAny -> hasChanged) <- listen (mapM (splitCastLetBinding is0) vs)+ let vs' = concat vss'+ if hasChanged then changed (Letrec vs' e')+ else return unchanged+ where+ splitCastLetBinding+ :: InScopeSet+ -> LetBinding+ -> RewriteMonad extra [LetBinding]+ splitCastLetBinding isN x@(nm, e) = case stripTicks e of+ Cast (Var {}) _ _ -> return [x] -- already work-free+ Cast (Cast {}) _ _ -> return [x] -- casts will be eliminated+ Cast e0 ty1 ty2 -> do+ tcm <- Lens.view tcCache+ nm' <- mkTmBinderFor isN tcm (mkDerivedName ctx (nameOcc $ varName nm)) e0+ changed [(nm',e0)+ ,(nm, Cast (Var nm') ty1 ty2)+ ]+ _ -> return [x]++splitCastWork _ e = return e+{-# SCC splitCastWork #-}+++-- | Inline work-free functions, i.e. fully applied functions that evaluate to+-- a constant+inlineWorkFree :: HasCallStack => NormRewrite+inlineWorkFree _ e@(collectArgsTicks -> (Var f,args@(_:_),ticks))+ = do+ tcm <- Lens.view tcCache+ let eTy = termType tcm e+ argsHaveWork <- or <$> mapM (either expressionHasWork+ (const (pure False)))+ args+ untranslatable <- isUntranslatableType True eTy+ let isSignal = isSignalType tcm eTy+ let lv = isLocalId f+ if untranslatable || isSignal || argsHaveWork || lv+ then return e+ else do+ bndrs <- Lens.use bindings+ case lookupVarEnv f bndrs of+ -- Don't inline recursive expressions+ Just b -> do+ isRecBndr <- isRecursiveBndr f+ if isRecBndr+ then return e+ else do+ let tm = mkTicks (bindingTerm b) (mkInlineTick f : ticks)+ changed $ mkApps tm args++ _ -> return e+ where+ -- an expression is has work when it contains free local variables,+ -- or has a Signal type, i.e. it does not evaluate to a work-free+ -- constant.+ expressionHasWork e' = do+ let fvIds = Lens.toListOf freeLocalIds e'+ tcm <- Lens.view tcCache+ let e'Ty = termType tcm e'+ isSignal = isSignalType tcm e'Ty+ return (not (null fvIds) || isSignal)++inlineWorkFree _ e@(Var f) = do+ tcm <- Lens.view tcCache+ let fTy = varType f+ closed = not (isPolyFunCoreTy tcm fTy)+ isSignal = isSignalType tcm fTy+ untranslatable <- isUntranslatableType True fTy+ topEnts <- Lens.view topEntities+ let gv = isGlobalId f+ if closed && f `notElemVarSet` topEnts && not untranslatable && not isSignal && gv+ then do+ bndrs <- Lens.use bindings+ case lookupVarEnv f bndrs of+ -- Don't inline recursive expressions+ Just top -> do+ isRecBndr <- isRecursiveBndr f+ if isRecBndr+ then return e+ else do+ let topB = bindingTerm top+ sizeLimit <- Lens.use (extra.inlineWFCacheLimit)+ -- caching only worth it from a certain size onwards, otherwise+ -- the caching mechanism itself brings more of an overhead.+ if termSize topB < sizeLimit then+ changed topB+ else do+ b <- normalizeTopLvlBndr False f top+ changed (bindingTerm b)+ _ -> return e+ else return e++inlineWorkFree _ e = return e+{-# SCC inlineWorkFree #-}++-- | Inline small functions+inlineSmall :: HasCallStack => NormRewrite+inlineSmall _ e@(collectArgsTicks -> (Var f,args,ticks)) = do+ untranslatable <- isUntranslatable True e+ topEnts <- Lens.view topEntities+ let lv = isLocalId f+ if untranslatable || f `elemVarSet` topEnts || lv+ then return e+ else do+ bndrs <- Lens.use bindings+ sizeLimit <- Lens.use (extra.inlineFunctionLimit)+ case lookupVarEnv f bndrs of+ -- Don't inline recursive expressions+ Just b -> do+ isRecBndr <- isRecursiveBndr f+ if not isRecBndr && bindingSpec b /= NoInline && termSize (bindingTerm b) < sizeLimit+ then do+ let tm = mkTicks (bindingTerm b) (mkInlineTick f : ticks)+ changed $ mkApps tm args+ else return e++ _ -> return e++inlineSmall _ e = return e+{-# SCC inlineSmall #-}++-- | Specialise functions on arguments which are constant, except when they+-- are clock, reset generators.+constantSpec :: HasCallStack => NormRewrite+constantSpec ctx@(TransformContext is0 tfCtx) e@(App e1 e2)+ | (Var {}, args) <- collectArgs e1+ , (_, []) <- Either.partitionEithers args+ , null $ Lens.toListOf termFreeTyVars e2+ = do specInfo<- constantSpecInfo ctx e2+ if csrFoundConstant specInfo then+ let newBindings = csrNewBindings specInfo in+ if null newBindings then+ -- Whole of e2 is constant+ specializeNorm ctx (App e1 e2)+ else do+ -- Parts of e2 are constant+ let is1 = extendInScopeSetList is0 (fst <$> csrNewBindings specInfo)+ Letrec newBindings+ <$> specializeNorm+ (TransformContext is1 tfCtx)+ (App e1 (csrNewTerm specInfo))++ else+ -- e2 has no constant parts+ return e+constantSpec _ e = return e+{-# SCC constantSpec #-}+++-- Experimental++-- | Propagate arguments of application inwards; except for 'Lam' where the+-- argument becomes let-bound. 'appPropFast' tries to propagate as many arguments+-- as possible, down as many levels as possible; and should be called in a+-- top-down traversal.+--+-- The idea is that this reduces the number of traversals, which hopefully leads+-- to shorter compile times.+--+-- Note [AppProp no shadowing]+--+-- Case 1.+--+-- Imagine:+--+-- @+-- (case x of+-- D a b -> h a) (f x y)+-- @+--+-- rewriting this to:+--+-- @+-- let b = f x y+-- in case x of+-- D a b -> h a b+-- @+--+-- is very bad because 'b' in 'h a b' is now bound by the pattern instead of the+-- newly introduced let-binding+--+-- instead me must deshadow w.r.t. the new variable and rewrite to:+--+-- @+-- let b = f x y+-- in case x of+-- D a b1 -> h a b+-- @+--+-- Case 2.+--+-- Imagine+--+-- @+-- (\x -> e) u+-- @+--+-- where @u@ has a free variable named @x@, rewriting this to:+--+-- @+-- let x = u+-- in e+-- @+--+-- would be very bad, because the let-binding suddenly captures the free+-- variable in @u@. To prevent this from happening we over-approximate and check+-- whether @x@ is in the current InScopeSet, and deshadow if that's the case,+-- i.e. we then rewrite to:+--+-- let x1 = u+-- in e [x:=x1]+--+-- Case 3.+--+-- The same for:+--+-- @+-- (let x = w in e) u+-- @+--+-- where @u@ again has a free variable @x@, rewriting this to:+--+-- @+-- let x = w in (e u)+-- @+--+-- would be bad because the let-binding now captures the free variable in @u@.+--+-- To prevent this from happening, we unconditionally deshadow the function part+-- of the application w.r.t. the free variables in the argument part of the+-- application. It is okay to over-approximate in this case and deshadow w.r.t+-- the current InScopeSet.+appPropFast :: HasCallStack => NormRewrite+appPropFast ctx@(TransformContext is _) = \case+ e@App {}+ | let (fun,args,ticks) = collectArgsTicks e+ -> go is (deShadowTerm is fun) args ticks+ e@TyApp {}+ | let (fun,args,ticks) = collectArgsTicks e+ -> go is (deShadowTerm is fun) args ticks+ e -> return e+ where+ go :: InScopeSet -> Term -> [Either Term Type] -> [TickInfo]+ -> NormalizeSession Term+ go is0 (collectArgsTicks -> (fun,args0@(_:_),ticks0)) args1 ticks1 =+ go is0 fun (args0 ++ args1) (ticks0 ++ ticks1)++ go is0 (Lam v e) (Left arg:args) ticks = do+ setChanged+ if isWorkFree arg || isVar arg+ then do+ let subst = extendIdSubst (mkSubst is0) v arg+ (`mkTicks` ticks) <$> go is0 (substTm "appPropFast.AppLam" subst e) args []+ else do+ let is1 = extendInScopeSet is0 v+ Letrec [(v, arg)] <$> go is1 (deShadowTerm is1 e) args ticks++ go is0 (Letrec vs e) args@(_:_) ticks = do+ setChanged+ let vbs = map fst vs+ is1 = extendInScopeSetList is0 vbs+ -- XXX: 'vs' should already be deshadowed w.r.t. 'is0'+ Letrec vs <$> go is1 e args ticks++ go is0 (TyLam tv e) (Right t:args) ticks = do+ setChanged+ let subst = extendTvSubst (mkSubst is0) tv t+ (`mkTicks` ticks) <$> go is0 (substTm "appPropFast.TyAppTyLam" subst e) args []++ go is0 (Case scrut ty0 alts) args0@(_:_) ticks = do+ setChanged+ let isA1 = unionInScope+ is0+ ((mkInScopeSet . mkVarSet . concatMap (patVars . fst)) alts)+ (ty1,vs,args1) <- goCaseArg isA1 ty0 [] args0+ case vs of+ [] -> (`mkTicks` ticks) . Case scrut ty1 <$> mapM (goAlt is0 args1) alts+ _ -> do+ let vbs = map fst vs+ is1 = extendInScopeSetList is0 vbs+ alts1 = map (deShadowAlt is1) alts+ Letrec vs . (`mkTicks` ticks) . Case scrut ty1 <$> mapM (goAlt is1 args1) alts1++ go is0 (Tick sp e) args ticks = do+ setChanged+ go is0 e args (sp:ticks)++ go _ fun args ticks = return (mkApps (mkTicks fun ticks) args)++ goAlt is0 args0 (p,e) = do+ let (tvs,ids) = patIds p+ is1 = extendInScopeSetList (extendInScopeSetList is0 tvs) ids+ (p,) <$> go is1 e args0 []++ goCaseArg isA ty0 ls0 (Right t:args0) = do+ tcm <- Lens.view tcCache+ let ty1 = piResultTy tcm ty0 t+ (ty2,ls1,args1) <- goCaseArg isA ty1 ls0 args0+ return (ty2,ls1,Right t:args1)++ goCaseArg isA0 ty0 ls0 (Left arg:args0) = do+ tcm <- Lens.view tcCache+ let argTy = termType tcm arg+ ty1 = applyFunTy tcm ty0 argTy+ case isWorkFree arg || isVar arg of+ True -> do+ (ty2,ls1,args1) <- goCaseArg isA0 ty1 ls0 args0+ return (ty2,ls1,Left arg:args1)+ False -> do+ boundArg <- mkTmBinderFor isA0 tcm (mkDerivedName ctx "app_arg") arg+ let isA1 = extendInScopeSet isA0 boundArg+ (ty2,ls1,args1) <- goCaseArg isA1 ty1 ls0 args0+ return (ty2,(boundArg,arg):ls1,Left (Var boundArg):args1)++ goCaseArg _ ty ls [] = return (ty,ls,[])+{-# SCC appPropFast #-}++-- | Flatten ridiculous case-statements generated by GHC+--+-- For case-statements in haskell of the form:+--+-- @+-- f :: Unsigned 4 -> Unsigned 4+-- f x = case x of+-- 0 -> 3+-- 1 -> 2+-- 2 -> 1+-- 3 -> 0+-- @+--+-- GHC generates Core that looks like:+--+-- @+-- f = \(x :: Unsigned 4) -> case x == fromInteger 3 of+-- False -> case x == fromInteger 2 of+-- False -> case x == fromInteger 1 of+-- False -> case x == fromInteger 0 of+-- False -> error "incomplete case"+-- True -> fromInteger 3+-- True -> fromInteger 2+-- True -> fromInteger 1+-- True -> fromInteger 0+-- @+--+-- Which would result in a priority decoder circuit where a normal decoder+-- circuit was desired.+--+-- This transformation transforms the above Core to the saner:+--+-- @+-- f = \(x :: Unsigned 4) -> case x of+-- _ -> error "incomplete case"+-- 0 -> fromInteger 3+-- 1 -> fromInteger 2+-- 2 -> fromInteger 1+-- 3 -> fromInteger 0+-- @+caseFlat :: HasCallStack => NormRewrite+caseFlat _ e@(Case (collectEqArgs -> Just (scrut',_)) ty _)+ = do+ case collectFlat scrut' e of+ Just alts' -> changed (Case scrut' ty (last alts' : init alts'))+ Nothing -> return e++caseFlat _ e = return e+{-# SCC caseFlat #-}++collectFlat :: Term -> Term -> Maybe [(Pat,Term)]+collectFlat scrut (Case (collectEqArgs -> Just (scrut', val)) _ty [lAlt,rAlt])+ | scrut' == scrut+ = case collectArgs val of+ (Prim p,args') | isFromInt (primName p) ->+ go (last args')+ (Data dc,args') | nameOcc (dcName dc) == "GHC.Types.I#" ->+ go (last args')+ _ -> Nothing+ where+ go (Left (Literal i)) = case (lAlt,rAlt) of+ ((pl,el),(pr,er))+ | isFalseDcPat pl || isTrueDcPat pr ->+ case collectFlat scrut el of+ Just alts' -> Just ((LitPat i, er) : alts')+ Nothing -> Just [(LitPat i, er)+ ,(DefaultPat, el)+ ]+ | otherwise ->+ case collectFlat scrut er of+ Just alts' -> Just ((LitPat i, el) : alts')+ Nothing -> Just [(LitPat i, el)+ ,(DefaultPat, er)+ ]+ go _ = Nothing++ isFalseDcPat (DataPat p _ _)+ = ((== "GHC.Types.False") . nameOcc . dcName) p+ isFalseDcPat _ = False++ isTrueDcPat (DataPat p _ _)+ = ((== "GHC.Types.True") . nameOcc . dcName) p+ isTrueDcPat _ = False++collectFlat _ _ = Nothing+{-# SCC collectFlat #-}++collectEqArgs :: Term -> Maybe (Term,Term)+collectEqArgs (collectArgsTicks -> (Prim p, args, ticks))+ | nm == "Clash.Sized.Internal.BitVector.eq#"+ = let [_,_,Left scrut,Left val] = args+ in Just (mkTicks scrut ticks,val)+ | nm == "Clash.Sized.Internal.Index.eq#" ||+ nm == "Clash.Sized.Internal.Signed.eq#" ||+ nm == "Clash.Sized.Internal.Unsigned.eq#"+ = let [_,Left scrut,Left val] = args+ in Just (mkTicks scrut ticks,val)+ | nm == "Clash.Transformations.eqInt"+ = let [Left scrut,Left val] = args+ in Just (mkTicks scrut ticks,val)+ where+ nm = primName p++collectEqArgs _ = Nothing++type NormRewriteW = Transform (StateT ([LetBinding],InScopeSet) (RewriteMonad NormalizeState))++-- | See Note [ANF InScopeSet]+tellBinders :: Monad m => [LetBinding] -> StateT ([LetBinding],InScopeSet) m ()+tellBinders bs = modify ((bs ++) *** (`extendInScopeSetList` (map fst bs)))++-- | See Note [ANF InScopeSet]; only extends the inscopeset+notifyBinders :: Monad m => [LetBinding] -> StateT ([LetBinding],InScopeSet) m ()+notifyBinders bs = modify (second (`extendInScopeSetList` (map fst bs)))++-- | Is the given type IO-like+isSimIOTy+ :: TyConMap+ -> Type+ -- ^ Type to check for IO-likeness+ -> Bool+isSimIOTy tcm ty = case tyView (coreView tcm ty) of+ TyConApp tcNm args+ | nameOcc tcNm == "Clash.Explicit.SimIO.SimIO"+ -> True+ | nameOcc tcNm == "GHC.Prim.(#,#)"+ , [_,_,st,_] <- args+ -> isStateTokenTy tcm st+ FunTy _ res -> isSimIOTy tcm res+ _ -> False++-- | Is the given type the state token+isStateTokenTy+ :: TyConMap+ -> Type+ -- ^ Type to check for state tokenness+ -> Bool+isStateTokenTy tcm ty = case tyView (coreView tcm ty) of+ TyConApp tcNm _ -> nameOcc tcNm == "GHC.Prim.State#"+ _ -> False++-- | Turn an expression into a modified ANF-form. As opposed to standard ANF,+-- constants do not become let-bound.+makeANF :: HasCallStack => NormRewrite+makeANF (TransformContext is0 ctx) (Lam bndr e) = do+ e' <- makeANF (TransformContext (extendInScopeSet is0 bndr)+ (LamBody bndr:ctx))+ e+ return (Lam bndr e')++makeANF _ e@(TyLam {}) = return e++makeANF ctx@(TransformContext is0 _) e0+ = do+ -- We need to freshen all binders in `e` because we're shuffling them around+ -- into a single let-binder, because even when binders don't shadow, they+ -- don't have to be unique within an expression. And so lifting them all+ -- to a single let-binder will cause issues when they're not unique.+ --+ -- We cannot make freshening part of collectANF, because when we generate+ -- new binders, we need to make sure those names do not conflict with _any_+ -- of the existing binders in the expression.+ --+ -- See also Note [ANF InScopeSet]+ let (is2,e1) = freshenTm is0 e0+ ((e2,(bndrs,_)),Monoid.getAny -> hasChanged) <-+ listen (runStateT (bottomupR collectANF ctx e1) ([],is2))+ case bndrs of+ [] -> if hasChanged then return e2 else return e0+ _ -> do+ let (e3,ticks) = collectTicks e2+ (srcTicks,nmTicks) = partitionTicks ticks+ -- Ensure that `AppendName` ticks still scope over the entire expression+ changed (mkTicks (Letrec bndrs (mkTicks e3 srcTicks)) nmTicks)+{-# SCC makeANF #-}++-- | Note [ANF InScopeSet]+--+-- The InScopeSet contains:+--+-- 1. All the free variables of the expression we are traversing+--+-- 2. All the bound variables of the expression we are traversing+--+-- 3. The newly created let-bindings as we recurse back up the traversal+--+-- All of these are needed to created let-bindings that+--+-- * Do not shadow+-- * Are not shadowed+-- * Nor conflict with each other (i.e. have the same unique)+--+-- Initially we start with the local InScopeSet and add the global variables:+--+-- @+-- is1 <- unionInScope is0 <$> Lens.use globalInScope+-- @+--+-- Which will gives us the (superset of) free variables of the expression. Then+-- we call 'freshenTm'+--+-- @+-- let (is2,e1) = freshenTm is1 e0+-- @+--+-- Which extends the InScopeSet with all the bound variables in 'e1', the+-- version of 'e0' where all binders are unique (not just deshadowed).+--+-- So we start out with an InScopeSet that satisfies points 1 and 2, now every+-- time we create a new binder we must add it to the InScopeSet to satisfy+-- point 3.+--+-- Note [ANF no let-bind]+--+-- | Do not let-bind:+--+-- 1. Arguments with an untranslatable type: untranslatable expressions+-- should be propagated down as far as possible+--+-- 2. Local variables or constants: they don't add any work, so no reason+-- to let-bind to enable sharing+--+-- 3. IO actions, the translation of IO actions to sequential HDL constructs+-- depends on IO actions to be propagated down as far as possible.+collectANF :: HasCallStack => NormRewriteW+collectANF ctx e@(App appf arg)+ | (conVarPrim, _) <- collectArgs e+ , isCon conVarPrim || isPrim conVarPrim || isVar conVarPrim+ = do+ untranslatable <- lift (isUntranslatable False arg)+ let localVar = isLocalVar arg+ constantNoCR <- lift (isConstantNotClockReset arg)+ -- See Note [ANF no let-bind]+ case (untranslatable,localVar || constantNoCR, isSimBind conVarPrim,arg) of+ (False,False,False,_) -> do+ tcm <- Lens.view tcCache+ -- See Note [ANF InScopeSet]+ is1 <- Lens.use _2+ argId <- lift (mkTmBinderFor is1 tcm (mkDerivedName ctx "app_arg") arg)+ -- See Note [ANF InScopeSet]+ tellBinders [(argId,arg)]+ return (App appf (Var argId))+ (True,False,_,Letrec binds body) -> do+ tellBinders binds+ return (App appf body)+ _ -> return e+ where+ isSimBind (Prim p) = primName p == "Clash.Explicit.SimIO.bindSimIO#"+ isSimBind _ = False++collectANF _ (Letrec binds body) = do+ tcm <- Lens.view tcCache+ let isSimIO = isSimIOTy tcm (termType tcm body)+ untranslatable <- lift (isUntranslatable False body)+ let localVar = isLocalVar body+ -- See Note [ANF no let-bind]+ if localVar || untranslatable || isSimIO+ then do+ tellBinders binds+ return body+ else do+ -- See Note [ANF InScopeSet]+ is1 <- Lens.use _2+ argId <- lift (mkTmBinderFor is1 tcm (mkUnsafeSystemName "result" 0) body)+ -- See Note [ANF InScopeSet]+ tellBinders [(argId,body)]+ tellBinders binds+ return (Var argId)++-- TODO: The code below special-cases ANF for the ':-' constructor for the+-- 'Signal' type. The 'Signal' type is essentially treated as a "transparent"+-- type by the Clash compiler, so observing its constructor leads to all kinds+-- of problems. In this case that "Clash.Rewrite.Util.mkSelectorCase" will+-- try to project the LHS and RHS of the ':-' constructor, however,+-- 'mkSelectorCase' uses 'coreView1' to find the "real" data-constructor.+-- 'coreView1' however looks through the 'Signal' type, and hence 'mkSelector'+-- finds the data constructors for the element type of Signal. This resulted in+-- error #24 (https://github.com/christiaanb/clash2/issues/24), where we+-- try to get the first field out of the 'Vec's 'Nil' constructor.+--+-- Ultimately we should stop treating Signal as a "transparent" type and deal+-- handling of the Signal type, and the involved co-recursive functions,+-- properly. At the moment, Clash cannot deal with this recursive type and the+-- recursive functions involved, hence the need for special-casing code. After+-- everything is done properly, we should remove the two lines below.+collectANF _ e@(Case _ _ [(DataPat dc _ _,_)])+ | nameOcc (dcName dc) == "Clash.Signal.Internal.:-" = return e++collectANF ctx (Case subj ty alts) = do+ let localVar = isLocalVar subj+ let isConstantSubj = isConstant subj++ (subj',subjBinders) <- if localVar || isConstantSubj+ then return (subj,[])+ else do+ tcm <- Lens.view tcCache+ -- See Note [ANF InScopeSet]+ is1 <- Lens.use _2+ argId <- lift (mkTmBinderFor is1 tcm (mkDerivedName ctx "case_scrut") subj)+ -- See Note [ANF InScopeSet]+ notifyBinders [(argId,subj)]+ return (Var argId,[(argId,subj)])++ tcm <- Lens.view tcCache+ let isSimIOAlt = isSimIOTy tcm ty++ alts' <- mapM (doAlt isSimIOAlt subj') alts+ tellBinders subjBinders++ case alts' of+ [(DataPat _ [] xs,altExpr)]+ | xs `localIdsDoNotOccurIn` altExpr || isSimIOAlt+ -> return altExpr+ _ -> return (Case subj' ty alts')+ where+ doAlt+ :: Bool -> Term -> (Pat,Term)+ -> StateT ([LetBinding],InScopeSet) (RewriteMonad NormalizeState)+ (Pat,Term)+ doAlt isSimIOAlt subj' alt@(DataPat dc exts xs,altExpr) | not (bindsExistentials exts xs) = do+ let lv = isLocalVar altExpr+ patSels <- Monad.zipWithM (doPatBndr subj' dc) xs [0..]+ let altExprIsConstant = isConstant altExpr+ let usesXs (Var n) = any (== n) xs+ usesXs _ = False+ -- See [ANF no let-bind]+ if or [isSimIOAlt, lv && (not (usesXs altExpr) || length alts == 1), altExprIsConstant]+ then do+ -- See Note [ANF InScopeSet]+ tellBinders patSels+ return alt+ else do+ tcm <- Lens.view tcCache+ -- See Note [ANF InScopeSet]+ is1 <- Lens.use _2+ altId <- lift (mkTmBinderFor is1 tcm (mkDerivedName ctx "case_alt") altExpr)+ -- See Note [ANF InScopeSet]+ tellBinders (patSels ++ [(altId,altExpr)])+ return (DataPat dc exts xs,Var altId)+ doAlt _ _ alt@(DataPat {}, _) = return alt+ doAlt isSimIOAlt _ alt@(pat,altExpr) = do+ let lv = isLocalVar altExpr+ let altExprIsConstant = isConstant altExpr+ -- See [ANF no let-bind]+ if isSimIOAlt || lv || altExprIsConstant+ then return alt+ else do+ tcm <- Lens.view tcCache+ -- See Note [ANF InScopeSet]+ is1 <- Lens.use _2+ altId <- lift (mkTmBinderFor is1 tcm (mkDerivedName ctx "case_alt") altExpr)+ tellBinders [(altId,altExpr)]+ return (pat,Var altId)++ doPatBndr+ :: Term -> DataCon -> Id -> Int+ -> StateT ([LetBinding],InScopeSet) (RewriteMonad NormalizeState)+ LetBinding+ doPatBndr subj' dc pId i+ = do+ tcm <- Lens.view tcCache+ -- See Note [ANF InScopeSet]+ is1 <- Lens.use _2+ patExpr <- lift (mkSelectorCase ($(curLoc) ++ "doPatBndr") is1 tcm subj' (dcTag dc) i)+ -- No need to 'tellBinders' here because 'pId' is already in the ANF+ -- InScopeSet.+ --+ -- See also Note [ANF InScopeSet]+ return (pId,patExpr)++collectANF _ e = return e+{-# SCC collectANF #-}++-- | Eta-expand top-level lambda's (DON'T use in a traversal!)+etaExpansionTL :: HasCallStack => NormRewrite+etaExpansionTL (TransformContext is0 ctx) (Lam bndr e) = do+ e' <- etaExpansionTL+ (TransformContext (extendInScopeSet is0 bndr) (LamBody bndr:ctx))+ e+ return $ Lam bndr e'++etaExpansionTL (TransformContext is0 ctx) (Letrec xes e) = do+ let bndrs = map fst xes+ e' <- etaExpansionTL+ (TransformContext (extendInScopeSetList is0 bndrs)+ (LetBody bndrs:ctx))+ e+ case stripLambda e' of+ (bs@(_:_),e2) -> do+ let e3 = Letrec xes e2+ changed (mkLams e3 bs)+ _ -> return (Letrec xes e')+ where+ stripLambda :: Term -> ([Id],Term)+ stripLambda (Lam bndr e0) =+ let (bndrs,e1) = stripLambda e0+ in (bndr:bndrs,e1)+ stripLambda e' = ([],e')++etaExpansionTL (TransformContext is0 ctx) e+ = do+ tcm <- Lens.view tcCache+ if isFun tcm e+ then do+ let argTy = ( fst+ . Maybe.fromMaybe (error $ $(curLoc) ++ "etaExpansion splitFunTy")+ . splitFunTy tcm+ . termType tcm+ ) e+ newId <- mkInternalVar is0 "arg" argTy+ e' <- etaExpansionTL (TransformContext (extendInScopeSet is0 newId)+ (LamBody newId:ctx))+ (App e (Var newId))+ changed (Lam newId e')+ else return e+{-# SCC etaExpansionTL #-}++-- | Eta-expand functions with a Synthesize annotation, needed to allow such+-- functions to appear as arguments to higher-order primitives.+etaExpandSyn :: HasCallStack => NormRewrite+etaExpandSyn (TransformContext is0 ctx) e@(collectArgs -> (Var f, _)) = do+ topEnts <- Lens.view topEntities+ tcm <- Lens.view tcCache+ let isTopEnt = f `elemVarSet` topEnts+ isAppFunCtx =+ \case+ AppFun:_ -> True+ TickC _:c -> isAppFunCtx c+ _ -> False+ argTyM = fmap fst (splitFunTy tcm (termType tcm e))+ case argTyM of+ Just argTy | isTopEnt && not (isAppFunCtx ctx) -> do+ newId <- mkInternalVar is0 "arg" argTy+ changed (Lam newId (App e (Var newId)))+ _ -> return e++etaExpandSyn _ e = return e+{-# SCC etaExpandSyn #-}++isClassConstraint :: Type -> Bool+isClassConstraint (tyView -> TyConApp nm0 _) =+ if -- Constraint tuple:+ | "GHC.Classes.(%" `Text.isInfixOf` nm1 -> True+ -- Constraint class:+ | "C:" `Text.isInfixOf` nm2 -> True+ | otherwise -> False+ where+ nm1 = nameOcc nm0+ nm2 = snd (Text.breakOnEnd "." nm1)++isClassConstraint _ = False+++-- | Turn a normalized recursive function, where the recursive calls only pass+-- along the unchanged original arguments, into let-recursive function. This+-- means that all recursive calls are replaced by the same variable reference as+-- found in the body of the top-level let-expression.+recToLetRec :: HasCallStack => NormRewrite+recToLetRec (TransformContext is0 []) e = do+ (fn,_) <- Lens.use curFun+ tcm <- Lens.view tcCache+ case splitNormalized tcm e of+ Right (args,bndrs,res) -> do+ let args' = map Var args+ (toInline,others) = List.partition (eqApp tcm fn args' . snd) bndrs+ resV = Var res+ case (toInline,others) of+ (_:_,_:_) -> do+ let is1 = extendInScopeSetList is0 (args ++ map fst bndrs)+ let substsInline = extendIdSubstList (mkSubst is1)+ $ map (second (const resV)) toInline+ others' = map (second (substTm "recToLetRec" substsInline))+ others+ changed $ mkLams (Letrec others' resV) args+ _ -> return e+ _ -> return e+ where+ -- This checks whether things are semantically equal. For example, say we+ -- have:+ --+ -- x :: (a, (b, c))+ --+ -- and+ --+ -- y :: (a, (b, c))+ --+ -- If we can determine that 'y' is constructed solely using the+ -- corresponding fields in 'x', then we can say they are semantically+ -- equal. The algorithm below keeps track of what (sub)field it is+ -- constructing, and checks if the field-expression projects the+ -- corresponding (sub)field from the target variable.+ --+ -- TODO: See [Note: Breaks on constants and predetermined equality]+ eqApp tcm v args (collectArgs . stripTicks -> (Var v',args'))+ | isGlobalId v'+ , v == v'+ , let args2 = Either.lefts args'+ , length args == length args2+ = and (zipWith (eqArg tcm) args args2)+ eqApp _ _ _ _ = False++ eqArg _ v1 v2@(stripTicks -> Var {})+ = v1 == v2+ eqArg tcm v1 v2@(collectArgs . stripTicks -> (Data _, args'))+ | let t1 = termType tcm v1+ , let t2 = termType tcm v2+ , t1 == t2+ = if isClassConstraint t1 then+ -- Class constraints are equal if their types are equal, so we can+ -- take a shortcut here.+ True+ else+ -- Check whether all arguments to the data constructor are projections+ --+ and (zipWith (eqDat v1) (map pure [0..]) (Either.lefts args'))+ eqArg _ _ _+ = False++ -- Recursively check whether a term /e/ is semantically equal to some variable /v/.+ -- Currently it can only assert equality when /e/ is syntactically equal+ -- to /v/, or is constructed out of projections of /v/, importantly:+ --+ -- [Note: Breaks on constants and predetermined equality]+ -- This function currently breaks if:+ --+ -- * One or more subfields are constants. Constants might have been+ -- inlined for the construction, instead of being a projection of the+ -- target variable.+ --+ -- * One or more subfields are determined to be equal and one is simply+ -- swapped / replaced by the other. For example, say we have+ -- `x :: (a, a)`. If GHC determines that both elements of the tuple will+ -- always be the same, it might replace the (semantically equal to 'x')+ -- construction of `y` with `(fst x, fst x)`.+ --+ eqDat :: Term -> [Int] -> Term -> Bool+ eqDat v fTrace (collectArgs . stripTicks -> (Data _, args)) =+ and (zipWith (eqDat v) (map (:fTrace) [0..]) (Either.lefts args))+ eqDat v1 fTrace v2 =+ case stripProjection (reverse fTrace) v1 v2 of+ Just [] -> True+ _ -> False++ stripProjection :: [Int] -> Term -> Term -> Maybe [Int]+ stripProjection fTrace0 vTarget0 (Case v _ [(DataPat _ _ xs, r)]) = do+ -- Get projection made in subject of case:+ fTrace1 <- stripProjection fTrace0 vTarget0 v++ -- Extract projection of this case statement. Subsequent calls to+ -- 'stripProjection' will check if new target is actually used.+ n <- headMaybe fTrace1+ vTarget1 <- indexMaybe xs n+ fTrace2 <- tailMaybe fTrace1++ stripProjection fTrace2 (Var vTarget1) r++ stripProjection fTrace (Var sTarget) (Var s) =+ if sTarget == s then Just fTrace else Nothing++ stripProjection _fTrace _vTarget _v =+ Nothing++recToLetRec _ e = return e+{-# SCC recToLetRec #-}++-- | Inline a function with functional arguments+inlineHO :: HasCallStack => NormRewrite+inlineHO _ e@(App _ _)+ | (Var f, args, ticks) <- collectArgsTicks e+ = do+ tcm <- Lens.view tcCache+ let hasPolyFunArgs = or (map (either (isPolyFun tcm) (const False)) args)+ if hasPolyFunArgs+ then do (cf,_) <- Lens.use curFun+ isInlined <- zoomExtra (alreadyInlined f cf)+ limit <- Lens.use (extra.inlineLimit)+ if (Maybe.fromMaybe 0 isInlined) > limit+ then do+ lvl <- Lens.view dbgLevel+ traceIf (lvl > DebugNone) ($(curLoc) ++ "InlineHO: " ++ show f ++ " already inlined " ++ show limit ++ " times in:" ++ show cf) (return e)+ else do+ bodyMaybe <- lookupVarEnv f <$> Lens.use bindings+ case bodyMaybe of+ Just b -> do+ zoomExtra (addNewInline f cf)+ changed (mkApps (mkTicks (bindingTerm b) ticks) args)+ _ -> return e+ else return e++inlineHO _ e = return e+{-# SCC inlineHO #-}++-- | Simplified CSE, only works on let-bindings, does an inverse topological+-- sort of the let-bindings and then works from top to bottom+--+-- XXX: Check whether inverse top-sort followed by single traversal removes as+-- many binders as the previous "apply-until-fixpoint" approach in the presence+-- of recursive groups in the let-bindings. If not but just for checking whether+-- changes to transformation affect the eventual size of the circuit, it would+-- be really helpful if we tracked circuit size in the regression/test suite.+-- On the two examples that were tested, Reducer and PipelinesViaFolds, this new+-- version of CSE removed the same amount of let-binders.+simpleCSE :: HasCallStack => NormRewrite+simpleCSE (TransformContext is0 _) (inverseTopSortLetBindings -> Letrec bndrs body) = do+ let is1 = extendInScopeSetList is0 (map fst bndrs)+ (subst,bndrs1) <- reduceBinders (mkSubst is1) [] bndrs+ -- TODO: check whether a substitution over the body is enough, the reason I'm+ -- doing a substitution over the the binders as well is that I don't know in+ -- what order a recursive group shows up in a inverse topological sort.+ -- Depending on the order and forgetting to apply the substitution over the+ -- let-bindings might lead to the introduction of free variables.+ --+ -- NB: don't apply the substitution to the entire let-expression, and that+ -- would rename the let-bindings because they've been added to the InScopeSet+ -- of the substitution.+ let bndrs2 = map (second (substTm "simpleCSE.bndrs" subst)) bndrs1+ body1 = substTm "simpleCSE.body" subst body+ return (Letrec bndrs2 body1)++simpleCSE _ e = return e+{-# SCC simpleCSE #-}++-- | XXX: is given inverse topologically sorted binders, but returns+-- topologically sorted binders+--+-- TODO: check further speed improvements:+--+-- 1. Store the processed binders in a `Map Expr LetBinding`:+-- * Trades O(1) `cons` and O(n)*aeqTerm `find` for:+-- * O(log n)*aeqTerm `insert` and O(log n)*aeqTerm `lookup`+-- 2. Store the processed binders in a `AEQTrie Expr LetBinding`+-- * Trades O(1) `cons` and O(n)*aeqTerm `find` for:+-- * O(e) `insert` and O(e) `lookup`+reduceBinders+ :: Subst+ -> [LetBinding]+ -> [LetBinding]+ -> RewriteMonad NormalizeState (Subst, [LetBinding])+reduceBinders !subst processed [] = return (subst,processed)+reduceBinders !subst processed ((i,substTm "reduceBinders" subst -> e):rest)+ | (_,_,ticks) <- collectArgsTicks e+ , NoDeDup `notElem` ticks+ , Just (i1,_) <- List.find ((== e) . snd) processed+ = do+ let subst1 = extendIdSubst subst i (Var i1)+ setChanged+ reduceBinders subst1 processed rest+ | otherwise+ = reduceBinders subst ((i,e):processed) rest+{-# SCC reduceBinders #-}++reduceConst :: HasCallStack => NormRewrite+reduceConst ctx e@(App _ _)+ | (Prim p0, _) <- collectArgs e+ = whnfRW False ctx e $ \_ctx1 e1 -> case e1 of+ (collectArgs -> (Prim p1, _)) | primName p0 == primName p1 -> return e+ _ -> changed e1++reduceConst _ e = return e+{-# SCC reduceConst #-}++-- | Replace primitives by their "definition" if they would lead to let-bindings+-- with a non-representable type when a function is in ANF. This happens for+-- example when Clash.Size.Vector.map consumes or produces a vector of+-- non-representable elements.+--+-- Basically what this transformation does is replace a primitive the completely+-- unrolled recursive definition that it represents. e.g.+--+-- > zipWith ($) (xs :: Vec 2 (Int -> Int)) (ys :: Vec 2 Int)+--+-- is replaced by:+--+-- > let (x0 :: (Int -> Int)) = case xs of (:>) _ x xr -> x+-- > (xr0 :: Vec 1 (Int -> Int)) = case xs of (:>) _ x xr -> xr+-- > (x1 :: (Int -> Int)( = case xr0 of (:>) _ x xr -> x+-- > (y0 :: Int) = case ys of (:>) _ y yr -> y+-- > (yr0 :: Vec 1 Int) = case ys of (:>) _ y yr -> xr+-- > (y1 :: Int = case yr0 of (:>) _ y yr -> y+-- > in (($) x0 y0 :> ($) x1 y1 :> Nil)+--+-- Currently, it only handles the following functions:+--+-- * Clash.Sized.Vector.zipWith+-- * Clash.Sized.Vector.map+-- * Clash.Sized.Vector.traverse#+-- * Clash.Sized.Vector.fold+-- * Clash.Sized.Vector.foldr+-- * Clash.Sized.Vector.dfold+-- * Clash.Sized.Vector.(++)+-- * Clash.Sized.Vector.head+-- * Clash.Sized.Vector.tail+-- * Clash.Sized.Vector.last+-- * Clash.Sized.Vector.init+-- * Clash.Sized.Vector.unconcat+-- * Clash.Sized.Vector.transpose+-- * Clash.Sized.Vector.replicate+-- * Clash.Sized.Vector.replace_int+-- * Clash.Sized.Vector.imap+-- * Clash.Sized.Vector.dtfold+-- * Clash.Sized.RTree.tdfold+-- * Clash.Sized.RTree.treplicate+-- * Clash.Sized.Internal.BitVector.split#+-- * Clash.Sized.Internal.BitVector.eq#+reduceNonRepPrim :: HasCallStack => NormRewrite+reduceNonRepPrim c@(TransformContext is0 ctx) e@(App _ _) | (Prim p, args, ticks) <- collectArgsTicks e = do+ tcm <- Lens.view tcCache+ ultra <- Lens.use (extra.normalizeUltra)+ let eTy = termType tcm e+ case tyView eTy of+ (TyConApp vecTcNm@(nameOcc -> "Clash.Sized.Vector.Vec")+ [runExcept . tyNatSize tcm -> Right 0, aTy]) -> do+ let (Just vecTc) = lookupUniqMap vecTcNm tcm+ [nilCon,consCon] = tyConDataCons vecTc+ nilE = mkVec nilCon consCon aTy 0 []+ changed (mkTicks nilE ticks)+ tv -> let argLen = length args in case primName p of+ "Clash.Sized.Vector.zipWith" | argLen == 7 -> do+ let [lhsElTy,rhsElty,resElTy,nTy] = Either.rights args+ case runExcept (tyNatSize tcm nTy) of+ Right n -> do+ shouldReduce1 <- orM [ pure (ultra || n < 2)+ , shouldReduce ctx+ , anyM isUntranslatableType_not_poly+ [lhsElTy,rhsElty,resElTy] ]+ if shouldReduce1+ then let [fun,lhsArg,rhsArg] = Either.lefts args+ in (`mkTicks` ticks) <$>+ reduceZipWith c n lhsElTy rhsElty resElTy fun lhsArg rhsArg+ else return e+ _ -> return e+ "Clash.Sized.Vector.map" | argLen == 5 -> do+ let [argElTy,resElTy,nTy] = Either.rights args+ case runExcept (tyNatSize tcm nTy) of+ Right n -> do+ shouldReduce1 <- orM [ pure (ultra || n < 2 )+ , shouldReduce ctx+ , anyM isUntranslatableType_not_poly+ [argElTy,resElTy] ]+ if shouldReduce1+ then let [fun,arg] = Either.lefts args+ in (`mkTicks` ticks) <$> reduceMap c n argElTy resElTy fun arg+ else return e+ _ -> return e+ "Clash.Sized.Vector.traverse#" | argLen == 7 ->+ let [aTy,fTy,bTy,nTy] = Either.rights args+ in case runExcept (tyNatSize tcm nTy) of+ Right n ->+ let [dict,fun,arg] = Either.lefts args+ in (`mkTicks` ticks) <$> reduceTraverse c n aTy fTy bTy dict fun arg+ _ -> return e+ "Clash.Sized.Vector.fold" | argLen == 4 -> do+ let [aTy,nTy] = Either.rights args+ case runExcept (tyNatSize tcm nTy) of+ Right n -> do+ shouldReduce1 <- orM [ pure (ultra || n == 0)+ , shouldReduce ctx+ , isUntranslatableType_not_poly aTy ]+ if shouldReduce1 then+ let [fun,arg] = Either.lefts args+ in (`mkTicks` ticks) <$> reduceFold c (n + 1) aTy fun arg+ else return e+ _ -> return e+ "Clash.Sized.Vector.foldr" | argLen == 6 ->+ let [aTy,bTy,nTy] = Either.rights args+ in case runExcept (tyNatSize tcm nTy) of+ Right n -> do+ shouldReduce1 <- orM [ pure ultra+ , shouldReduce ctx+ , anyM isUntranslatableType_not_poly [aTy,bTy] ]+ if shouldReduce1+ then let [fun,start,arg] = Either.lefts args+ in (`mkTicks` ticks) <$> reduceFoldr c n aTy fun start arg+ else return e+ _ -> return e+ "Clash.Sized.Vector.dfold" | argLen == 8 ->+ let ([_kn,_motive,fun,start,arg],[_mTy,nTy,aTy]) = Either.partitionEithers args+ in case runExcept (tyNatSize tcm nTy) of+ Right n -> (`mkTicks` ticks) <$> reduceDFold is0 n aTy fun start arg+ _ -> return e+ "Clash.Sized.Vector.++" | argLen == 5 ->+ let [nTy,aTy,mTy] = Either.rights args+ [lArg,rArg] = Either.lefts args+ in case (runExcept (tyNatSize tcm nTy), runExcept (tyNatSize tcm mTy)) of+ (Right n, Right m)+ | n == 0 -> changed rArg+ | m == 0 -> changed lArg+ | otherwise -> do+ shouldReduce1 <- orM [ shouldReduce ctx+ , isUntranslatableType_not_poly aTy ]+ if shouldReduce1+ then (`mkTicks` ticks) <$> reduceAppend is0 n m aTy lArg rArg+ else return e+ _ -> return e+ "Clash.Sized.Vector.head" | argLen == 3 -> do+ let [nTy,aTy] = Either.rights args+ [vArg] = Either.lefts args+ case runExcept (tyNatSize tcm nTy) of+ Right n -> do+ shouldReduce1 <- orM [ shouldReduce ctx+ , isUntranslatableType_not_poly aTy ]+ if shouldReduce1+ then (`mkTicks` ticks) <$> reduceHead is0 (n+1) aTy vArg+ else return e+ _ -> return e+ "Clash.Sized.Vector.tail" | argLen == 3 -> do+ let [nTy,aTy] = Either.rights args+ [vArg] = Either.lefts args+ case runExcept (tyNatSize tcm nTy) of+ Right n -> do+ shouldReduce1 <- orM [ shouldReduce ctx+ , isUntranslatableType_not_poly aTy ]+ if shouldReduce1+ then (`mkTicks` ticks) <$> reduceTail is0 (n+1) aTy vArg+ else return e+ _ -> return e+ "Clash.Sized.Vector.last" | argLen == 3 -> do+ let [nTy,aTy] = Either.rights args+ [vArg] = Either.lefts args+ case runExcept (tyNatSize tcm nTy) of+ Right n -> do+ shouldReduce1 <- orM [ shouldReduce ctx+ , isUntranslatableType_not_poly aTy+ ]+ if shouldReduce1+ then (`mkTicks` ticks) <$> reduceLast is0 (n+1) aTy vArg+ else return e+ _ -> return e+ "Clash.Sized.Vector.init" | argLen == 3 -> do+ let [nTy,aTy] = Either.rights args+ [vArg] = Either.lefts args+ case runExcept (tyNatSize tcm nTy) of+ Right n -> do+ shouldReduce1 <- orM [ shouldReduce ctx+ , isUntranslatableType_not_poly aTy ]+ if shouldReduce1+ then (`mkTicks` ticks) <$> reduceInit is0 (n+1) aTy vArg+ else return e+ _ -> return e+ "Clash.Sized.Vector.unconcat" | argLen == 6 -> do+ let ([_knN,_sm,arg],[mTy,nTy,aTy]) = Either.partitionEithers args+ case (runExcept (tyNatSize tcm nTy), runExcept (tyNatSize tcm mTy)) of+ (Right n, Right 0) -> (`mkTicks` ticks) <$> reduceUnconcat n 0 aTy arg+ _ -> return e+ "Clash.Sized.Vector.transpose" | argLen == 5 -> do+ let ([_knN,arg],[mTy,nTy,aTy]) = Either.partitionEithers args+ case (runExcept (tyNatSize tcm nTy), runExcept (tyNatSize tcm mTy)) of+ (Right n, Right 0) -> (`mkTicks` ticks) <$> reduceTranspose n 0 aTy arg+ _ -> return e+ "Clash.Sized.Vector.replicate" | argLen == 4 -> do+ let ([_sArg,vArg],[nTy,aTy]) = Either.partitionEithers args+ case runExcept (tyNatSize tcm nTy) of+ Right n -> do+ shouldReduce1 <- orM [ shouldReduce ctx+ , isUntranslatableType_not_poly aTy+ ]+ if shouldReduce1+ then (`mkTicks` ticks) <$> reduceReplicate n aTy eTy vArg+ else return e+ _ -> return e+ -- replace_int :: KnownNat n => Vec n a -> Int -> a -> Vec n a+ "Clash.Sized.Vector.replace_int" | argLen == 6 -> do+ let ([_knArg,vArg,iArg,aArg],[nTy,aTy]) = Either.partitionEithers args+ case runExcept (tyNatSize tcm nTy) of+ Right n -> do+ shouldReduce1 <- orM [ pure ultra+ , shouldReduce ctx+ , isUntranslatableType_not_poly aTy+ ]+ if shouldReduce1+ then (`mkTicks` ticks) <$> reduceReplace_int is0 n aTy eTy vArg iArg aArg+ else return e+ _ -> return e++ "Clash.Sized.Vector.index_int" | argLen == 5 -> do+ let ([_knArg,vArg,iArg],[nTy,aTy]) = Either.partitionEithers args+ case runExcept (tyNatSize tcm nTy) of+ Right n -> do+ shouldReduce1 <- orM [ pure ultra+ , shouldReduce ctx+ , isUntranslatableType_not_poly aTy ]+ if shouldReduce1+ then (`mkTicks` ticks) <$> reduceIndex_int is0 n aTy vArg iArg+ else return e+ _ -> return e++ "Clash.Sized.Vector.imap" | argLen == 6 -> do+ let [nTy,argElTy,resElTy] = Either.rights args+ case runExcept (tyNatSize tcm nTy) of+ Right n -> do+ shouldReduce1 <- orM [ pure (ultra || n < 2)+ , shouldReduce ctx+ , anyM isUntranslatableType_not_poly [argElTy,resElTy] ]+ if shouldReduce1+ then let [_,fun,arg] = Either.lefts args+ in (`mkTicks` ticks) <$> reduceImap c n argElTy resElTy fun arg+ else return e+ _ -> return e+ "Clash.Sized.Vector.dtfold" | argLen == 8 ->+ let ([_kn,_motive,lrFun,brFun,arg],[_mTy,nTy,aTy]) = Either.partitionEithers args+ in case runExcept (tyNatSize tcm nTy) of+ Right n -> (`mkTicks` ticks) <$> reduceDTFold is0 n aTy lrFun brFun arg+ _ -> return e++ "Clash.Sized.Vector.reverse"+ | ultra+ , ([vArg],[nTy,aTy]) <- Either.partitionEithers args+ , Right n <- runExcept (tyNatSize tcm nTy)+ -> (`mkTicks` ticks) <$> reduceReverse is0 n aTy vArg++ "Clash.Sized.RTree.tdfold" | argLen == 8 ->+ let ([_kn,_motive,lrFun,brFun,arg],[_mTy,nTy,aTy]) = Either.partitionEithers args+ in case runExcept (tyNatSize tcm nTy) of+ Right n -> (`mkTicks` ticks) <$> reduceTFold is0 n aTy lrFun brFun arg+ _ -> return e+ "Clash.Sized.RTree.treplicate" | argLen == 4 -> do+ let ([_sArg,vArg],[nTy,aTy]) = Either.partitionEithers args+ case runExcept (tyNatSize tcm nTy) of+ Right n -> do+ shouldReduce1 <- orM [ shouldReduce ctx+ , isUntranslatableType False aTy ]+ if shouldReduce1+ then (`mkTicks` ticks) <$> reduceTReplicate n aTy eTy vArg+ else return e+ _ -> return e+ "Clash.Sized.Internal.BitVector.split#" | argLen == 4 -> do+ let ([_knArg,bvArg],[nTy,mTy]) = Either.partitionEithers args+ case (runExcept (tyNatSize tcm nTy), runExcept (tyNatSize tcm mTy), tv) of+ (Right n, Right m, TyConApp tupTcNm [lTy,rTy])+ | n == 0 -> do+ let (Just tupTc) = lookupUniqMap tupTcNm tcm+ [tupDc] = tyConDataCons tupTc+ tup = mkApps (Data tupDc)+ [Right lTy+ ,Right rTy+ ,Left bvArg+ ,Left (removedTm rTy)+ ]++ changed (mkTicks tup ticks)+ | m == 0 -> do+ let (Just tupTc) = lookupUniqMap tupTcNm tcm+ [tupDc] = tyConDataCons tupTc+ tup = mkApps (Data tupDc)+ [Right lTy+ ,Right rTy+ ,Left (removedTm lTy)+ ,Left bvArg+ ]++ changed (mkTicks tup ticks)+ _ -> return e+ "Clash.Sized.Internal.BitVector.eq#"+ | ([_,_],[nTy]) <- Either.partitionEithers args+ , Right 0 <- runExcept (tyNatSize tcm nTy)+ , TyConApp boolTcNm [] <- tv+ -> let (Just boolTc) = lookupUniqMap boolTcNm tcm+ [_falseDc,trueDc] = tyConDataCons boolTc+ in changed (mkTicks (Data trueDc) ticks)+ _ -> return e+ where+ isUntranslatableType_not_poly t = do+ u <- isUntranslatableType False t+ if u+ then return (null $ Lens.toListOf typeFreeVars t)+ else return False++reduceNonRepPrim _ e = return e+{-# SCC reduceNonRepPrim #-}++-- | This transformation lifts applications of global binders out of+-- alternatives of case-statements.+--+-- e.g. It converts:+--+-- @+-- case x of+-- A -> f 3 y+-- B -> f x x+-- C -> h x+-- @+--+-- into:+--+-- @+-- let f_arg0 = case x of {A -> 3; B -> x}+-- f_arg1 = case x of {A -> y; B -> x}+-- f_out = f f_arg0 f_arg1+-- in case x of+-- A -> f_out+-- B -> f_out+-- C -> h x+-- @+disjointExpressionConsolidation :: HasCallStack => NormRewrite+disjointExpressionConsolidation ctx@(TransformContext is0 _) e@(Case _scrut _ty _alts@(_:_:_)) = do+ (_,collected) <- collectGlobals is0 [] [] e+ let disJoint = filter (isDisjoint . snd . snd) collected+ if null disJoint+ then return e+ else do+ exprs <- mapM (mkDisjointGroup is0) disJoint+ tcm <- Lens.view tcCache+ lids <- Monad.zipWithM (mkFunOut is0 tcm) disJoint exprs+ let substitution = zip (map fst disJoint) (map Var lids)+ subsMatrix = l2m substitution+ (exprs',_) <- unzip <$> Monad.zipWithM+ (\s (e',seen) -> collectGlobals is0 s seen e')+ subsMatrix+ exprs+ (e',_) <- collectGlobals is0 substitution [] e+ let lb = Letrec (zip lids exprs') e'+ lb' <- bottomupR deadCode ctx lb+ changed lb'+ where+ mkFunOut isN tcm (fun,_) (e',_) = do+ let ty = termType tcm e'+ nm = case collectArgs fun of+ (Var v,_) -> nameOcc (varName v)+ (Prim p,_) -> primName p+ _ -> "complex_expression_"+ nm'' = last (Text.splitOn "." nm) `Text.append` "Out"+ mkInternalVar isN nm'' ty++ l2m = go []+ where+ go _ [] = []+ go xs (y:ys) = (xs ++ ys) : go (xs ++ [y]) ys++disjointExpressionConsolidation _ e = return e+{-# SCC disjointExpressionConsolidation #-}++-- | Given a function in the desired normal form, inline all the following+-- let-bindings:+--+-- Let-bindings with an internal name that is only used once, where it binds:+-- * a primitive that will be translated to an HDL expression (as opposed to+-- a HDL declaration)+-- * a projection case-expression (1 alternative)+-- * a data constructor+-- * I/O actions+inlineCleanup :: HasCallStack => NormRewrite+inlineCleanup (TransformContext is0 _) (Letrec binds body) = do+ prims <- Lens.use (extra.primitives)+ -- For all let-bindings, count the number of times they are referenced.+ -- We only inline let-bindings which are referenced only once, otherwise+ -- we would lose sharing.+ let is1 = extendInScopeSetList is0 (map fst binds)+ bindsFvs = map (\(v,e) -> (v,((v,e),countFreeOccurances e))) binds+ allOccs = List.foldl' (unionVarEnvWith (+)) emptyVarEnv+ $ map (snd.snd) bindsFvs+ bodyFVs = Lens.foldMapOf freeLocalIds unitVarSet body+ (il,keep) = List.partition (isInteresting allOccs prims bodyFVs)+ bindsFvs+ keep' = inlineBndrsCleanup is1 (mkVarEnv il) emptyVarEnv+ $ map snd keep++ if | null il -> return (Letrec binds body)+ | null keep' -> changed body+ | otherwise -> changed (Letrec keep' body)+ where+ -- Determine whether a let-binding is interesting to inline+ isInteresting+ :: VarEnv Int+ -> CompiledPrimMap+ -> VarSet+ -> (Id,((Id, Term), VarEnv Int))+ -> Bool+ isInteresting allOccs prims bodyFVs (id_,((_,(fst.collectArgs) -> tm),_))+ | nameSort (varName id_) /= User+ , id_ `notElemVarSet` bodyFVs+ = case tm of+ Prim pInfo+ | let nm = primName pInfo+ , Just (extractPrim -> Just p@(BlackBox {})) <- HashMap.lookup nm prims+ , TExpr <- kind p+ , Just occ <- lookupVarEnv id_ allOccs+ , occ < 2+ -> True+ | otherwise+ -> primName pInfo `elem` ["Clash.Explicit.SimIO.bindSimIO#"]+ Case _ _ [_] -> True+ Data _ -> True+ Case _ aTy (_:_:_)+ | TyConApp (nameOcc -> "Clash.Explicit.SimIO.SimIO") _ <- tyView aTy+ -> True+ _ -> False+ | id_ `notElemVarSet` bodyFVs+ = case tm of+ Prim pInfo+ | primName pInfo `elem` [ "Clash.Explicit.SimIO.openFile"+ , "Clash.Explicit.SimIO.fgetc"+ , "Clash.Explicit.SimIO.feof"+ ]+ , Just occ <- lookupVarEnv id_ allOccs+ , occ < 2+ -> True+ | otherwise+ -> primName pInfo `elem` ["Clash.Explicit.SimIO.bindSimIO#"]+ Case _ _ [(DataPat dcE _ _,_)]+ -> let nm = (nameOcc (dcName dcE))+ in -- Inlines WW projection that exposes internals of the BitVector types+ nm == "Clash.Sized.Internal.BitVector.BV" ||+ nm == "Clash.Sized.Internal.BitVector.Bit" ||+ -- Inlines projections out of constraint-tuples (e.g. HiddenClockReset)+ "GHC.Classes" `Text.isPrefixOf` nm+ Case _ aTy (_:_:_)+ | TyConApp (nameOcc -> "Clash.Explicit.SimIO.SimIO") _ <- tyView aTy+ -> True+ _ -> False++ isInteresting _ _ _ _ = False++inlineCleanup _ e = return e+{-# SCC inlineCleanup #-}++-- | Mark to track progress of 'reduceBindersCleanup'+data Mark = Temp | Done | Rec++-- | Used by 'inlineCleanup' to inline binders that we want to inline into the+-- binders that we want to keep.+inlineBndrsCleanup+ :: InScopeSet+ -- ^ Current InScopeSet+ -> VarEnv ((Id,Term),VarEnv Int)+ -- ^ Original let-binders with their free variables (+ #occurrences), that we+ -- want to inline+ -> VarEnv ((Id,Term),VarEnv Int,Mark)+ -- ^ Processed let-binders with their free variables and a tag to mark the+ -- progress:+ -- * Temp: Will eventually form a recursive cycle+ -- * Done: Processed, non-recursive+ -- * Rec: Processed, recursive+ -> [((Id,Term),VarEnv Int)]+ -- ^ The let-binders with their free variables (+ #occurrences), that we want+ -- to keep+ -> [(Id,Term)]+inlineBndrsCleanup isN origInl = go+ where+ go doneInl [] =+ -- If some of the let-binders that we wanted to inline turn out to be+ -- recursive, then we have to keep those around as well, as we weren't able+ -- to inline them.+ [ (v,e) | ((v,e),_,Rec) <- eltsVarEnv doneInl ]+ go !doneInl (((v,e),eFVs):il) =+ let (sM,_,doneInl1) = foldlWithUniqueVarEnv'+ (reduceBindersCleanup isN origInl)+ (Nothing, emptyVarEnv, doneInl)+ eFVs+ e1 = case sM of+ Nothing -> e+ Just s -> substTm "inlineBndrsCleanup" s e+ in (v,e1):go doneInl1 il+{-# SCC inlineBndrsCleanup #-}++-- | Used (transitively) by 'inlineCleanup' inline to-inline let-binders into+-- the other to-inline let-binders.+reduceBindersCleanup+ :: InScopeSet+ -- ^ Current InScopeSet+ -> VarEnv ((Id,Term),VarEnv Int)+ -- ^ Original let-binders with their free variables (+ #occurrences)+ -> (Maybe Subst,VarEnv Int,VarEnv ((Id,Term),VarEnv Int,Mark))+ -- ^ Accumulated:+ --+ -- 1. (Maybe) the build up substitution so far+ -- 2. The free variables of the range of the substitution+ -- 3. Processed let-binders with their free variables and a tag to mark+ -- the progress:+ -- * Temp: Will eventually form a recursive cycle+ -- * Done: Processed, non-recursive+ -- * Rec: Processed, recursive+ -> Unique+ -- ^ The unique of the let-binding that we want to simplify+ -> Int+ -- ^ Ignore, artifact of 'foldlWithUniqueVarEnv'+ -> (Maybe Subst,VarEnv Int,VarEnv ((Id,Term),VarEnv Int,Mark))+ -- ^ Same as the third argument+reduceBindersCleanup isN origInl (!substM,!substFVs,!doneInl) u _ = case lookupVarEnvDirectly u doneInl of+ Nothing -> case lookupVarEnvDirectly u origInl of+ Nothing ->+ -- let-binding not found, cannot extend the substitution+ (substM,substFVs,doneInl)+ Just ((v,e),eFVs) ->+ -- Simplify the transitive dependencies+ let (sM,substFVsE,doneInl1) =+ foldlWithUniqueVarEnv'+ (reduceBindersCleanup isN origInl)+ ( Nothing+ -- It's okay/needed to over-approximate the free variables of+ -- the range of the new substitution by including the free+ -- variables of the original let-binder, because this set of+ -- free variables is only used to check whether let-binding will+ -- become self-recursive after applying the substitution.+ --+ -- That is, it was already self-recursive, or becomes+ -- self-recursive after applying the substitution because it was+ -- part of a recursive group. And we do not want to inline+ -- recursive binders.+ , eFVs+ -- Temporarily extend the processing environment with the+ -- let-binding so we don't end up in a loop in case there is a+ -- recursive group.+ , extendVarEnv v ((v,e),eFVs,Temp) doneInl)+ eFVs++ e1 = case sM of+ Nothing -> e+ Just s -> substTm "reduceBindersCleanup" s e+ in if v `elemVarEnv` substFVsE then+ -- We cannot inline recursive let-bindings, so we do not extend+ -- the substitution environment.+ ( substM+ , substFVs+ -- And we explicitly mark the let-binding as recursive in the+ -- processing environment. So that it will be kept around at the+ -- end of 'inlineCleanup'+ , extendVarEnv v ((v,e1),substFVsE,Rec) doneInl1+ )+ else+ -- Extend the substitution+ ( Just (extendIdSubst (Maybe.fromMaybe (mkSubst isN) substM) v e1)+ , unionVarEnv substFVsE substFVs+ -- Mark the let-binding a fully "reduced", so we don't repeat+ -- this process when we encounter it again.+ , extendVarEnv v ((v,e1),substFVsE,Done) doneInl1+ )+ -- It's already been process, just extend the substitution environment+ Just ((v,e),eFVs,Done) ->+ ( Just (extendIdSubst (Maybe.fromMaybe (mkSubst isN) substM) v e)+ , unionVarEnv eFVs substFVs+ , doneInl+ )++ -- It's either recursive (Rec), or part of a recursive group (Temp) where we+ -- originally entered a different part of the cycle. Regardless, we do not+ -- extend the substitution environment.+ Just _ ->+ ( substM+ , substFVs+ , doneInl+ )+{-# SCC reduceBindersCleanup #-}++-- | Flatten's letrecs after `inlineCleanup`+--+-- `inlineCleanup` sometimes exposes additional possibilities for `caseCon`,+-- which then introduces let-bindings in what should be ANF. This transformation+-- flattens those nested let-bindings again.+--+-- NB: must only be called in the cleaning up phase.+flattenLet :: HasCallStack => NormRewrite+flattenLet (TransformContext is0 _) (Letrec binds body) = do+ let is1 = extendInScopeSetList is0 (map fst binds)+ bodyOccs = Lens.foldMapByOf+ freeLocalIds (unionVarEnvWith (+))+ emptyVarEnv (`unitVarEnv` (1 :: Int))+ body+ (is2,binds1) <- second concat <$> mapAccumLM go is1 binds+ case binds1 of+ -- inline binders into the body when there's only a single binder, and only+ -- if that binder doesn't perform any work or is only used once in the body+ [(id1,e1)] | Just occ <- lookupVarEnv id1 bodyOccs, isWorkFree e1 || occ < 2 ->+ if id1 `localIdOccursIn` e1+ -- Except when the binder is recursive!+ then return (Letrec binds1 body)+ else let subst = extendIdSubst (mkSubst is2) id1 e1+ in changed (substTm "flattenLet" subst body)+ _ -> return (Letrec binds1 body)+ where+ go :: InScopeSet -> LetBinding -> NormalizeSession (InScopeSet,[LetBinding])+ go isN (id1,collectTicks -> (Letrec binds1 body1,ticks)) = do+ let bs1 = map fst binds1+ let (binds2,body2,isN1) =+ -- We need to deshadow because we're merging nested let-expressions+ -- into a single let-expression: and within a let-expression, the+ -- bindings are not allowed to shadow each-other. Of course, we+ -- only need to deshadow if any shadowing is happening in the+ -- first place.+ --+ -- This is much better than blindly calling freshenTm, and saves+ -- almost 30% run-time of the normalization phase on some examples.+ if any (`elemInScopeSet` isN) bs1 then+ let Letrec bindsN bodyN = deShadowTerm isN (Letrec binds1 body1)+ in (bindsN,bodyN,extendInScopeSetList isN (map fst bindsN))+ else+ (binds1,body1,extendInScopeSetList isN bs1)+ let bodyOccs = Lens.foldMapByOf+ freeLocalIds (unionVarEnvWith (+))+ emptyVarEnv (`unitVarEnv` (1 :: Int))+ body2+ (srcTicks,nmTicks) = partitionTicks ticks+ -- Distribute the name ticks of the let-expression over all the bindings+ (isN1,) . map (second (`mkTicks` nmTicks)) <$> case binds2 of+ -- inline binders into the body when there's only a single binder, and+ -- only if that binder doesn't perform any work or is only used once in+ -- the body+ [(id2,e2)] | Just occ <- lookupVarEnv id2 bodyOccs, isWorkFree e2 || occ < 2 ->+ if id2 `localIdOccursIn` e2+ -- Except when the binder is recursive!+ then changed ([(id2,e2),(id1, body2)])+ else let subst = extendIdSubst (mkSubst isN1) id2 e2+ in changed [(id1+ -- Only apply srcTicks to the body+ ,mkTicks (substTm "flattenLetGo" subst body2)+ srcTicks)]+ bs -> changed (bs ++ [(id1+ -- Only apply srcTicks to the body+ ,mkTicks body2 srcTicks)])+ go isN b = return (isN,[b])++flattenLet _ e = return e+{-# SCC flattenLet #-}++-- | Worker function of 'separateArguments'.+separateLambda+ :: TyConMap+ -> TransformContext+ -> Id+ -- ^ Lambda binder+ -> Term+ -- ^ Lambda body+ -> Maybe Term+ -- ^ If lambda is split up, this function returns a Just containing the new term+separateLambda tcm ctx@(TransformContext is0 _) b eb0 =+ case shouldSplit tcm (varType b) of+ Just (dc,argTys@(_:_:_)) ->+ let+ nm = mkDerivedName ctx (nameOcc (varName b))+ bs0 = map (`mkLocalId` nm) argTys+ (is1, bs1) = List.mapAccumL newBinder is0 bs0+ subst = extendIdSubst (mkSubst is1) b (mkApps dc (map (Left . Var) bs1))+ eb1 = substTm "separateArguments" subst eb0+ in+ Just (mkLams eb1 bs1)+ _ ->+ Nothing+ where+ newBinder isN0 x =+ let+ x' = uniqAway isN0 x+ isN1 = extendInScopeSet isN0 x'+ in+ (isN1, x')+{-# SCC separateLambda #-}++-- | Split apart (global) function arguments that contain types that we+-- want to separate off, e.g. Clocks. Works on both the definition side (i.e. the+-- lambda), and the call site (i.e. the application of the global variable). e.g.+-- turns+--+-- > f :: (Clock System, Reset System) -> Signal System Int+--+-- into+--+-- > f :: Clock System -> Reset System -> Signal System Int+separateArguments :: HasCallStack => NormRewrite+separateArguments ctx e0@(Lam b eb) = do+ tcm <- Lens.view tcCache+ case separateLambda tcm ctx b eb of+ Just e1 -> changed e1+ Nothing -> return e0++separateArguments (TransformContext is0 _) e@(collectArgsTicks -> (Var g, args, ticks))+ | isGlobalId g = do+ -- We ensure that both the type of the global variable reference is updated+ -- to take into account the changed arguments, and that we apply the global+ -- function with the split apart arguments.+ let (argTys0,resTy) = splitFunForallTy (varType g)+ (concat -> args1, Monoid.getAny -> hasChanged)+ <- listen (mapM (uncurry splitArg) (zip argTys0 args))+ if hasChanged then+ let (argTys1,args2) = unzip args1+ gTy = mkPolyFunTy resTy argTys1+ in return (mkApps (mkTicks (Var g {varType = gTy}) ticks) args2)+ else+ return e++ where+ -- Split a single argument+ splitArg+ :: Either TyVar Type+ -- The quantifier/function argument type of the global variable+ -> Either Term Type+ -- The applied type argument or term argument+ -> NormalizeSession [(Either TyVar Type,Either Term Type)]+ splitArg tv arg@(Right _) = return [(tv,arg)]+ splitArg ty arg@(Left tmArg) = do+ tcm <- Lens.view tcCache+ let argTy = termType tcm tmArg+ case shouldSplit tcm argTy of+ Just (_,argTys@(_:_:_)) -> do+ tmArgs <- mapM (mkSelectorCase ($(curLoc) ++ "splitArg") is0 tcm tmArg 1)+ [0..length argTys - 1]+ changed (map ((ty,) . Left) tmArgs)+ _ ->+ return [(ty,arg)]++separateArguments _ e = return e+{-# SCC separateArguments #-}++-- | Remove all undefined alternatives from case expressions, replacing them+-- with the value of another defined alternative. If there is one defined+-- alternative, the entire expression is replaced with that alternative. If+-- there are no defined alternatives, the entire expression is replaced with+-- a call to 'errorX'.+--+-- e.g. It converts+--+-- case x of+-- D1 a -> f a+-- D2 -> undefined+-- D3 -> undefined+--+-- to+--+-- let subj = x+-- a = case subj of+-- D1 a -> field0+-- in f a+--+-- where fieldN is an internal variable referring to the nth argument of a+-- data constructor.+--+xOptimize :: HasCallStack => NormRewrite+xOptimize (TransformContext is0 _) e@(Case subj ty alts) = do+ runXOpt <- Lens.view aggressiveXOpt++ if runXOpt then do+ defPart <- partitionM (isPrimError . snd) alts++ case defPart of+ ([], _) -> return e+ (_, []) -> changed (Prim (PrimInfo "Clash.XException.errorX" ty WorkConstant))+ (_, [alt]) -> xOptimizeSingle is0 subj alt+ (_, defs) -> xOptimizeMany is0 subj ty defs+ else+ return e++xOptimize _ e = return e+{-# SCC xOptimize #-}++-- Return an expression equivalent to the alternative given. When only one+-- alternative is defined the result of this function is used to replace the+-- case expression.+--+xOptimizeSingle :: InScopeSet -> Term -> Alt -> NormalizeSession Term+xOptimizeSingle is subj (DataPat dc tvs vars, expr) = do+ tcm <- Lens.view tcCache+ subjId <- mkInternalVar is "subj" (termType tcm subj)++ let fieldTys = fmap varType vars+ lets <- Monad.zipWithM (mkFieldSelector is subjId dc tvs fieldTys) vars [0..]++ changed (Letrec ((subjId, subj) : lets) expr)++xOptimizeSingle _ _ (_, expr) = changed expr++-- Given a list of alternatives which are defined, create a new case+-- expression which only ever returns a defined value.+--+xOptimizeMany+ :: HasCallStack+ => InScopeSet+ -> Term+ -> Type+ -> [Alt]+ -> NormalizeSession Term+xOptimizeMany is subj ty defs@(d:ds)+ | isAnyDefault defs = changed (Case subj ty defs)+ | otherwise = do+ newAlt <- xOptimizeSingle is subj d+ changed (Case subj ty $ ds <> [(DefaultPat, newAlt)])+ where+ isAnyDefault :: [Alt] -> Bool+ isAnyDefault = any ((== DefaultPat) . fst)++xOptimizeMany _ _ _ [] =+ error $ $(curLoc) ++ "Report as bug: xOptimizeMany error: No defined alternatives"++mkFieldSelector+ :: MonadUnique m+ => InScopeSet+ -> Id+ -- ^ subject id+ -> DataCon+ -> [TyVar]+ -> [Type]+ -- ^ concrete types of fields+ -> Id+ -> Int+ -> m LetBinding+mkFieldSelector is0 subj dc tvs fieldTys nm index = do+ fields <- mapM (\ty -> mkInternalVar is0 "field" ty) fieldTys+ let alt = (DataPat dc tvs fields, Var $ fields !! index)+ return (nm, Case (Var subj) (fieldTys !! index) [alt])++-- Check whether a term is really a black box primitive representing an error.+-- Such values are undefined and are removed in X Optimization.+--+isPrimError :: Term -> NormalizeSession Bool+isPrimError (collectArgs -> (Prim pInfo, _)) = do+ prim <- Lens.use (extra . primitives . Lens.at (primName pInfo))++ case prim >>= extractPrim of+ Just p -> return (isErr p)+ Nothing -> return False+ where+ isErr BlackBox{template=(BBTemplate [Err _])} = True+ isErr _ = False++isPrimError _ = return False+
src/Clash/Normalize/Types.hs view
@@ -7,7 +7,7 @@ Types used in Normalize modules -} -{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TemplateHaskell #-} module Clash.Normalize.Types where @@ -68,6 +68,8 @@ -- ^ High-effort normalization session, trading performance improvement for -- potentially much longer compile times. Follows the 'Clash.Driver.opt_ultra' -- flag.+ , _inlineWFCacheLimit :: !Word+ -- ^ At what size do we cache normalized work-free top-level binders. } makeLenses ''NormalizeState
src/Clash/Normalize/Util.hs view
@@ -6,11 +6,9 @@ Utility functions used by the normalisation transformations -} -{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE RecordWildCards #-} module Clash.Normalize.Util ( ConstantSpecInfo(..)@@ -29,10 +27,13 @@ , normalizeTopLvlBndr , rewriteExpr , removedTm+ , mkInlineTick+ , substWithTyEq+ , tvSubstWithTyEq ) where -import Control.Lens ((&),(+~),(%=),(^.),_4,(.=))+import Control.Lens ((&),(+~),(%=),(.=)) import qualified Control.Lens as Lens import Data.Bifunctor (bimap) import Data.Either (lefts)@@ -40,26 +41,33 @@ import qualified Data.Map as Map import qualified Data.HashMap.Strict as HashMapS import Data.Text (Text)+import qualified Data.Text as Text -import BasicTypes (InlineSpec)+import PrelNames (eqTyConKey)+import Unique (getKey) import Clash.Annotations.Primitive (extractPrim) import Clash.Core.FreeVars (globalIds, hasLocalFreeVars, globalIdOccursIn)+import Clash.Core.Name (Name(nameOcc,nameUniq)) import Clash.Core.Pretty (showPpr)-import Clash.Core.Subst (deShadowTerm)+import Clash.Core.Subst+ (deShadowTerm, extendTvSubst, extendTvSubstList, mkSubst, substTm, substTy,+ substId, extendIdSubst) import Clash.Core.Term (Context, CoreContext(AppArg), PrimInfo (..), Term (..), WorkInfo (..),- TickInfo, collectArgs, collectArgsTicks)+ TickInfo(NameMod), NameMod(PrefixName), collectArgs, collectArgsTicks) import Clash.Core.TyCon (TyConMap)-import Clash.Core.Type (Type, undefinedTy)+import Clash.Core.Type+ (Type(LitTy, VarTy), LitTy(SymTy), TypeView (..), tyView, undefinedTy,+ splitFunForallTy, splitTyConAppM, mkPolyFunTy) import Clash.Core.Util (isClockOrReset, isPolyFun, termType, mkApps, mkTicks)-import Clash.Core.Var (Id, Var (..), isGlobalId)+import Clash.Core.Var (Id, TyVar, Var (..), isGlobalId) import Clash.Core.VarEnv (VarEnv, emptyInScopeSet, emptyVarEnv, extendVarEnv, extendVarEnvWith, lookupVarEnv, unionVarEnvWith, unitVarEnv, extendInScopeSetList)-import Clash.Driver.Types (BindingMap, DebugLevel (..))+import Clash.Driver.Types (BindingMap, Binding(..), DebugLevel (..)) import {-# SOURCE #-} Clash.Normalize.Strategy (normalization) import Clash.Normalize.Types import Clash.Primitives.Util (constantArgs)@@ -83,6 +91,7 @@ -- ^ Yields @DontCare@ for if given primitive name is not found, if the -- argument does not exist, or if the argument was not mentioned by the -- blackbox.+isConstantArg "Clash.Explicit.SimIO.mealyIO" i = pure (i == 2 || i == 3) isConstantArg nm i = do argMap <- Lens.use (extra.primitiveArgs) case Map.lookup nm argMap of@@ -170,11 +179,11 @@ fBodyM <- lookupVarEnv f <$> Lens.use bindings case fBodyM of Nothing -> return False- Just (_,_,_,fBody) -> do+ Just b -> do -- There are no global mutually-recursive functions, only self-recursive -- ones, so checking whether 'f' is part of the free variables of the -- body of 'f' is sufficient.- let isR = f `globalIdOccursIn` fBody+ let isR = f `globalIdOccursIn` bindingTerm b (extra.recursiveComponents) %= extendVarEnv f isR return isR @@ -290,17 +299,17 @@ -- primitive's workinfo. if isClockOrReset tcm (termType tcm e) then case collectArgs e of- (Prim "Clash.Transformations.removedArg" _, _) ->- pure (constantCsr e)- _ -> do- bindCsr ctx e+ (Prim p, _)+ | primName p == "Clash.Transformations.removedArg" ->+ pure (constantCsr e)+ _ -> bindCsr ctx e else case collectArgsTicks e of (dc@(Data _), args, ticks) -> mergeCsrs ctx ticks e (mkApps dc) args -- TODO: Work with prim's WorkInfo?- (prim@(Prim _ _), args, ticks) -> do+ (prim@(Prim _), args, ticks) -> do csr <- mergeCsrs ctx ticks e (mkApps prim) args if null (csrNewBindings csr) then pure csr@@ -346,7 +355,7 @@ | Nothing <- lookupUniqMap root cg , Just rootTm <- lookupUniqMap root bndrs = let used = Lens.foldMapByOf globalIds (unionVarEnvWith (+))- emptyVarEnv (`unitUniqMap` 1) (rootTm ^. _4)+ emptyVarEnv (`unitUniqMap` 1) (bindingTerm rootTm) cg' = extendUniqMap root used cg in List.foldl' go cg' (keysUniqMap used) go cg _ = cg@@ -388,10 +397,11 @@ | otherwise -> False normalizeTopLvlBndr- :: Id- -> (Id, SrcSpan, InlineSpec, Term)- -> NormalizeSession (Id, SrcSpan, InlineSpec, Term)-normalizeTopLvlBndr nm (nm',sp,inl,tm) = makeCachedU nm (extra.normalized) $ do+ :: Bool+ -> Id+ -> Binding+ -> NormalizeSession Binding+normalizeTopLvlBndr isTop nm (Binding nm' sp inl tm) = makeCachedU nm (extra.normalized) $ do tcm <- Lens.view tcCache let nmS = showPpr (varName nm) -- We deshadow the term because sometimes GHC gives us@@ -399,16 +409,83 @@ -- global binder, sometimes causing the inliner to go -- into a loop. Deshadowing freshens all the bindings -- to avoid this.- --- -- Additionally, it allows for a much cheaper `appProp`- -- transformation, see Note [AppProp no-shadow invariant] let tm1 = deShadowTerm emptyInScopeSet tm+ tm2 = if isTop then substWithTyEq tm1 else tm1 old <- Lens.use curFun- tm2 <- rewriteExpr ("normalization",normalization) (nmS,tm1) (nm',sp)+ tm3 <- rewriteExpr ("normalization",normalization) (nmS,tm2) (nm',sp) curFun .= old- let ty' = termType tcm tm2- return (nm' {varType = ty'},sp,inl,tm2)+ let ty' = termType tcm tm3+ return (Binding nm'{varType = ty'} sp inl tm3) +-- | Turn type equality constraints into substitutions and apply them.+--+-- So given:+--+-- > /\dom . \(eq : dom ~ "System") . \(eta : Signal dom Bool) . eta+--+-- we create the substitution [dom := "System"] and apply it to create:+--+-- > \(eq : "System" ~ "System") . \(eta : Signal "System" Bool) . eta+--+-- __NB:__ Users of this function should ensure it's only applied to TopEntities+substWithTyEq+ :: Term+ -> Term+substWithTyEq e0 = go [] False [] e0+ where+ go+ :: [TyVar]+ -> Bool+ -> [Id]+ -> Term+ -> Term+ go tvs changed ids_ (TyLam tv e) = go (tv:tvs) changed ids_ e+ go tvs changed ids_ (Lam v e)+ | TyConApp (nameUniq -> tcUniq) (tvFirst -> Just (tv, ty)) <- tyView (varType v)+ , tcUniq == getKey eqTyConKey+ , tv `elem` tvs+ = let+ subst0 = extendTvSubst (mkSubst emptyInScopeSet) tv ty+ subst1 = extendIdSubst subst0 v (removedTm (varType v))+ in go (tvs List.\\ [tv]) True (substId subst0 v : ids_) (substTm "substWithTyEq e" subst1 e)+ | otherwise = go tvs changed (v:ids_) e+ go tvs True ids_ e =+ let+ e1 = List.foldl' (flip TyLam) e tvs+ e2 = List.foldl' (flip Lam) e1 ids_+ in e2+ go _ False _ _ = e0++-- Type equality (~) is symmetrical, so users could write: (dom ~ System) or (System ~ dom)+tvFirst :: [Type] -> Maybe (TyVar, Type)+tvFirst [_, VarTy tv, ty] = Just (tv, ty)+tvFirst [_, ty, VarTy tv] = Just (tv, ty)+tvFirst _ = Nothing++-- | The type equivalent of 'substWithTyEq'+tvSubstWithTyEq+ :: Type+ -> Type+tvSubstWithTyEq ty0 = go [] args0+ where+ (args0,tyRes) = splitFunForallTy ty0++ go :: [(TyVar,Type)] -> [Either TyVar Type] -> Type+ go eqs (Right arg : args)+ | Just (tc,tcArgs) <- splitTyConAppM arg+ , nameUniq tc == getKey eqTyConKey+ , Just eq <- tvFirst tcArgs+ = go (eq:eqs) args+ | otherwise = go eqs args+ go eqs (Left _tv : args)+ = go eqs args -- drop (ForAll) tv+ go [] [] = ty0 -- no eq constraints, returning original type+ go eqs [] = substTy subst ty2+ where+ subst = extendTvSubstList (mkSubst emptyInScopeSet) eqs+ args2 = args0 List.\\ (map (Left . fst) eqs)+ ty2 = mkPolyFunTy tyRes args2+ -- | Rewrite a term according to the provided transformation rewriteExpr :: (String,NormRewrite) -- ^ Transformation to apply -> (String,Term) -- ^ Term to transform@@ -431,4 +508,20 @@ :: Type -> Term removedTm =- TyApp (Prim "Clash.Transformations.removedArg" (PrimInfo undefinedTy WorkNever))+ TyApp (Prim (PrimInfo "Clash.Transformations.removedArg" undefinedTy WorkNever))++-- | A tick to prefix an inlined expression with it's original name.+-- For example, given+--+-- foo = bar -- ...+-- bar = baz -- ...+-- baz = quuz -- ...+--+-- if bar is inlined into foo, then the name of the component should contain+-- the name of the inlined component. This tick ensures that the component in+-- foo is called bar_baz instead of just baz.+--+mkInlineTick :: Id -> TickInfo+mkInlineTick n = NameMod PrefixName (LitTy . SymTy $ toStr n)+ where+ toStr = Text.unpack . snd . Text.breakOnEnd "." . nameOcc . varName
src/Clash/Primitives/GHC/Int.hs view
@@ -5,9 +5,8 @@ Blackbox generation for GHC.Int.IntX# data constructors. (System)Verilog only! -}-{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ViewPatterns #-} module Clash.Primitives.GHC.Int (intTF) where
src/Clash/Primitives/Intel/ClockGen.hs view
@@ -7,6 +7,7 @@ -} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE ViewPatterns #-} module Clash.Primitives.Intel.ClockGen where@@ -15,10 +16,12 @@ import Clash.Netlist.BlackBox.Util import Clash.Netlist.Id import Clash.Netlist.Types+import Clash.Netlist.Util hiding (mkUniqueIdentifier) import Control.Monad.State import Data.Semigroup.Monad+import qualified Data.String.Interpolate.IsString as I import Data.Text.Prettyprint.Doc.Extra import qualified Data.Text as TextS@@ -26,25 +29,45 @@ altpllTF :: TemplateFunction altpllTF = TemplateFunction used valid altpllTemplate where- used = [0,1,2]+ used = [0..4] valid bbCtx- | [(nm,_,_),_,_] <- bbInputs bbCtx+ | [_,_,(nm,_,_),_,_] <- bbInputs bbCtx , Just _ <- exprToString nm , (Identifier _ Nothing,Product {}) <- bbResult bbCtx = True valid _ = False +altpllQsysTF :: TemplateFunction+altpllQsysTF = TemplateFunction used valid altpllQsysTemplate+ where+ used = [0..4]+ valid bbCtx+ | [_,_,(nm,_,_),_,_] <- bbInputs bbCtx+ , Just _ <- exprToString nm+ , (Identifier _ Nothing,Product {}) <- bbResult bbCtx+ = True+ valid _ = False+ alteraPllTF :: TemplateFunction alteraPllTF = TemplateFunction used valid alteraPllTemplate where- used = [1,2,3]+ used = [1..20] valid bbCtx- | [_,(nm,_,_),_,_] <- bbInputs bbCtx+ | ((nm,_,_):_) <- drop 3 (bbInputs bbCtx) , Just _ <- exprToString nm- , (Identifier _ Nothing,Product {}) <- bbResult bbCtx = True valid _ = False +alteraPllQsysTF :: TemplateFunction+alteraPllQsysTF = TemplateFunction used valid alteraPllQsysTemplate+ where+ used = [1..20]+ valid bbCtx+ | ((nm,_,_):_) <- drop 3 (bbInputs bbCtx)+ , Just _ <- exprToString nm+ = True+ valid _ = False+ alteraPllTemplate :: Backend s => BlackBoxContext@@ -53,14 +76,14 @@ let mkId = mkUniqueIdentifier Basic locked <- mkId "locked" pllLock <- mkId "pllLock"- alteraPll <- mkId "alteraPll"- alteraPll_inst <- mkId "alterPll_inst"+ alteraPll <- mkId "altera_pll_block"+ alteraPll_inst <- mkId instname0 clocks <- traverse (mkUniqueIdentifier Extended) [TextS.pack ("pllOut" ++ show n) | n <- [0..length tys - 1]] getMon $ blockDecl alteraPll $ concat [[ NetDecl Nothing locked rstTy- , NetDecl Nothing pllLock Bool]+ , NetDecl' Nothing Reg pllLock (Right Bool) Nothing] ,[ NetDecl Nothing clkNm ty | (clkNm,ty) <- zip clocks tys] ,[ InstDecl Comp Nothing compName alteraPll_inst [] $ concat [[(Identifier "refclk" Nothing,In,clkTy,clk)@@ -78,10 +101,11 @@ ] ] where- [_,(nm,_,_),(clk,clkTy,_),(rst,rstTy,_)] = bbInputs bbCtx- (Identifier result Nothing,resTy@(Product _ _ (tail -> tys))) = bbResult bbCtx+ (Identifier result Nothing,resTy@(Product _ _ (init -> tys))) = bbResult bbCtx+ [(nm,_,_),(clk,clkTy,_),(rst,rstTy,_)] = drop 3 (bbInputs bbCtx) Just nm' = exprToString nm- compName = TextS.pack nm'+ instname0 = TextS.pack nm'+ compName = head (bbQsysIncName bbCtx) altpllTemplate :: Backend s@@ -92,14 +116,14 @@ pllOut <- mkId "pllOut" locked <- mkId "locked" pllLock <- mkId "pllLock"- alteraPll <- mkId "altpll"- alteraPll_inst <- mkId "altpll_inst"+ alteraPll <- mkId "altpll_block"+ alteraPll_inst <- mkId instname0 getMon $ blockDecl alteraPll [ NetDecl Nothing locked Bit- , NetDecl Nothing pllLock Bool+ , NetDecl' Nothing Reg pllLock (Right Bool) Nothing , NetDecl Nothing pllOut clkOutTy , InstDecl Comp Nothing compName alteraPll_inst []- [(Identifier "inclk0" Nothing,In,clkTy,clk)+ [(Identifier "clk" Nothing,In,clkTy,clk) ,(Identifier "areset" Nothing,In,rstTy,rst) ,(Identifier "c0" Nothing,Out,clkOutTy,Identifier pllOut Nothing) ,(Identifier "locked" Nothing,Out,Bit,Identifier locked Nothing)]@@ -112,8 +136,94 @@ ] where- [(nm,_,_),(clk,clkTy,_),(rst,rstTy,_)] = bbInputs bbCtx+ [_,_,(nm,_,_),(clk,clkTy,_),(rst,rstTy,_)] = bbInputs bbCtx (Identifier result Nothing,resTy@(Product _ _ [clkOutTy,_])) = bbResult bbCtx Just nm' = exprToString nm- compName = TextS.pack nm'+ instname0 = TextS.pack nm'+ compName = head (bbQsysIncName bbCtx) ++altpllQsysTemplate+ :: Backend s+ => BlackBoxContext+ -> State s Doc+altpllQsysTemplate bbCtx = pure bbText+ where+ ((_,stripVoid -> kdIn,_):(_,stripVoid -> kdOut,_):_) = bbInputs bbCtx+ KnownDomain _ clkInPeriod _ _ _ _ = kdIn+ KnownDomain _ clkOutPeriod _ _ _ _ = kdOut+ clkOutFreq :: Double+ clkOutFreq = (1.0 / (fromInteger clkOutPeriod * 1.0e-12)) / 1e6+ clklcm = lcm clkInPeriod clkOutPeriod+ clkmult = clklcm `quot` clkOutPeriod+ clkdiv = clklcm `quot` clkInPeriod+ -- Note [QSys file templates]+ -- This QSys file template was derived from a "full" QSys system with a single+ -- "altpll" IP. Module parameters were then stripped on a trial-and-error+ -- basis to get a template that has the minimal number of parameters, but+ -- still has the desired, working, configuration.+ bbText = [I.i|<?xml version="1.0" encoding="UTF-8"?>+<system name="$${FILENAME}">+ <module+ name="altpll0"+ kind="altpll"+ enabled="1"+ autoexport="1">+ <parameter name="AVALON_USE_SEPARATE_SYSCLK" value="NO" />+ <parameter name="BANDWIDTH" value="" />+ <parameter name="BANDWIDTH_TYPE" value="AUTO" />+ <parameter name="CLK0_DIVIDE_BY" value="#{clkdiv}" />+ <parameter name="CLK0_DUTY_CYCLE" value="50" />+ <parameter name="CLK0_MULTIPLY_BY" value="#{clkmult}" />+ <parameter name="CLK0_PHASE_SHIFT" value="0" />+ <parameter name="COMPENSATE_CLOCK" value="CLK0" />+ <parameter name="INCLK0_INPUT_FREQUENCY" value="#{clkInPeriod}" />+ <parameter name="OPERATION_MODE" value="NORMAL" />+ <parameter name="PLL_TYPE" value="AUTO" />+ <parameter name="PORT_ARESET" value="PORT_USED" />+ <parameter name="PORT_INCLK0" value="PORT_USED" />+ <parameter name="PORT_LOCKED" value="PORT_USED" />+ <parameter name="PORT_clk0" value="PORT_USED" />+ <parameter name="HIDDEN_IS_FIRST_EDIT" value="0" />+ <parameter name="HIDDEN_PRIVATES">PT#EFF_OUTPUT_FREQ_VALUE0 #{clkOutFreq}</parameter>+ </module>+</system>|]++alteraPllQsysTemplate+ :: Backend s+ => BlackBoxContext+ -> State s Doc+alteraPllQsysTemplate bbCtx = pure bbText+ where+ (_:(_,stripVoid -> kdIn,_):(_,stripVoid -> kdOutsProd,_):_) = bbInputs bbCtx+ kdOuts = case kdOutsProd of+ Product _ _ ps -> ps+ KnownDomain {} -> [kdOutsProd]+ _ -> error "internal error: not a Product or KnownDomain"++ cklFreq (KnownDomain _ p _ _ _ _)+ = (1.0 / (fromInteger p * 1.0e-12 :: Double)) / 1e6+ cklFreq _ = error "internal error: not a KnownDomain"++ clkOuts = TextS.unlines+ [[I.i|<parameter name="gui_output_clock_frequency#{n}" value="#{f}"/>|]+ | (n,f) <- zip [(0 :: Word)..] (map cklFreq kdOuts)+ ]++ -- See Note [QSys file templates] on how this qsys template was derived.+ bbText = [I.i|<?xml version="1.0" encoding="UTF-8"?>+<system name="$${FILENAME}">+ <module+ name="pll_0"+ kind="altera_pll"+ enabled="1"+ autoexport="1">+ <parameter name="gui_feedback_clock" value="Global Clock" />+ <parameter name="gui_number_of_clocks" value="#{length kdOuts}" />+ <parameter name="gui_operation_mode" value="direct" />+ #{clkOuts}+ <parameter name="gui_pll_mode" value="Integer-N PLL" />+ <parameter name="gui_reference_clock_frequency" value="#{cklFreq kdIn}" />+ <parameter name="gui_use_locked" value="true" />+ </module>+</system>|]
+ src/Clash/Primitives/Sized/ToInteger.hs view
@@ -0,0 +1,109 @@+{-# LANGUAGE TupleSections #-}+module Clash.Primitives.Sized.ToInteger+ ( bvToIntegerVerilog+ , bvToIntegerVHDL+ , indexToIntegerVerilog+ , indexToIntegerVHDL+ , signedToIntegerVerilog+ , signedToIntegerVHDL+ , unsignedToIntegerVerilog+ , unsignedToIntegerVHDL+ )+where++import qualified Control.Lens as Lens+import Control.Monad (when)+import Control.Monad.IO.Class (liftIO)+import Data.Text.Lazy (pack)+import System.IO (hPutStrLn, stderr)+import Text.Trifecta.Result (Result(Success))++import DynFlags (unsafeGlobalDynFlags)+import ErrUtils (mkPlainWarnMsg, pprLocErrMsg)+import Outputable+ (blankLine, empty, int, integer, showSDocUnsafe, text, ($$), ($+$), (<+>))+import qualified Outputable+import SrcLoc (isGoodSrcSpan)++import Clash.Annotations.Primitive (HDL (Verilog,VHDL))+import Clash.Core.Type (Type (LitTy), LitTy (NumTy))+import Clash.Netlist.BlackBox.Parser (runParse)+import Clash.Netlist.BlackBox.Types+ (BlackBoxFunction, BlackBoxMeta (bbKind), TemplateKind (TExpr),+ emptyBlackBoxMeta)+import Clash.Netlist.Types+ (BlackBox (BBTemplate), HWType (..), curCompNm, intWidth)+import Clash.Util (clogBase)++bvToIntegerVerilog, bvToIntegerVHDL, indexToIntegerVerilog,+ indexToIntegerVHDL, signedToIntegerVerilog, signedToIntegerVHDL,+ unsignedToIntegerVerilog, unsignedToIntegerVHDL :: BlackBoxFunction++bvToIntegerVerilog = toIntegerBB Verilog (BitVector 0)+bvToIntegerVHDL = toIntegerBB VHDL (BitVector 0)+indexToIntegerVerilog = toIntegerBB Verilog (Index 0)+indexToIntegerVHDL = toIntegerBB VHDL (Index 0)+signedToIntegerVerilog = toIntegerBB Verilog (Signed 0)+signedToIntegerVHDL = toIntegerBB VHDL (Signed 0)+unsignedToIntegerVerilog = toIntegerBB Verilog (Unsigned 0)+unsignedToIntegerVHDL = toIntegerBB VHDL (Unsigned 0)++toIntegerBB :: HDL -> HWType -> BlackBoxFunction+toIntegerBB hdl hty _isD _primName args _ty = do+ case args of+ (Right (LitTy (NumTy i)):_) -> do+ iw <- Lens.use intWidth+ let i1 = width i+ when (fromInteger i1 > iw) $ do+ (_,sp) <- Lens.use curCompNm+ let srcInfo1 | isGoodSrcSpan sp = srcInfo+ | otherwise = empty++ warnMsg1 = mkPlainWarnMsg unsafeGlobalDynFlags sp (warnMsg i1 iw $+$ blankLine $+$ srcInfo1)++ liftIO (hPutStrLn stderr (showSDocUnsafe (pprLocErrMsg warnMsg1)))+ _ -> return ()+ return ((meta,) <$> bb)+ where+ meta = emptyBlackBoxMeta{bbKind=TExpr}++ bb = BBTemplate <$> case runParse (pack bbText) of+ Success t -> Right t+ _ -> Left "internal error: parse fail"++ bbText = case hdl of+ VHDL -> case hty of+ BitVector {} -> "~IF~SIZE[~TYP[1]]~THENsigned(std_logic_vector(resize(unsigned(~ARG[1]),~SIZE[~TYPO])))~ELSEto_signed(0,64)~FI"+ Index {} -> "~IF~SIZE[~TYP[0]]~THENsigned(std_logic_vector(resize(~ARG[0],~SIZE[~TYPO])))~ELSEto_signed(0,64)~FI"+ Signed {} -> "~IF~SIZE[~TYP[0]]~THENresize(~ARG[0],~SIZE[~TYPO])~ELSEto_signed(0,64)~FI"+ Unsigned {} -> "~IF~SIZE[~TYP[0]]~THENsigned(std_logic_vector(resize(~ARG[0],~SIZE[~TYPO])))~ELSEto_signed(0,64)~FI"+ _ -> error "internal error"+ _ -> case hty of+ BitVector {} -> "~IF~SIZE[~TYP[1]]~THEN~IF~CMPLE[~SIZE[~TYPO]][~SIZE[~TYP[1]]]~THEN$unsigned(~VAR[bv][1][0+:~SIZE[~TYPO]])~ELSE$unsigned({{(~SIZE[~TYPO]-~SIZE[~TYP[1]]) {1'b0}},~VAR[bv][1]})~FI~ELSE~SIZE[~TYPO]'sd0~FI"+ Index {} -> "~IF~SIZE[~TYP[0]]~THEN~IF~CMPLE[~SIZE[~TYPO]][~SIZE[~TYP[0]]]~THEN$unsigned(~VAR[i][0][0+:~SIZE[~TYPO]])~ELSE$unsigned({{(~SIZE[~TYPO]-~SIZE[~TYP[0]]) {1'b0}},~VAR[i][0]})~FI~ELSE~SIZE[~TYPO]'sd0~FI"+ Signed {} -> "~IF~SIZE[~TYP[0]]~THEN~IF~CMPLE[~SIZE[~TYPO]][~SIZE[~TYP[0]]]~THEN$signed(~VAR[i][0][0+:~SIZE[~TYPO]])~ELSE$signed({{(~SIZE[~TYPO]-~SIZE[~TYP[0]]) {1'b0}},~VAR[i][0]})~FI~ELSE~SIZE[~TYPO]'sd0~FI"+ Unsigned {} -> "~IF~SIZE[~TYP[0]]~THEN~IF~CMPLE[~SIZE[~TYPO]][~SIZE[~TYP[0]]]~THEN$unsigned(~VAR[i][0][0+:~SIZE[~TYPO]])~ELSE$unsigned({{(~SIZE[~TYPO]-~SIZE[~TYP[0]]) {1'b0}},~VAR[i][0]})~FI~ELSE~SIZE[~TYPO]'sd0~FI"+ _ -> error "internal error"++ tyName = case hty of+ BitVector {} -> text "BitVector"+ Index {} -> text "Index"+ Signed {} -> text "Signed"+ Unsigned {} -> text "Unsigned"+ _ -> error "internal error"++ width i = case hty of+ Index {} -> maybe 0 toInteger (clogBase 2 i)+ _ -> i++ warnMsg i iw =+ tyName Outputable.<> text ".toInteger: Integer width," <+> int iw Outputable.<>+ text ", is smaller than" <+> tyName <+> text "width," <+> integer i Outputable.<>+ text ". Dropping MSBs." $+$+ text "Are you using 'fromIntegral' to convert between types?" <+>+ text "Use 'bitCoerce' instead."++ srcInfo =+ text "NB: The source location of the error is not exact, only indicative, as it is acquired after optimisations." $$+ text "The actual location of the error can be in a function that is inlined." $$+ text "To prevent inlining of those functions, annotate them with a NOINLINE pragma."
src/Clash/Primitives/Sized/Vector.hs view
@@ -1,36 +1,197 @@-{-# LANGUAGE CPP #-}+{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE TupleSections #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-} module Clash.Primitives.Sized.Vector where -import Control.Monad.State-import Data.Semigroup.Monad-import Data.Text.Lazy (pack)-import Data.Text.Prettyprint.Doc.Extra-import Text.Trifecta.Result+import Control.Monad.State (State, zipWithM)+import qualified Control.Lens as Lens+import Data.Either (rights)+import qualified Data.IntMap as IntMap+import Data.Semigroup.Monad (getMon)+import qualified Data.Text as Text+import qualified Data.Text.Lazy as LText+import Data.Text.Lazy (pack)+import Data.Text.Prettyprint.Doc.Extra+ (Doc, string, renderLazy, layoutPretty, LayoutOptions(..),+ PageWidth(AvailablePerLine))+import Text.Trifecta.Result (Result(Success))+import qualified Data.String.Interpolate as I+import qualified Data.String.Interpolate.Util as I -import Clash.Backend-import Clash.Netlist.BlackBox-import Clash.Netlist.BlackBox.Parser-import Clash.Netlist.BlackBox.Types-import Clash.Netlist.Types-import Clash.Netlist.Util+import Clash.Backend+ (Backend, hdlTypeErrValue, expr, mkUniqueIdentifier, blockDecl)+import Clash.Core.Type+ (Type(LitTy), LitTy(NumTy), coreView)+import Clash.Netlist.BlackBox (isLiteral)+import Clash.Netlist.BlackBox.Util (renderElem)+import Clash.Netlist.BlackBox.Parser (runParse)+import Clash.Netlist.BlackBox.Types+ (BlackBoxFunction, BlackBoxMeta(..), TemplateKind(TExpr, TDecl),+ Element(Component, Typ, TypElem, Text), Decl(Decl), emptyBlackBoxMeta)+import Clash.Netlist.Types+ (Identifier, TemplateFunction, BlackBoxContext, HWType(Vector),+ Declaration(..), Expr(BlackBoxE, Literal, Identifier), Literal(NumLit),+ BlackBox(BBTemplate, BBFunction), TemplateFunction(..), WireOrReg(Wire),+ Modifier(Indexed, Nested), bbInputs, bbResult, emptyBBContext, tcCache,+ bbFunctions)+import Clash.Netlist.Id (IdType(Basic))+import Clash.Netlist.Util (typeSize) -import qualified Data.String.Interpolate as I-import qualified Data.String.Interpolate.Util as I+import Clash.Util (HasCallStack, curLoc) +data FCall =+ FCall+ Identifier -- left+ Identifier -- right+ Identifier -- result++-- | Calculates the number of function calls needed for an evaluation of+-- 'Clash.Sized.Vector.fold', given the length of the vector given to fold.+foldFunctionPlurality :: HasCallStack => Int -> Int+foldFunctionPlurality 1 = 0+foldFunctionPlurality 2 = 1+foldFunctionPlurality n+ | n <= 0 = error $ "functionPlurality: unexpected n: " ++ show n+ | otherwise =+ let (d, r) = n `divMod` 2 in+ 1 + foldFunctionPlurality d + foldFunctionPlurality (d+r)++foldBBF :: HasCallStack => BlackBoxFunction+foldBBF _isD _primName args _resTy = do+ tcm <- Lens.use tcCache+ pure (Right (meta tcm, bb))+ where+ bb = BBFunction "Clash.Primitives.Sized.Vector.foldTF" 0 foldTF+ [_, vecLengthMinusOne] = rights args+ vecLength tcm =+ case coreView tcm vecLengthMinusOne of+ (LitTy (NumTy n)) -> n + 1+ vl -> error $ "Unexpected vector length: " ++ show vl+ funcPlural tcm = foldFunctionPlurality (fromInteger (vecLength tcm))+ meta tcm = emptyBlackBoxMeta {bbKind=TDecl, bbFunctionPlurality=[(0, funcPlural tcm)]}++-- | Type signature of function we're generating netlist for:+--+-- fold :: (a -> a -> a) -> Vec (n + 1) a -> a+--+-- The implementation promises to create a (balanced) tree structure.+foldTF :: TemplateFunction+foldTF = TemplateFunction [] (const True) foldTF'++foldTF' :: forall s . (HasCallStack, Backend s) => BlackBoxContext -> State s Doc+foldTF' bbCtx@(bbInputs -> [_f, (vec, vecType@(Vector n aTy), _isLiteral)]) = do+ -- Create an id for every element in the vector+ vecIds <- mapM (\i -> mkId ("acc_0_" <> show i)) [0..n-1]++ vecId <- mkId "vec"+ let vecDecl = sigDecl vecType Wire vecId+ vecAssign = Assignment vecId vec+ elemAssigns = zipWith Assignment vecIds (map (iIndex vecId) [0..])+ resultId =+ case bbResult bbCtx of+ (Identifier t _, _) -> t+ _ -> error "Unexpected result identifier"++ -- Create a list of function calls to be made (creates identifiers for+ -- intermediate result signals)+ (concat -> fCalls, result) <- mkTree 1 vecIds++ let intermediateResultIds = concatMap (\(FCall l r _) -> [l, r]) fCalls+ wr = case IntMap.lookup 0 (bbFunctions bbCtx) of+ Just ((_,rw,_,_,_,_):_) -> rw+ _ -> error "internal error"+ sigDecls = zipWith (sigDecl aTy) (wr:replicate n Wire ++ repeat wr)+ (result : intermediateResultIds)+ resultAssign = Assignment resultId (Identifier result Nothing)++ callDecls <- zipWithM callDecl [0..] fCalls+ foldNm <- mkId "fold"++ getMon $ blockDecl foldNm $+ resultAssign :+ vecAssign :+ vecDecl :+ elemAssigns +++ sigDecls +++ callDecls++ where+ mkId :: String -> State s Identifier+ mkId = mkUniqueIdentifier Basic . Text.pack++ callDecl :: Int -> FCall -> State s Declaration+ callDecl fSubPos (FCall a b r) = do+ rendered0 <- string =<< (renderElem bbCtx call <*> pure 0)+ let layout = LayoutOptions (AvailablePerLine 120 0.4)+ rendered1 = renderLazy (layoutPretty layout rendered0)+ pure (+ BlackBoxD+ "__FOLD_BB_INTERNAL__"+ [] [] []+ (BBTemplate [Text rendered1])+ (emptyBBContext "__FOLD_BB_INTERNAL__")+ )+ where+ call = Component (Decl fPos fSubPos (resEl:aEl:[bEl]))+ elTyp = [TypElem (Typ (Just vecPos))]+ resEl = ([Text (LText.fromStrict r)], elTyp)+ aEl = ([Text (LText.fromStrict a)], elTyp)+ bEl = ([Text (LText.fromStrict b)], elTyp)++ -- Argument no. of function+ fPos = 0++ -- Argument no. of vector+ vecPos = 1++ -- Create the whole tree+ mkTree+ :: Int+ -- ^ Current level+ -> [Identifier]+ -- ^ Elements left to process+ -> State s ( [[FCall]] -- function calls to be rendered+ , Identifier -- result signal+ )+ mkTree _lvl [] = error "Unreachable?"+ mkTree _lvl [res] = pure ([], res)+ mkTree lvl results0 = do+ (calls0, results1) <- mkLevel (lvl, 0) results0+ (calls1, result) <- mkTree (lvl+1) results1+ pure (calls0 : calls1, result)++ -- Create a single layer of a tree+ mkLevel+ :: (Int, Int)+ -- ^ (level, offset)+ -> [Identifier]+ -> State s ([FCall], [Identifier])+ mkLevel (!lvl, !offset) (a:b:rest) = do+ c <- mkId ("acc_" <> show lvl <> "_" <> show offset)+ (calls, results) <- mkLevel (lvl, offset+1) rest+ pure (FCall a b c:calls, c:results)+ mkLevel _lvl rest =+ pure ([], rest)++ -- Simple wire without comment+ sigDecl :: HWType -> WireOrReg -> Identifier -> Declaration+ sigDecl typ rw nm = NetDecl' Nothing rw nm (Right typ) Nothing++ -- Index the intermediate vector. This uses a hack in Clash: the 10th+ -- constructor of Vec doesn't exist; using it will be interpreted by the+ -- HDL backends as vector indexing.+ iIndex :: Identifier -> Int -> Expr+ iIndex vecId i = Identifier vecId (Just (Indexed (vecType, 10, i)))++foldTF' args =+ error $ "Unexpected number of arguments: " ++ show (length (bbInputs args))+ indexIntVerilog :: BlackBoxFunction indexIntVerilog _isD _primName args _ty = return ((meta,) <$> bb) where- meta = BlackBoxMeta- { bbOutputReg = False- , bbKind = bbKi- , bbLibrary = []- , bbImports = []- , bbIncludes = []- }+ meta = emptyBlackBoxMeta{bbKind=bbKi} bbKi = case args of [_nTy,_aTy,_kn,_v,Left ix]@@ -74,7 +235,7 @@ Just m -> expr False (Identifier i (Just (Nested m (Indexed (vTy,10,ixI ix))))) _ -> expr False (Identifier i (Just (Indexed (vTy,10,ixI ix))))- _ -> error ("Expected Identifier: " ++ show vec)+ _ -> error ($(curLoc) ++ "Expected Identifier: " ++ show vec) where [ _kn , (vec, vTy, _)@@ -85,8 +246,10 @@ ixI :: Expr -> Int ixI ix0 = case ix0 of- Literal _ (NumLit i) -> fromInteger i- BlackBoxE "GHC.Types.I#" _ _ _ _ ixCtx _ ->- let (ix1,_,_) = head (bbInputs ixCtx)- in ixI ix1- _ -> error ("Unexpected literal" ++ show ix)+ Literal _ (NumLit i) ->+ fromInteger i+ BlackBoxE "GHC.Types.I#" _ _ _ _ ixCtx _ ->+ let (ix1,_,_) = head (bbInputs ixCtx)+ in ixI ix1+ _ ->+ error ($(curLoc) ++ "Unexpected literal: " ++ show ix)
src/Clash/Primitives/Types.hs view
@@ -8,15 +8,11 @@ Type and instance definitions for Primitive -} -{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE DeriveFoldable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ViewPatterns #-} module Clash.Primitives.Types ( TemplateSource(..)@@ -24,6 +20,7 @@ , TemplateFormat(..) , BlackBoxFunctionName(..) , Primitive(..)+ , UsedArguments(..) , GuardedCompiledPrimitive , GuardedResolvedPrimitive , PrimMap@@ -38,7 +35,7 @@ import Clash.Annotations.Primitive (PrimitiveGuard) import Clash.Core.Term (WorkInfo (..)) import Clash.Netlist.BlackBox.Types- (BlackBoxFunction, BlackBoxTemplate, TemplateKind (..))+ (BlackBoxFunction, BlackBoxTemplate, TemplateKind (..), RenderVoid(..)) import Control.Applicative ((<|>)) import Control.DeepSeq (NFData) import Data.Aeson@@ -133,6 +130,14 @@ | THaskell deriving (Show, Generic, Hashable, NFData) +-- | Data type to indicate what arguments are in use by a BlackBox+data UsedArguments+ = UsedArguments [Int]+ -- ^ Only these are used+ | IgnoredArguments [Int]+ -- ^ All but these are used+ deriving (Show, Generic, Hashable, NFData, Binary)+ -- | Externally defined primitive data Primitive a b c d -- | Primitive template written in a Clash specific templating language@@ -141,6 +146,9 @@ -- ^ Name of the primitive , workInfo :: WorkInfo -- ^ Whether the primitive does any work, i.e. takes chip area+ , renderVoid :: RenderVoid+ -- ^ Whether this primitive should be rendered when its result type is+ -- void. Defaults to 'NoRenderVoid'. , kind :: TemplateKind -- ^ Whether this results in an expression or a declaration , warning :: c@@ -155,10 +163,31 @@ -- ^ VHDL only: add /library/ declarations for the given names , imports :: [a] -- ^ VHDL only: add /use/ declarations for the given names+ , functionPlurality :: [(Int, Int)] -- Using map ruins Hashable instance+ -- ^ Indicates how often a function will be instantiated in a blackbox. For+ -- example, consider the following higher-order function that creates a tree+ -- structure:+ --+ -- fold :: (a -> a -> a) -> Vec n a -> a+ --+ -- In order to generate HDL for an instance of fold we need log2(n) calls+ -- to the first argument, `a -> a -> a` (plus a few more if n is not a+ -- power of two). Note that this only targets multiple textual instances+ -- of the function. If you can generate the HDL using a for-loop and only+ -- need to call ~INST once, you don't have to worry about this option. See+ -- the blackbox for 'Clash.Sized.Vector.map' for an example of this.+ --+ -- Right now, option can only be generated by BlackBoxHaskell. It cannot be+ -- used within JSON primitives. To see how to use this, see the Haskell+ -- blackbox for 'Clash.Sized.Vector.fold'. , includes :: [((S.Text,S.Text),b)] -- ^ Create files to be included with the generated primitive. The fields -- are ((name, extension), content), where content is a template of the file -- Defaults to @[]@ when not specified in the /.json/ file+ , resultName :: Maybe b+ -- ^ (Maybe) Control the generated name of the result+ , resultInit :: Maybe b+ -- ^ (Maybe) Control the initial/power-up value of the result , template :: b -- ^ Used to indiciate type of template (declaration or expression). Will be -- filled with @Template@ or an @Either decl expr@.@@ -169,6 +198,8 @@ -- ^ Name of the primitive , workInfo :: WorkInfo -- ^ Whether the primitive does any work, i.e. takes chip area+ , usedArguments :: UsedArguments+ -- ^ Arguments used by blackbox. Used to remove arguments during normalization. , functionName :: BlackBoxFunctionName , function :: d -- ^ Holds blackbox function and its hash, (Int, BlackBoxFunction), in a@@ -193,6 +224,16 @@ [(conKey,Object conVal)] -> case conKey of "BlackBoxHaskell" -> do+ usedArguments <- conVal .:? "usedArguments"+ ignoredArguments <- conVal .:? "ignoredArguments"+ args <-+ case (usedArguments, ignoredArguments) of+ (Nothing, Nothing) -> pure (IgnoredArguments [])+ (Just a, Nothing) -> pure (UsedArguments a)+ (Nothing, Just a) -> pure (IgnoredArguments a)+ (Just _, Just _) ->+ fail "[8] Don't use both 'usedArguments' and 'ignoredArguments'"+ name' <- conVal .: "name" wf <- ((conVal .:? "workInfo" >>= maybe (pure Nothing) parseWorkInfo) .!= WorkVariable) fName <- conVal .: "templateFunction"@@ -200,16 +241,20 @@ <|> (Just . TFile <$> conVal .: "file") <|> (pure Nothing) fName' <- either fail return (parseBBFN fName)- return (BlackBoxHaskell name' wf fName' templ)+ return (BlackBoxHaskell name' wf args fName' templ) "BlackBox" -> BlackBox <$> conVal .: "name" <*> (conVal .:? "workInfo" >>= maybe (pure Nothing) parseWorkInfo) .!= WorkVariable+ <*> conVal .:? "renderVoid" .!= NoRenderVoid <*> (conVal .: "kind" >>= parseTemplateKind) <*> conVal .:? "warning" <*> conVal .:? "outputReg" .!= False <*> conVal .:? "libraries" .!= [] <*> conVal .:? "imports" .!= []+ <*> pure [] -- functionPlurality not supported in json <*> (conVal .:? "includes" .!= [] >>= traverse parseInclude)+ <*> (conVal .:? "resultName" >>= maybe (pure Nothing) parseResult) .!= Nothing+ <*> (conVal .:? "resultInit" >>= maybe (pure Nothing) parseResult) .!= Nothing <*> parseTemplate conVal "Primitive" -> Primitive <$> conVal .: "name"@@ -247,6 +292,10 @@ parseBBFN' = either fail return . parseBBFN defTemplateFunction = BlackBoxFunctionName ["Template"] "template"++ parseResult (Object c) =+ Just . Just <$> parseTemplate c+ parseResult e = fail $ "[7] unexpected result: " ++ show e parseJSON unexpected = fail $ "[3] Expected: BlackBox or Primitive object, got: " ++ show unexpected
src/Clash/Primitives/Util.hs view
@@ -8,16 +8,16 @@ Utility functions to generate Primitives -} -{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE TupleSections #-}+{-# LANGUAGE RecordWildCards #-} module Clash.Primitives.Util ( generatePrimMap , hashCompiledPrimMap , constantArgs , decodeOrErr+ , getFunctionPlurality ) where import Control.DeepSeq (force)@@ -28,7 +28,8 @@ import qualified Data.HashMap.Strict as HashMapStrict import qualified Data.Set as Set import Data.Hashable (hash)-import Data.List (isSuffixOf, sort)+import Data.List (isSuffixOf, sort, find)+import Data.Maybe (fromMaybe) import qualified Data.Text as TS import Data.Text.Lazy (Text) import qualified Data.Text.Lazy.IO as T@@ -40,15 +41,18 @@ import Clash.Annotations.Primitive ( PrimitiveGuard(HasBlackBox, WarnNonSynthesizable, WarnAlways, DontTranslate) , extractPrim)+import Clash.Core.Term (Term)+import Clash.Core.Type (Type) import Clash.Primitives.Types ( Primitive(BlackBox), CompiledPrimitive, ResolvedPrimitive, ResolvedPrimMap , includes, template, TemplateSource(TFile, TInline), Primitive(..) , UnresolvedPrimitive, CompiledPrimMap, GuardedResolvedPrimitive)-import Clash.Netlist.Types (BlackBox(..))+import Clash.Netlist.Types (BlackBox(..), NetlistMonad)+import Clash.Netlist.Util (preserveState) import Clash.Netlist.BlackBox.Util (walkElement) import Clash.Netlist.BlackBox.Types- (Element(Const, Lit))+ (Element(Const, Lit), BlackBoxMeta(..)) hashCompiledPrimitive :: CompiledPrimitive -> Int hashCompiledPrimitive (Primitive {name, primSort}) = hash (name, primSort)@@ -89,15 +93,19 @@ -> IO (TS.Text, GuardedResolvedPrimitive) resolvePrimitive' _metaPath (Primitive name wf primType) = return (name, HasBlackBox (Primitive name wf primType))-resolvePrimitive' metaPath BlackBox{template=t, includes=i, ..} = do- let resolvedIncludes = mapM (traverse (traverse (traverse (resolveTemplateSource metaPath)))) i- resolved = traverse (traverse (resolveTemplateSource metaPath)) t- bb <- BlackBox name workInfo kind () outputReg libraries imports <$> resolvedIncludes <*> resolved+resolvePrimitive' metaPath BlackBox{template=t, includes=i, resultName=r, resultInit=ri, ..} = do+ let resolveSourceM = traverse (traverse (resolveTemplateSource metaPath))+ bb <- BlackBox name workInfo renderVoid kind () outputReg libraries imports functionPlurality+ <$> mapM (traverse resolveSourceM) i+ <*> traverse resolveSourceM r+ <*> traverse resolveSourceM ri+ <*> resolveSourceM t case warning of Just w -> pure (name, WarnNonSynthesizable (TS.unpack w) bb) Nothing -> pure (name, HasBlackBox bb)-resolvePrimitive' metaPath (BlackBoxHaskell bbName wf funcName t) =- (bbName,) . HasBlackBox . BlackBoxHaskell bbName wf funcName <$> (mapM (resolveTemplateSource metaPath) t)+resolvePrimitive' metaPath (BlackBoxHaskell bbName wf usedArgs funcName t) =+ (bbName,) . HasBlackBox . BlackBoxHaskell bbName wf usedArgs funcName <$>+ (mapM (resolveTemplateSource metaPath) t) -- | Interprets contents of json file as list of @Primitive@s. Throws -- exception if it fails.@@ -169,12 +177,19 @@ primitives1 <- sequence $ zipWith resolvePrimitive' metapaths unresolvedPrims let primMap = HashMap.fromList (primitives0 ++ primitives1) return (force (addGuards primMap primGuards))+{-# SCC generatePrimMap #-} -- | Determine what argument should be constant / literal constantArgs :: TS.Text -> CompiledPrimitive -> Set.Set Int-constantArgs nm BlackBox {template = BBTemplate template} =- Set.fromList (fromIntForce ++ concatMap (walkElement getConstant) template)+constantArgs nm BlackBox {template = templ@(BBTemplate _), resultInit = tRIM} =+ Set.fromList (concat [ fromIntForce+ , maybe [] walkTemplate tRIM+ , walkTemplate templ+ ]) where+ walkTemplate (BBTemplate t) = concatMap (walkElement getConstant) t+ walkTemplate _ = []+ getConstant (Lit i) = Just i getConstant (Const i) = Just i getConstant _ = Nothing@@ -205,3 +220,37 @@ | nm == "Clash.Sized.Vector.replace_int" = [1,2] | otherwise = [] constantArgs _ _ = Set.empty++-- | Helper function of 'getFunctionPlurality'+getFunctionPlurality' :: [(Int, Int)] -> Int -> Int+getFunctionPlurality' functionPlurality n =+ fromMaybe 1 (snd <$> (find ((== n) . fst) functionPlurality))++-- | Looks up the plurality of a function's function argument. See+-- 'functionPlurality' for more information. If not set, the returned plurality+-- will default to /1/.+getFunctionPlurality+ :: HasCallStack+ => CompiledPrimitive+ -> [Either Term Type]+ -- ^ Arguments passed to blackbox+ -> Type+ -- ^ Result type+ -> Int+ -- ^ Argument number holding the function of interest+ -> NetlistMonad Int+ -- ^ Plurality of function. Defaults to 1. Does not err if argument isn't+ -- a function in the first place. State of monad will not be modified.+getFunctionPlurality (Primitive {}) _args _resTy _n = pure 1+getFunctionPlurality (BlackBoxHaskell {name, function, functionName}) args resTy n = do+ errOrMeta <- preserveState ((snd function) False name args resTy)+ case errOrMeta of+ Left err ->+ error $ concat [ "Tried to determine function plurality for "+ , TS.unpack name, " by quering ", show functionName+ , ". Function returned an error message instead:\n\n"+ , err ]+ Right (BlackBoxMeta {bbFunctionPlurality}, _bb) ->+ pure (getFunctionPlurality' bbFunctionPlurality n)+getFunctionPlurality (BlackBox {functionPlurality}) _args _resTy n =+ pure (getFunctionPlurality' functionPlurality n)
src/Clash/Rewrite/Combinators.hs view
@@ -6,10 +6,6 @@ Rewriting combinators and traversals -} -{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE ViewPatterns #-}- module Clash.Rewrite.Combinators ( allR , (!->)
src/Clash/Rewrite/Types.hs view
@@ -8,13 +8,12 @@ Type and instance definitions for Rewrite modules -} -{-# LANGUAGE CPP #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TemplateHaskell #-} module Clash.Rewrite.Types where @@ -33,9 +32,10 @@ import Data.Hashable (Hashable) import Data.IntMap.Strict (IntMap) import Data.Monoid (Any)+import qualified Data.Set as Set import GHC.Generics -import Clash.Core.Evaluator (GlobalHeap, PrimEvaluator)+import Clash.Core.Evaluator.Types (PrimHeap, PrimStep, PrimUnwind) import Clash.Core.Term (Term, Context) import Clash.Core.Type (Type) import Clash.Core.TyCon (TyConName, TyConMap)@@ -75,7 +75,7 @@ -- ^ Function which is currently normalized , _nameCounter :: {-# UNPACK #-} !Int -- ^ Used for 'Fresh'- , _globalHeap :: GlobalHeap+ , _globalHeap :: PrimHeap -- ^ Used as a heap for compile-time evaluation of primitives that live in I/O , _extra :: !extra -- ^ Additional state@@ -87,7 +87,10 @@ data RewriteEnv = RewriteEnv { _dbgLevel :: DebugLevel- -- ^ Lvl at which we print debugging messages+ -- ^ Level at which we print debugging messages+ , _dbgTransformations :: Set.Set String+ , _aggressiveXOpt :: Bool+ -- ^ Transformations to print debugging info for , _typeTranslator :: CustomReprs -> TyConMap -> Type@@ -97,7 +100,7 @@ -- ^ TyCon cache , _tupleTcCache :: IntMap TyConName -- ^ Tuple TyCon cache- , _evaluator :: PrimEvaluator+ , _evaluator :: (PrimStep, PrimUnwind) -- ^ Hardcoded evaluator (delta-reduction)} , _topEntities :: VarSet -- ^ Functions that are considered TopEntities
src/Clash/Rewrite/Util.hs view
@@ -8,24 +8,22 @@ Utilities for rewriting: e.g. inlining, specialisation, etc. -} -{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE NondecreasingIndentation #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE Rank2Types #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE ViewPatterns #-}--{-# OPTIONS_GHC -Wno-unused-imports #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TemplateHaskell #-} module Clash.Rewrite.Util where +import Control.Concurrent.Supply (splitSupply) import Control.DeepSeq import Control.Exception (throw) import Control.Lens- (Lens', (%=), (+=), (^.), _3, _4, _Left)+ (Lens', (%=), (+=), (^.), _Left) import qualified Control.Lens as Lens import qualified Control.Monad as Monad #if !MIN_VERSION_base(4,13,0)@@ -33,15 +31,18 @@ #endif import qualified Control.Monad.State.Strict as State import qualified Control.Monad.Writer as Writer+import Data.Bool (bool) import Data.Bifunctor (bimap) import Data.Coerce (coerce) import Data.Functor.Const (Const (..))-import Data.List (group, sort)+import Data.List (group, partition, sort) import qualified Data.Map as Map import Data.Maybe (catMaybes,isJust,mapMaybe) import qualified Data.Monoid as Monoid import qualified Data.Set as Set import qualified Data.Set.Lens as Lens+import qualified Data.Set.Ordered as OSet+import qualified Data.Set.Ordered.Extra as OSet import Data.Text (Text) import qualified Data.Text as Text @@ -55,13 +56,15 @@ import BasicTypes (InlineSpec (..)) import Clash.Core.DataCon (dcExtTyVars)+import Clash.Core.Evaluator (whnf')+import Clash.Core.Evaluator.Types (PureHeap) import Clash.Core.FreeVars (freeLocalVars, hasLocalFreeVars, localIdDoesNotOccurIn, localIdOccursIn,- typeFreeVars, termFreeVars', freeIds)+ typeFreeVars, termFreeVars') import Clash.Core.Name import Clash.Core.Pretty (showPpr) import Clash.Core.Subst- (aeqTerm, aeqType, extendIdSubst, mkSubst, substTm)+ (substTmEnv, aeqTerm, aeqType, extendIdSubst, mkSubst, substTm) import Clash.Core.Term (LetBinding, Pat (..), Term (..), CoreContext (..), Context, PrimInfo (..), TmName, WorkInfo (..), TickInfo, collectArgs, collectArgsTicks)@@ -73,18 +76,21 @@ typeKind, tyView, isPolyFunTy) import Clash.Core.Util (isPolyFun, mkAbstraction, mkApps, mkLams, mkTicks,- mkTmApps, mkTyApps, mkTyLams, termType, dataConInstArgTysE, isClockOrReset)+ mkTmApps, mkTyApps, mkTyLams, termType, dataConInstArgTysE, isClockOrReset,+ isEnable, piResultTy) import Clash.Core.Var (Id, IdScope (..), TyVar, Var (..), isLocalId, mkGlobalId, mkLocalId, mkTyVar) import Clash.Core.VarEnv (InScopeSet, VarEnv, elemVarSet, extendInScopeSetList, mkInScopeSet,- notElemVarEnv, uniqAway, uniqAway')+ uniqAway, uniqAway', mapVarEnv) import Clash.Driver.Types- (DebugLevel (..), BindingMap)+ (DebugLevel (..), BindingMap, Binding(..)) import Clash.Netlist.Util (representableType)+import Clash.Pretty (clashPretty, showDoc) import Clash.Rewrite.Types import Clash.Unique import Clash.Util+import qualified Clash.Util.Interpolate as I -- | Lift an action working in the '_extra' state to the 'RewriteMonad' zoomExtra :: State.State extra a@@ -140,6 +146,11 @@ -> Rewrite extra apply = \s rewrite ctx expr0 -> do lvl <- Lens.view dbgLevel+ dbgTranss <- Lens.view dbgTransformations+ let isTryLvl = lvl == DebugTry || lvl >= DebugAll+ isRelevantTrans = s `Set.member` dbgTranss || Set.null dbgTranss+ traceIf (isTryLvl && isRelevantTrans) ("Trying: " ++ s) (pure ())+ (expr1,anyChanged) <- Writer.listen (rewrite ctx expr0) let hasChanged = Monoid.getAny anyChanged !expr2 = if hasChanged then expr1 else expr0@@ -160,14 +171,17 @@ } return () #endif+ if lvl == DebugNone then return expr2- else applyDebug lvl s expr0 hasChanged expr2+ else applyDebug lvl dbgTranss s expr0 hasChanged expr2 {-# INLINE apply #-} applyDebug :: DebugLevel -- ^ The current debugging level+ -> Set.Set String+ -- ^ Transformations to debug -> String -- ^ Name of the transformation -> Term@@ -177,8 +191,13 @@ -> Term -- ^ New expression -> RewriteMonad extra Term-applyDebug lvl name exprOld hasChanged exprNew =- traceIf (lvl >= DebugAll) ("Trying: " ++ name ++ " on:\n" ++ before) $ do+applyDebug lvl transformations name exprOld hasChanged exprNew+ | not (Set.null transformations) =+ let newLvl = bool DebugNone lvl (name `Set.member` transformations) in+ applyDebug newLvl Set.empty name exprOld hasChanged exprNew++applyDebug lvl _transformations name exprOld hasChanged exprNew =+ traceIf (lvl >= DebugAll) ("Tried: " ++ name ++ " on:\n" ++ before) $ do Monad.when (lvl > DebugNone && hasChanged) $ do tcm <- Lens.view tcCache let beforeTy = termType tcm exprOld@@ -210,7 +229,7 @@ , "substitution." ]) - traceIf (lvl >= DebugAll && (beforeTy `aeqType` afterTy))+ traceIf (lvl >= DebugApplied && (not (beforeTy `aeqType` afterTy))) ( concat [ $(curLoc) , "Error when applying rewrite ", name , " to:\n" , before@@ -251,10 +270,10 @@ -> RewriteState extra -> RewriteMonad extra a -> a-runRewriteSession r s m = traceIf True ("Clash: Applied " ++- show (s' ^. transformCounter) ++- " transformations")- a+runRewriteSession r s m =+ traceIf (_dbgLevel r > DebugNone)+ ("Clash: Applied " ++ show (s' ^. transformCounter) ++ " transformations")+ a where (a,s',_) = runR m r s @@ -281,7 +300,7 @@ -- | Make a new binder and variable reference for a term mkTmBinderFor- :: (Monad m, MonadUnique m, MonadFail m)+ :: (MonadUnique m, MonadFail m) => InScopeSet -> TyConMap -- ^ TyCon cache -> Name a -- ^ Name of the new binder@@ -293,7 +312,7 @@ -- | Make a new binder and variable reference for either a term or a type mkBinderFor- :: (Monad m, MonadUnique m, MonadFail m)+ :: (MonadUnique m, MonadFail m) => InScopeSet -> TyConMap -- ^ TyCon cache -> Name a -- ^ Name of the new binder@@ -311,7 +330,7 @@ -- | Make a new, unique, identifier mkInternalVar- :: (Monad m, MonadUnique m)+ :: (MonadUnique m) => InScopeSet -> OccName -- ^ Name of the identifier@@ -328,17 +347,16 @@ -- ^ Property test -> Rewrite extra inlineBinders condition (TransformContext inScope0 _) expr@(Letrec xes res) = do- (replace,others) <- partitionM (condition expr) xes- case replace of+ (toInline,toKeep) <- partitionM (condition expr) xes+ case toInline of [] -> return expr _ -> do let inScope1 = extendInScopeSetList inScope0 (map fst xes)- (others',res') = substituteBinders inScope1 replace others res- newExpr = case others' of- [] -> res'- _ -> Letrec others' res'-- changed newExpr+ (toInlRec,(toKeep1,res1)) =+ substituteBinders inScope1 toInline toKeep res+ case toInlRec ++ toKeep1 of+ [] -> changed res1+ xes1 -> changed (Letrec xes1 res1) inlineBinders _ _ e = return e @@ -400,9 +418,8 @@ bndr `localIdDoesNotOccurIn` e isVoidWrapper _ = False --- | Substitute the RHS of the first set of Let-binders for references to the--- first set of Let-binders in: the second set of Let-binders and the additional--- term+-- | Inline the first set of binder into the second set of binders and into the+-- body of the original let expression. substituteBinders :: InScopeSet -> [LetBinding]@@ -410,21 +427,74 @@ -> [LetBinding] -- ^ Let-binders where substitution takes place -> Term- -- ^ Expression where substitution takes place- -> ([LetBinding],Term)-substituteBinders _ [] others res = (others,res)-substituteBinders inScope ((bndr,val):rest) others res =- substituteBinders inScope rest' others' res'+ -- ^ Body where substitution takes place+ -> ([LetBinding],([LetBinding],Term))+ -- ^+ -- 1. Let-bindings that we wanted to substitute, but turned out to be recursive+ -- 2.1 Let-binders where substitution took place+ -- 2.2 Body where substitution took place+substituteBinders inScope toInline toKeep body =+ let (subst,toInlRec) = go (mkSubst inScope) [] toInline+ in ( map (second (substTm "substToInlRec" subst)) toInlRec+ , ( map (second (substTm "substToKeep" subst)) toKeep+ , substTm "substBody" subst body) ) where- subst = extendIdSubst (mkSubst inScope) bndr val- selfRef = bndr `localIdOccursIn` val- (res',rest',others') = if selfRef- then (res,rest,(bndr,val):others)- else ( substTm "substituteBindersRes" subst res- , map (second (substTm "substituteBindersRest" subst)) rest- , map (second (substTm "substituteBindersOthers" subst)) others- )+ go subst inlRec [] = (subst,inlRec)+ go !subst !inlRec ((x,e):toInl) =+ let e1 = substTm "substInl" subst e+ substE = extendIdSubst (mkSubst inScope) x e1+ subst1 = subst { substTmEnv = mapVarEnv (substTm "substSubst" substE)+ (substTmEnv subst)}+ subst2 = extendIdSubst subst1 x e1+ in if x `localIdOccursIn` e1 then+ go subst ((x,e1):inlRec) toInl+ else+ go subst2 inlRec toInl +-- | Lift the first set of binders to the level of global bindings, and substitute+-- these lifted bindings into the second set of binders and the body of the+-- original let expression.+liftAndSubsituteBinders+ :: InScopeSet+ -> [LetBinding]+ -- ^ Let-binders to lift, and substitute the lifted result+ -> [LetBinding]+ -- ^ Lef-binders where substitution takes place+ -> Term+ -- ^ Body where substitution takes place+ -> RewriteMonad extra ([LetBinding],Term)+liftAndSubsituteBinders inScope toLift toKeep body = do+ subst <- go (mkSubst inScope) toLift+ pure ( map (second (substTm "liftToKeep" subst)) toKeep+ , substTm "keepBody" subst body+ )+ where+ go subst [] = pure subst+ go !subst ((x,e):inl) = do+ let e1 = substTm "liftInl" subst e+ (_,e2) <- liftBinding (x,e1)+ let substE = extendIdSubst (mkSubst inScope) x e2+ subst1 = subst { substTmEnv = mapVarEnv (substTm "liftSubst" substE)+ (substTmEnv subst) }+ subst2 = extendIdSubst subst1 x e2+ if x `localIdOccursIn` e2 then do+ (_,sp) <- Lens.use curFun+ throw (ClashException sp [I.i|+ Internal error: inlineOrLiftBInders failed on:++ #{showPpr (x,e)}++ creating a self-recursive let-binding:++ #{showPpr (x,e2)}++ given the already built subtitution:++ #{showDoc (clashPretty (substTmEnv subst))}+ |] Nothing)+ else+ go subst2 inl+ -- | Determine whether a term does any work, i.e. adds to the size of the circuit isWorkFree :: Term@@ -433,7 +503,7 @@ Var i -> isLocalId i && not (isPolyFunTy (varType i)) Data {} -> all isWorkFreeArg args Literal {} -> True- Prim _ pInfo -> case primWorkInfo pInfo of+ Prim pInfo -> case primWorkInfo pInfo of WorkConstant -> True -- We can ignore the arguments, because this -- primitive outputs a constant regardless of its -- arguments@@ -463,7 +533,7 @@ isConstant :: Term -> Bool isConstant e = case collectArgs e of (Data _, args) -> all (either isConstant (const True)) args- (Prim _ _, args) -> all (either isConstant (const True)) args+ (Prim _, args) -> all (either isConstant (const True)) args (Lam _ _, _) -> not (hasLocalFreeVars e) (Literal _,_) -> True _ -> False@@ -476,21 +546,23 @@ let eTy = termType tcm e if isClockOrReset tcm eTy then case collectArgs e of- (Prim nm _,_) -> return (nm == "Clash.Transformations.removedArg")+ (Prim p,_) -> return (primName p == "Clash.Transformations.removedArg") _ -> return False else pure (isConstant e) -- TODO: Remove function after using WorkInfo in 'isWorkFreeIsh'-isWorkFreeClockOrReset+isWorkFreeClockOrResetOrEnable :: TyConMap -> Term -> Maybe Bool-isWorkFreeClockOrReset tcm e =+isWorkFreeClockOrResetOrEnable tcm e = let eTy = termType tcm e in- if isClockOrReset tcm eTy then+ if isClockOrReset tcm eTy || isEnable tcm eTy then case collectArgs e of- (Prim nm _,_) -> Just (nm == "Clash.Transformations.removedArg")+ (Prim p,_) -> Just (primName p == "Clash.Transformations.removedArg") (Var _, []) -> Just True+ (Data _, []) -> Just True -- For Enable True/False+ (Literal _,_) -> Just True _ -> Just False else Nothing@@ -512,44 +584,51 @@ -> RewriteMonad extra Bool isWorkFreeIsh e = do tcm <- Lens.view tcCache- case isWorkFreeClockOrReset tcm e of+ case isWorkFreeClockOrResetOrEnable tcm e of Just b -> pure b Nothing -> case collectArgs e of- (Data _, args) -> allM (either isWorkFreeIsh (pure . const True)) args- -- TODO: Use WorkInfo- (Prim _ _, args) -> allM (either isWorkFreeIsh (pure . const True)) args+ (Data _, args) -> allM isWorkFreeIshArg args+ (Prim pInfo, args) -> case primWorkInfo pInfo of+ WorkAlways -> pure False -- Things like clock or reset generator always+ -- perform work+ WorkVariable -> pure (all isConstantArg args)+ _ -> allM isWorkFreeIshArg args (Lam _ _, _) -> pure (not (hasLocalFreeVars e)) (Literal _,_) -> pure True _ -> pure False+ where+ isWorkFreeIshArg = either isWorkFreeIsh (pure . const True)+ isConstantArg = either isConstant (const True) inlineOrLiftBinders :: (LetBinding -> RewriteMonad extra Bool) -- ^ Property test- -> (Term -> LetBinding -> RewriteMonad extra Bool)+ -> (Term -> LetBinding -> Bool) -- ^ Test whether to lift or inline -- -- * True: inline -- * False: lift -> Rewrite extra-inlineOrLiftBinders condition inlineOrLift (TransformContext inScope0 _) expr@(Letrec xes res) = do- (replace,others) <- partitionM condition xes- case replace of- [] -> return expr+inlineOrLiftBinders condition inlineOrLift (TransformContext inScope0 _) e@(Letrec bndrs body) = do+ (toReplace,toKeep) <- partitionM condition bndrs+ case toReplace of+ [] -> return e _ -> do- let inScope1 = extendInScopeSetList inScope0 (map fst xes)- (doInline,doLift) <- partitionM (inlineOrLift expr) replace+ let inScope1 = extendInScopeSetList inScope0 (map fst bndrs)+ let (toInline,toLift) = partition (inlineOrLift e) toReplace -- We first substitute the binders that we can inline both the binders -- that we intend to lift, the other binders, and the body- let (others',res') = substituteBinders inScope1 doInline (doLift ++ others) res- (doLift',others'') = splitAt (length doLift) others'- doLift'' <- mapM liftBinding doLift'+ let (toLiftExtra,(toReplace1,body1)) =+ substituteBinders inScope1 toInline (toLift ++ toKeep) body+ (toLift1,toKeep1) = splitAt (length toLift) toReplace1 -- We then substitute the lifted binders in the other binders and the body- let (others3,res'') = substituteBinders inScope1 doLift'' others'' res'- newExpr = case others3 of- [] -> res''- _ -> Letrec others3 res''- changed newExpr+ (toKeep2,body2) <- liftAndSubsituteBinders inScope1+ (toLiftExtra ++ toLift1)+ toKeep1 body1+ case toKeep2 of+ [] -> changed body2+ _ -> changed (Letrec toKeep2 body2) inlineOrLiftBinders _ _ _ e = return e @@ -600,7 +679,7 @@ newBody = mkTyLams (mkLams e' boundFVs) boundFTVs -- Check if an alpha-equivalent global binder already exists- aeqExisting <- (eltsUniqMap . filterUniqMap ((`aeqTerm` newBody) . (^. _4))) <$> Lens.use bindings+ aeqExisting <- (eltsUniqMap . filterUniqMap ((`aeqTerm` newBody) . bindingTerm)) <$> Lens.use bindings case aeqExisting of -- If it doesn't, create a new binder [] -> do -- Add the created function to the list of global bindings@@ -613,20 +692,21 @@ -- function at this moment for a reason! -- (termination, CSE and DEC oppertunities, -- ,etc.)- (newBodyId- ,sp+ (Binding+ newBodyId+ sp #if MIN_VERSION_ghc(8,4,1)- ,NoUserInline+ NoUserInline #else- ,EmptyInlineSpec+ EmptyInlineSpec #endif- ,newBody)+ newBody) -- Return the new binder return (var, newExpr) -- If it does, use the existing binder- ((k,_,_,_):_) ->+ (b:_) -> let newExpr' = mkTmApps- (mkTyApps (Var k)+ (mkTyApps (Var $ bindingId b) (map VarTy boundFTVs)) (map Var boundFVs) in return (var, newExpr')@@ -669,12 +749,12 @@ -> RewriteMonad extra () addGlobalBind vNm ty sp inl body = do let vId = mkGlobalId ty vNm- (ty,body) `deepseq` bindings %= extendUniqMap vNm (vId,sp,inl,body)+ (ty,body) `deepseq` bindings %= extendUniqMap vNm (Binding vId sp inl body) -- | Create a new name out of the given name, but with another unique. Resulting -- unique is guaranteed to not be in the given InScopeSet. cloneNameWithInScopeSet- :: (Monad m, MonadUnique m)+ :: (MonadUnique m) => InScopeSet -> Name a -> m (Name a)@@ -685,7 +765,7 @@ -- | Create a new name out of the given name, but with another unique. Resulting -- unique is guaranteed to not be in the given BindingMap. cloneNameWithBindingMap- :: (Monad m, MonadUnique m)+ :: (MonadUnique m) => BindingMap -> Name a -> m (Name a)@@ -724,7 +804,7 @@ -- | Make a binder that should not be referenced mkWildValBinder- :: (Monad m, MonadUnique m)+ :: (MonadUnique m) => InScopeSet -> Type -> m Id@@ -733,7 +813,7 @@ -- | Make a case-decomposition that extracts a field out of a (Sum-of-)Product type mkSelectorCase :: HasCallStack- => (Functor m, Monad m, MonadUnique m)+ => (Functor m, MonadUnique m) => String -- ^ Name of the caller of this function -> InScopeSet -> TyConMap -- ^ TyCon cache@@ -786,15 +866,24 @@ -> RewriteMonad extra Term specialise' specMapLbl specHistLbl specLimitLbl (TransformContext is0 _) e (Var f, args, ticks) specArgIn = do lvl <- Lens.view dbgLevel+ tcm <- Lens.view tcCache -- Don't specialise TopEntities topEnts <- Lens.view topEntities if f `elemVarSet` topEnts- then traceIf (lvl >= DebugNone) ("Not specialising TopEntity: " ++ showPpr (varName f)) (return e)+ then do+ case specArgIn of+ Left _ -> traceIf (lvl >= DebugNone) ("Not specializing TopEntity: " ++ showPpr (varName f)) (return e)+ Right tyArg -> traceIf (lvl >= DebugApplied) ("Dropping type application on TopEntity: " ++ showPpr (varName f) ++ "\ntype:\n" ++ showPpr tyArg) $+ -- TopEntities aren't allowed to be semantically polymorphic.+ -- But using type equality constraints they may be syntactically polymorphic.+ -- > topEntity :: forall dom . (dom ~ "System") => Signal dom Bool -> Signal dom Bool+ -- The TyLam's in the body will have been removed by 'Clash.Normalize.Util.substWithTyEq'.+ -- So we drop the TyApp ("specialising" on it) and change the varType to match.+ let newVarTy = piResultTy tcm (varType f) tyArg+ in changed (mkApps (mkTicks (Var f{varType = newVarTy}) ticks) args) else do -- NondecreasingIndentation - tcm <- Lens.view tcCache- let specArg = bimap (normalizeTermTypes tcm) (normalizeType tcm) specArgIn -- Create binders and variable references for free variables in 'specArg' -- (specBndrsIn,specVars) :: ([Either Id TyVar], [Either Term Type])@@ -818,7 +907,7 @@ -- Determine if we can specialize f bodyMaybe <- fmap (lookupUniqMap (varName f)) $ Lens.use bindings case bodyMaybe of- Just (_,sp,inl,bodyTm) -> do+ Just (Binding _ sp inl bodyTm) -> do -- Determine if we see a sequence of specialisations on a growing argument specHistM <- lookupUniqMap f <$> Lens.use (extra.specHistLbl) specLim <- Lens.use (extra . specLimitLbl)@@ -860,7 +949,7 @@ -- are inlined, meaning the state-transition-function -- and the memory element will be in a single function. gTmM <- fmap (lookupUniqMap (varName g)) $ Lens.use bindings- return (g,maybe inl (^. _3) gTmM, maybe specArg (Left . (`mkApps` gArgs) . (^. _4)) gTmM)+ return (g,maybe inl bindingSpec gTmM, maybe specArg (Left . (`mkApps` gArgs) . bindingTerm) gTmM) else return (f,inl,specArg) _ -> return (f,inl,specArg) -- Create specialized functions@@ -894,7 +983,7 @@ newBody = mkAbstraction specArg specBndrs -- See if there's an existing binder that's alpha-equivalent to the -- specialized function- existing <- filterUniqMap ((`aeqTerm` newBody) . (^. _4)) <$> Lens.use bindings+ existing <- filterUniqMap ((`aeqTerm` newBody) . bindingTerm) <$> Lens.use bindings -- Create a new function if an alpha-equivalent binder doesn't exist newf <- case eltsUniqMap existing of [] -> do (cf,sp) <- Lens.use curFun@@ -906,7 +995,7 @@ EmptyInlineSpec #endif newBody- ((k,_,_,_):_) -> return k+ (b:_) -> return (bindingId b) -- Create specialized argument let newArg = Left $ mkApps (Var newf) specVars -- Use specialized argument@@ -926,18 +1015,42 @@ normalizeId tcm v@(Id {}) = v {varType = normalizeType tcm (varType v)} normalizeId _ tyvar = tyvar +-- Note [Collect free-variables in an insertion-ordered set]+--+-- In order for the specialization cache to work, 'specArgBndrsAndVars' should+-- yield (alpha equivalent) results for the same specialization. While collecting+-- free variables in a given term or type it should therefore keep a stable+-- ordering based on the order in which it finds free vars. To see why,+-- consider the following two pseudo-code calls to 'specialise':+--+-- specialise {f ('a', x[123], y[456])}+-- specialise {f ('b', x[456], y[123])}+--+-- Collecting the binders in a VarSet would yield the following (unique ordered)+-- sets:+--+-- {x[123], y[456]}+-- {y[123], x[456]}+--+-- ..and therefore breaking specializing caching. We now track them in insert-+-- ordered sets, yielding:+--+-- {x[123], y[456]}+-- {x[456], y[123]}+-- -- | Create binders and variable references for free variables in 'specArg' specArgBndrsAndVars :: Either Term Type -> ([Either Id TyVar], [Either Term Type]) specArgBndrsAndVars specArg =- let unitFV :: Var a -> Const (UniqSet TyVar,UniqSet Id) (Var a)- unitFV v@(Id {}) = Const (emptyUniqSet,unitUniqSet (coerce v))- unitFV v@(TyVar {}) = Const (unitUniqSet (coerce v),emptyUniqSet)+ -- See Note [Collect free-variables in an insertion-ordered set]+ let unitFV :: Var a -> Const (OSet.OLSet TyVar, OSet.OLSet Id) (Var a)+ unitFV v@(Id {}) = Const (mempty, coerce (OSet.singleton (coerce v)))+ unitFV v@(TyVar {}) = Const (coerce (OSet.singleton (coerce v)), mempty) (specFTVs,specFVs) = case specArg of- Left tm -> (eltsUniqSet *** eltsUniqSet) . getConst $+ Left tm -> (OSet.toListL *** OSet.toListL) . getConst $ Lens.foldMapOf freeLocalVars unitFV tm Right ty -> (eltsUniqSet (Lens.foldMapOf typeFreeVars unitUniqSet ty),[] :: [Id]) @@ -948,3 +1061,52 @@ specTmVars = map (Left . Var) specFVs in (specTyBndrs ++ specTmBndrs,specTyVars ++ specTmVars)++-- | Evaluate an expression to weak-head normal form (WHNF), and apply a+-- transformation on the expression in WHNF.+whnfRW+ :: Bool+ -- ^ Whether the expression we're reducing to WHNF is the subject of a+ -- case expression.+ -> TransformContext+ -> Term+ -> Rewrite extra+ -> RewriteMonad extra Term+whnfRW isSubj ctx@(TransformContext is0 _) e rw = do+ tcm <- Lens.view tcCache+ bndrs <- Lens.use bindings+ (primEval, primUnwind) <- Lens.view evaluator+ ids <- Lens.use uniqSupply+ let (ids1,ids2) = splitSupply ids+ uniqSupply Lens..= ids2+ gh <- Lens.use globalHeap+ case whnf' primEval primUnwind bndrs tcm gh ids1 is0 isSubj e of+ (!gh1,ph,v) -> do+ globalHeap Lens..= gh1+ bindPureHeap tcm ph rw ctx v+{-# SCC whnfRW #-}++-- | Binds variables on the PureHeap over the result of the rewrite+--+-- To prevent unnecessary rewrites only do this when rewrite changed something.+bindPureHeap+ :: TyConMap+ -> PureHeap+ -> Rewrite extra+ -> Rewrite extra+bindPureHeap tcm heap rw (TransformContext is0 hist) e = do+ (e1, Monoid.getAny -> hasChanged) <- Writer.listen $ rw ctx e+ if hasChanged && not (null bndrs)+ then return $ Letrec bndrs e1+ else return e1+ where+ bndrs = map toLetBinding $ toListUniqMap heap+ heapIds = map fst bndrs+ is1 = extendInScopeSetList is0 heapIds+ ctx = TransformContext is1 (LetBody heapIds : hist)++ toLetBinding :: (Unique,Term) -> LetBinding+ toLetBinding (uniq,term) = (nm, term)+ where+ ty = termType tcm term+ nm = mkLocalId ty (mkUnsafeSystemName "x" uniq) -- See [Note: Name re-creation]
src/Clash/Unique.hs view
@@ -1,7 +1,6 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE CPP #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE OverloadedStrings #-} module Clash.Unique ( -- * Unique@@ -40,6 +39,7 @@ , elemUniqMapDirectly -- ** Folding , foldrWithUnique+ , foldlWithUnique' -- ** Conversions -- *** Lists , eltsUniqMap@@ -302,6 +302,14 @@ -> UniqMap a -> b foldrWithUnique f s (UniqMap m) = IntMap.foldrWithKey f s m++-- | Strict left-fold over a map using both the key and the value+foldlWithUnique'+ :: (a -> Unique -> b -> a)+ -> a+ -> UniqMap b+ -> a+foldlWithUnique' f s (UniqMap m) = IntMap.foldlWithKey' f s m -- | Set of things that have a 'Unique' --
src/Clash/Util.hs view
@@ -6,12 +6,11 @@ Assortment of utility function used in the Clash library -} -{-# LANGUAGE CPP #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MagicHash #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE Rank2Types #-}-{-# LANGUAGE TupleSections #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-} {-# OPTIONS_GHC -fno-warn-orphans #-} @@ -47,6 +46,7 @@ import Debug.Trace (trace) import GHC.Base (Int(..),isTrue#,(==#),(+#)) import GHC.Integer.Logarithms (integerLogBase#)+import qualified GHC.LanguageExtensions.Type as LangExt import GHC.Stack (HasCallStack, callStack, prettyCallStack) import Type.Reflection (tyConPackage, typeRepTyCon, typeOf) import qualified Language.Haskell.TH as TH@@ -128,7 +128,7 @@ doc = sep [heading, nest 2 prettyMsg] -- | A class that can generate unique numbers-class MonadUnique m where+class Monad m => MonadUnique m where -- | Get a new unique getUniqueM :: m Int @@ -217,6 +217,11 @@ traceIf False _ = id {-# INLINE traceIf #-} +-- | A version of 'concatMap' that works with a monadic predicate.+concatMapM :: Monad m => (a -> m [b]) -> [a] -> m [b]+concatMapM f as = concat <$> sequence (map f as)+{-# INLINE concatMapM #-}+ -- | Monadic version of 'Data.List.partition' partitionM :: Monad m => (a -> m Bool)@@ -264,11 +269,30 @@ indexMaybe (x:_) 0 = Just x indexMaybe (_:xs) n = indexMaybe xs (n-1) +-- | Same as 'indexNote' with last two arguments swapped+indexNote'+ :: HasCallStack+ => String+ -- ^ Error message to display+ -> Int+ -- ^ Index /n/+ -> [a]+ -- ^ List to index+ -> a+ -- ^ Error or element /n/+indexNote' = flip . indexNote+ -- | Unsafe indexing, return a custom error message when indexing fails-indexNote :: String- -> [a]- -> Int- -> a+indexNote+ :: HasCallStack+ => String+ -- ^ Error message to display+ -> [a]+ -- ^ List to index+ -> Int+ -- ^ Index /n/+ -> a+ -- ^ Error or element /n/ indexNote note = \xs i -> fromMaybe (error note) (indexMaybe xs i) -- | Safe version of 'head'@@ -374,6 +398,18 @@ else anyM p xs +-- | short-circuiting monadic version of 'or'+orM+ :: (Monad m)+ => [m Bool]+ -> m Bool+orM [] = pure False+orM (x:xs) = do+ p <- x+ if p then+ pure True+ else+ orM xs -- | Get the package id of the type of a value -- >>> pkgIdFromTypeable (undefined :: TopEntity)@@ -411,6 +447,12 @@ else return False +traceWith :: (a -> String) -> a -> a+traceWith f a = trace (f a) a++traceShowWith :: Show b => (a -> b) -> a -> a+traceShowWith f a = trace (show (f a)) a+ -- | Left-biased choice on maybes orElse :: Maybe a -> Maybe a -> Maybe a orElse x@(Just _) _y = x@@ -419,3 +461,52 @@ -- | Left-biased choice on maybes orElses :: [Maybe a] -> Maybe a orElses = listToMaybe . catMaybes++-- These language extensions are used for+-- * the interactive session inside clashi+-- * compiling files with clash+-- * running output tests with runghc+-- * compiling (local) Template/Blackbox functions with Hint+wantedLanguageExtensions, unwantedLanguageExtensions :: [LangExt.Extension]+wantedLanguageExtensions =+ [ LangExt.BinaryLiterals+ , LangExt.ConstraintKinds+ , LangExt.DataKinds+ , LangExt.DeriveAnyClass+ , LangExt.DeriveGeneric+ , LangExt.DeriveLift+ , LangExt.DerivingStrategies+ , LangExt.ExplicitForAll+ , LangExt.ExplicitNamespaces+ , LangExt.FlexibleContexts+ , LangExt.FlexibleInstances+ , LangExt.KindSignatures+ , LangExt.MagicHash+ , LangExt.MonoLocalBinds+ , LangExt.QuasiQuotes+ , LangExt.ScopedTypeVariables+ , LangExt.TemplateHaskell+ , LangExt.TemplateHaskellQuotes+ , LangExt.TypeApplications+ , LangExt.TypeFamilies+ , LangExt.TypeOperators+#if !MIN_VERSION_ghc(8,6,0)+ , LangExt.TypeInType+#endif+ ]++unwantedLanguageExtensions =+ [ LangExt.ImplicitPrelude+ , LangExt.MonomorphismRestriction+#if MIN_VERSION_ghc(8,6,0)+ , LangExt.StarIsType+#endif+ , LangExt.Strict+ , LangExt.StrictData+ ]++filterOnFst :: (a -> Bool) -> [(a, b)] -> [b]+filterOnFst f xs = map snd (filter (f . fst) xs)++filterOnSnd :: (b -> Bool) -> [(a, b)] -> [a]+filterOnSnd f xs = map fst (filter (f . snd) xs)
src/Clash/Util/Graph.hs view
@@ -5,6 +5,7 @@ Collection of utilities -}+ module Clash.Util.Graph (topSort, reverseTopSort) where import Data.Tuple (swap)
+ src/Clash/Util/Interpolate.hs view
@@ -0,0 +1,369 @@+{-|+Copyright : (C) 2019, QBayLogic B.V.+ 2013, Nikita Volkov+License : BSD2 (see the file LICENSE)+Maintainer : QBayLogic B.V. <devops@qbaylogic.com>+-}++{-+This is an adaptation of++ https://github.com/nikita-volkov/neat-interpolation/tree/0fc1dd73ea++which is licensed under MIT. The original license will follow.++---------++Copyright (c) 2013, Nikita Volkov++Permission is hereby granted, free of charge, to any person+obtaining a copy of this software and associated documentation+files (the "Software"), to deal in the Software without+restriction, including without limitation the rights to use,+copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the+Software is furnished to do so, subject to the following+conditions:++The above copyright notice and this permission notice shall be+included in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR+OTHER DEALINGS IN THE SOFTWARE.++-}++{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE TemplateHaskell #-}++module Clash.Util.Interpolate(i) where++import Language.Haskell.Meta.Parse (parseExp)+import Language.Haskell.TH.Lib (appE, varE)+import Language.Haskell.TH.Quote (QuasiQuoter(..))+import Language.Haskell.TH.Syntax (Q, Exp)++import qualified Numeric as N+import Data.Char+ (isHexDigit, chr, isOctDigit, isDigit, isSpace)+import Data.Maybe (fromMaybe, isJust, catMaybes)+import Text.Read (readMaybe)++data Line+ = EmptyLine+ | ExprLine Indent String+ | Line Indent [Node]+ deriving (Show)++data Node+ = Literal String+ | Expression String+ deriving (Show)++type Indent = Int++format :: [Node] -> String+format = stripWhiteSpace . showLines . nodesToLines+ where+ go _ [] = []+ go n (c:cs) | c == ' ' = go (n+1) cs+ go 0 (c:cs) = c : go 0 cs+ go n cs = replicate n ' ' ++ (go 0 cs)++ stripWhiteSpace = go 0 . dropWhile isSpace+++showLines :: [Line] -> String+showLines [] = ""+showLines ns = init (concatMap showLine ns)+ where+ showLine :: Line -> String+ showLine EmptyLine = "\n"+ showLine (Line n ns') =+ let theIndent = replicate (n - commonIndent) ' ' in+ theIndent ++ (concatMap nodeToString ns') ++ "\n"+ showLine (ExprLine n s) =+ let theIndent = replicate (n - commonIndent) ' ' in+ concat [theIndent ++ l ++ "\n" | l <- lines s]++ nodeToString :: Node -> String+ nodeToString (Literal s) = s+ nodeToString (Expression s) = s++ commonIndent :: Indent+ commonIndent = foldl1 min (catMaybes (map indent ns))++ indent :: Line -> Maybe Indent+ indent EmptyLine = Nothing+ indent (ExprLine n _) = Just n+ indent (Line n _) = Just n++-- | Collects nodes into lines. Expressions might still contain newlines! Does+-- not start or end with 'EmptyLine'.+nodesToLines :: [Node] -> [Line]+nodesToLines =+ concatMap splitLines+ . mergeLines+ . dropEmpty+ . map splitWords+ . map toLine+ . map dropTrailingEmpty+ . collectLines []+ . joinLiterals+ where+ emptyLit (Literal s) =+ if all isSpace s then+ Just (length s)+ else+ Nothing+ emptyLit _ = Nothing++ isEmptyLine EmptyLine = True+ isEmptyLine _ = False++ dropEmpty = reverse . dropWhile isEmptyLine . reverse . dropWhile isEmptyLine+ dropTrailingEmpty = reverse . dropWhile (isJust . emptyLit) . reverse++ splitLines :: Line -> [Line]+ splitLines EmptyLine = [EmptyLine]+ splitLines e@(ExprLine {}) = [e]+ splitLines (Line n nodes) = map (Line n) (go 0 [] nodes)+ where+ maxLength = 80++ go :: Int -> [Node] -> [Node] -> [[Node]]+ go accLen acc goNodes | accLen > maxLength = reverse acc : go 0 [] goNodes+ go accLen acc (l@(Literal s):goNodes) = go (accLen + length s) (l:acc) goNodes+ go accLen acc (e@(Expression s):goNodes) = go (accLen + length s) (e:acc) goNodes+ go _accLen acc [] = [reverse acc]++ mergeLines :: [Line] -> [Line]+ mergeLines (l0@(Line n0 nodes0):l1@(Line n1 nodes1):ls) =+ if n0 == n1 then+ mergeLines (Line n0 (nodes0 ++ [Literal " "] ++ nodes1) : ls)+ else+ l0:mergeLines (l1:ls)+ mergeLines (l:ls) = l:mergeLines ls+ mergeLines [] = []++ splitWords :: Line -> Line+ splitWords EmptyLine = EmptyLine+ splitWords e@(ExprLine {})= e+ splitWords (Line n nodes) = Line n (concatMap go nodes)+ where+ go (Expression s) = [Expression s]+ go (Literal "") = []+ go (Literal s0) =+ let+ pre = takeWhile (not . (==' ')) s0+ post = dropWhile (not . (== ' ')) s0+ in case post of+ [] -> [Literal s0]+ (_:s1) -> Literal (pre ++ " ") : go (Literal s1)++ -- Convert to 'Line' type+ toLine = \case+ [] -> EmptyLine+ [emptyLit -> Just _] -> EmptyLine+ [Expression s] -> ExprLine 0 s+ [emptyLit -> Just n, Expression s] -> ExprLine n s+ ns@(Expression _:_) -> Line 0 ns+ (Literal s:ns) ->+ Line+ (length (takeWhile (==' ') s))+ (Literal (dropWhile (==' ') s):ns)++ -- collects list of nodes, where each list is a single line+ collectLines collected todo =+ case (collected, todo) of+ ([], []) -> []+ (_, []) -> [reverse collected]+ (_, s@(Expression _):ns) ->+ collectLines (s:collected) ns+ (_, Literal s0:ns) ->+ let+ pre = takeWhile (/= '\n') s0+ post = dropWhile (/= '\n') s0+ in case post of+ [] ->+ collectLines (Literal s0:collected) ns+ (_:s1) ->+ reverse (Literal pre:collected) : collectLines [] (Literal s1:ns)++ joinLiterals :: [Node] -> [Node]+ joinLiterals [] = []+ joinLiterals (Literal s0:Literal s1:ss) = joinLiterals (Literal (s0 ++ s1):ss)+ joinLiterals (n:ns) = n:joinLiterals ns++i :: QuasiQuoter+i = QuasiQuoter {+ quoteExp = (varE 'format `appE`) . toExp . parseNodes . decodeNewlines+ , quotePat = err "pattern"+ , quoteType = err "type"+ , quoteDec = err "declaration"+ }+ where+ err name =+ error ("Clash.Util.Interpolate.i: This QuasiQuoter can not be used as a "+ ++ name ++ "!")++ toExp:: [Node] -> Q Exp+ toExp nodes = case nodes of+ [] -> [|[]|]+ (x:xs) -> f x `appE` toExp xs+ where+ f (Literal s) = [|(Literal s:)|]+ f (Expression e) = [|(Expression (toString ($(reifyExpression e))):)|]++ reifyExpression :: String -> Q Exp+ reifyExpression s = case parseExp s of+ Left _ -> do+ fail ("Parse error in expression: " ++ s) :: Q Exp+ Right e -> return e++parseNodes :: String -> [Node]+parseNodes = go ""+ where+ go :: String -> String -> [Node]+ go acc input = case input of+ "" -> [(lit . reverse) acc]+ '\\':x:xs -> go (x:'\\':acc) xs+ '#':'{':xs -> goExpr input acc [] xs+ x:xs -> go (x:acc) xs+ -- allow '}' to be escaped in code sections+ goExpr input accLit accExpr xs = case span (\x -> x /= '}' && x /= '\\') xs of+ (ys, '}' :zs) -> (lit . reverse) accLit : Expression (reverse accExpr ++ ys) : go "" zs+ (ys, '\\':'}':zs) -> goExpr input accLit ('}' : reverse ys ++ accExpr) zs+ (ys, '\\':zs) -> goExpr input accLit ('\\' : reverse ys ++ accExpr) zs+ (_, "") -> [lit (reverse accLit ++ input)]+ _ -> error "(impossible) parseError in parseNodes"+ lit :: String -> Node+ lit = Literal . unescape++-------------------------------------------------------------------+-- Everything below this line is unchanged from neat-interpolate --+-------------------------------------------------------------------+decodeNewlines :: String -> String+decodeNewlines = go+ where+ go xs = case xs of+ '\r' : '\n' : ys -> '\n' : go ys+ y : ys -> y : go ys+ [] -> []++toString :: Show a => a -> String+toString a = let s = show a in fromMaybe s (readMaybe s)+{-# NOINLINE toString #-}+{-# RULES "toString/String" toString = id #-}+{-# RULES "toString/Int" toString = show :: Int -> String #-}+{-# RULES "toString/Integer" toString = show :: Integer -> String #-}+{-# RULES "toString/Float" toString = show :: Float -> String #-}+{-# RULES "toString/Double" toString = show :: Double -> String #-}++-- Haskell 2010 character unescaping, see:+-- http://www.haskell.org/onlinereport/haskell2010/haskellch2.html#x7-200002.6+unescape :: String -> String+unescape = go+ where+ go input = case input of+ "" -> ""+ '\\' : 'x' : x : xs | isHexDigit x -> case span isHexDigit xs of+ (ys, zs) -> (chr . readHex $ x:ys) : go zs+ '\\' : 'o' : x : xs | isOctDigit x -> case span isOctDigit xs of+ (ys, zs) -> (chr . readOct $ x:ys) : go zs+ '\\' : x : xs | isDigit x -> case span isDigit xs of+ (ys, zs) -> (chr . read $ x:ys) : go zs+ '\\' : input_ -> case input_ of+ '\\' : xs -> '\\' : go xs+ 'a' : xs -> '\a' : go xs+ 'b' : xs -> '\b' : go xs+ 'f' : xs -> '\f' : go xs+ 'n' : xs -> '\n' : go xs+ 'r' : xs -> '\r' : go xs+ 't' : xs -> '\t' : go xs+ 'v' : xs -> '\v' : go xs+ '&' : xs -> go xs+ 'N':'U':'L' : xs -> '\NUL' : go xs+ 'S':'O':'H' : xs -> '\SOH' : go xs+ 'S':'T':'X' : xs -> '\STX' : go xs+ 'E':'T':'X' : xs -> '\ETX' : go xs+ 'E':'O':'T' : xs -> '\EOT' : go xs+ 'E':'N':'Q' : xs -> '\ENQ' : go xs+ 'A':'C':'K' : xs -> '\ACK' : go xs+ 'B':'E':'L' : xs -> '\BEL' : go xs+ 'B':'S' : xs -> '\BS' : go xs+ 'H':'T' : xs -> '\HT' : go xs+ 'L':'F' : xs -> '\LF' : go xs+ 'V':'T' : xs -> '\VT' : go xs+ 'F':'F' : xs -> '\FF' : go xs+ 'C':'R' : xs -> '\CR' : go xs+ 'S':'O' : xs -> '\SO' : go xs+ 'S':'I' : xs -> '\SI' : go xs+ 'D':'L':'E' : xs -> '\DLE' : go xs+ 'D':'C':'1' : xs -> '\DC1' : go xs+ 'D':'C':'2' : xs -> '\DC2' : go xs+ 'D':'C':'3' : xs -> '\DC3' : go xs+ 'D':'C':'4' : xs -> '\DC4' : go xs+ 'N':'A':'K' : xs -> '\NAK' : go xs+ 'S':'Y':'N' : xs -> '\SYN' : go xs+ 'E':'T':'B' : xs -> '\ETB' : go xs+ 'C':'A':'N' : xs -> '\CAN' : go xs+ 'E':'M' : xs -> '\EM' : go xs+ 'S':'U':'B' : xs -> '\SUB' : go xs+ 'E':'S':'C' : xs -> '\ESC' : go xs+ 'F':'S' : xs -> '\FS' : go xs+ 'G':'S' : xs -> '\GS' : go xs+ 'R':'S' : xs -> '\RS' : go xs+ 'U':'S' : xs -> '\US' : go xs+ 'S':'P' : xs -> '\SP' : go xs+ 'D':'E':'L' : xs -> '\DEL' : go xs+ '^':'@' : xs -> '\^@' : go xs+ '^':'A' : xs -> '\^A' : go xs+ '^':'B' : xs -> '\^B' : go xs+ '^':'C' : xs -> '\^C' : go xs+ '^':'D' : xs -> '\^D' : go xs+ '^':'E' : xs -> '\^E' : go xs+ '^':'F' : xs -> '\^F' : go xs+ '^':'G' : xs -> '\^G' : go xs+ '^':'H' : xs -> '\^H' : go xs+ '^':'I' : xs -> '\^I' : go xs+ '^':'J' : xs -> '\^J' : go xs+ '^':'K' : xs -> '\^K' : go xs+ '^':'L' : xs -> '\^L' : go xs+ '^':'M' : xs -> '\^M' : go xs+ '^':'N' : xs -> '\^N' : go xs+ '^':'O' : xs -> '\^O' : go xs+ '^':'P' : xs -> '\^P' : go xs+ '^':'Q' : xs -> '\^Q' : go xs+ '^':'R' : xs -> '\^R' : go xs+ '^':'S' : xs -> '\^S' : go xs+ '^':'T' : xs -> '\^T' : go xs+ '^':'U' : xs -> '\^U' : go xs+ '^':'V' : xs -> '\^V' : go xs+ '^':'W' : xs -> '\^W' : go xs+ '^':'X' : xs -> '\^X' : go xs+ '^':'Y' : xs -> '\^Y' : go xs+ '^':'Z' : xs -> '\^Z' : go xs+ '^':'[' : xs -> '\^[' : go xs+ '^':'\\' : xs -> '\^\' : go xs+ '^':']' : xs -> '\^]' : go xs+ '^':'^' : xs -> '\^^' : go xs+ '^':'_' : xs -> '\^_' : go xs+ xs -> go xs+ x:xs -> x : go xs++ readHex :: String -> Int+ readHex xs = case N.readHex xs of+ [(n, "")] -> n+ _ -> error "Data.String.Interpolate.Util.readHex: no parse"++ readOct :: String -> Int+ readOct xs = case N.readOct xs of+ [(n, "")] -> n+ _ -> error "Data.String.Interpolate.Util.readHex: no parse"
src/Data/Aeson/Extra.hs view
@@ -14,6 +14,8 @@ import Data.Aeson (FromJSON, Result (..), fromJSON, json) import Data.Attoparsec.Lazy (Result (..), parse) import Data.ByteString.Lazy (ByteString)+import qualified Data.ByteString.Lazy as BS+import qualified Data.ByteString.Lazy.Char8 as BSChar import System.FilePath () import Clash.Util (ClashException(..))@@ -55,8 +57,12 @@ -> a decodeOrErr path contents = case parse json contents of- Done _ v ->+ Done leftover v -> case fromJSON v of+ Success _ | BS.any notWhitespace leftover ->+ clashError ("After parsing " ++ show path+ ++ ", found unparsed trailing garbage:\n"+ ++ BSChar.unpack leftover) Success a -> a Error msg ->@@ -77,3 +83,5 @@ where loc = mkGeneralSrcSpan $ mkFastString path clashError msg = throw $ ClashException loc msg Nothing+ notWhitespace c = BS.notElem c whitespace+ where whitespace = BSChar.pack " \t\n\r"
+ src/Data/Set/Ordered/Extra.hs view
@@ -0,0 +1,36 @@+{-|+ Copyright : (C) 2019, QBayLogic B.V+ License : BSD2 (see the file LICENSE)+ Maintainer : QBayLogic B.V <devops@qbaylogic.com>++ Convenience functions for "Data.Set.Ordered" from the package+ "ordered-containers".+-}++module Data.Set.Ordered.Extra+ ( OLSet+ , ORSet+ , toSetL+ , toSetR+ , toListL+ , toListR+ ) where++import Data.Coerce (coerce)+import Data.Foldable (toList)+import qualified Data.Set.Ordered as OSet++type OLSet a = OSet.Bias OSet.L (OSet.OSet a)+type ORSet a = OSet.Bias OSet.R (OSet.OSet a)++toSetL :: Ord a => [a] -> OLSet a+toSetL = coerce . OSet.fromList++toSetR :: Ord a => [a] -> ORSet a+toSetR = coerce . OSet.fromList++toListL :: forall a. OLSet a -> [a]+toListL = toList . coerce @(OLSet a) @(OSet.OSet a)++toListR :: forall a. ORSet a -> [a]+toListR = toList . coerce @(ORSet a) @(OSet.OSet a)
src/Data/Text/Prettyprint/Doc/Extra.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE BangPatterns #-} {-# LANGUAGE FlexibleInstances #-} {-# OPTIONS_GHC -Wno-orphans #-}@@ -179,3 +178,11 @@ instance Applicative f => IsString (f Doc) where fromString = string . fromString++comment :: Applicative f => T.Text -> T.Text -> f Doc+comment prefix comm =+ let go s = PP.pretty prefix PP.<+> PP.pretty s in+ pure (PP.vsep (Prelude.map go (T.lines comm)))++squote :: Applicative f => f Doc+squote = string (LT.pack "'")
src/GHC/BasicTypes/Extra.hs view
@@ -4,9 +4,6 @@ Maintainer : Christiaan Baaij <christiaan.baaij@gmail.com> -} -{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE StandaloneDeriving #-}- {-# OPTIONS_GHC -fno-warn-orphans #-} module GHC.BasicTypes.Extra where
src/GHC/SrcLoc/Extra.hs view
@@ -4,10 +4,8 @@ Maintainer : Christiaan Baaij <christiaan.baaij@gmail.com> -} -{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE StandaloneDeriving #-}-{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -fno-warn-orphans #-}
+ tests/Clash/Tests/Core/Subst.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE OverloadedStrings #-}++module Clash.Tests.Core.Subst (tests) where++import SrcLoc (noSrcSpan)++import Test.Tasty+import Test.Tasty.HUnit++import Clash.Core.Name (Name(..), NameSort(..))+import Clash.Core.Term (Term(Var))+import Clash.Core.Type (ConstTy(..), Type(ConstTy))+import Clash.Core.Subst+import Clash.Core.VarEnv+import Clash.Core.Var (IdScope(..), Var(..))++fakeName :: Name a+fakeName =+ Name+ { nameSort=User+ , nameOcc="fake"+ , nameUniq=0+ , nameLoc=noSrcSpan+ }++unique :: Int+unique = 20++termVar :: Var Term+termVar = Id {+ varName = fakeName {nameUniq=unique, nameOcc="term"}+ , varUniq = unique+ , varType = ConstTy (TyCon fakeName)+ , idScope = LocalId+ }++typeVar :: Var Type+typeVar = TyVar {+ varName = fakeName {nameUniq=unique, nameOcc="type"}+ , varUniq = unique+ , varType = ConstTy (TyCon fakeName)+ }++term1 :: Term+term1 = Var termVar++tests :: TestTree+tests =+ testGroup+ "Clash.Tests.Core.Subst"+ [ testCase "deShadow type/term" $+ term1 @=? deShadowTerm (extendInScopeSet emptyInScopeSet typeVar) term1+ ]
+ tests/Clash/Tests/Util/Interpolate.hs view
@@ -0,0 +1,72 @@+{-|+Copyright : (C) 2019, QBayLogic B.V.+License : BSD2 (see the file LICENSE)+Maintainer : QBayLogic B.V. <devops@qbaylogic.com>+-}+{-# LANGUAGE QuasiQuotes #-}++module Clash.Tests.Util.Interpolate where++import qualified Clash.Util.Interpolate as I++import Test.Tasty+import Test.Tasty.HUnit++test1, test2, test3, test4, test5, test6, test7, test8, test9 :: String+test1 = [I.i| Simple |]+test2 = [I.i|+ Single line+|]+test3 = [I.i|++ Surrounded by newlines++|]+test4 = [I.i|+ One+ Two+ Three+|]+test5 = [I.i|+ One+ Two+ Three+|]+test6 = [I.i|+ #{test5}+|]++test7 = [I.i|+ The big test:++ #{test5}+|]++test8 = [I.i|+ looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong word+|]++data SomeRecord = SomeRecord { getField :: Int }+someRecord :: SomeRecord+someRecord = SomeRecord 5+-- test that we can escape closing '}'+test9 = [I.i| #{ "\\" ++ show (getField someRecord { getField=6*7+ \}+ )+ }+ |]++tests :: TestTree+tests =+ testGroup+ "Clash.Tests.Core.Util.Interpolation"+ [ testCase "test1" $ "Simple" @=? test1+ , testCase "test2" $ "Single line" @=? test2+ , testCase "test3" $ "Surrounded by newlines" @=? test3+ , testCase "test4" $ "One Two Three" @=? test4+ , testCase "test5" $ "One\n Two\nThree" @=? test5+ , testCase "test6" $ test5 @=? test6+ , testCase "test7" $ test7 @?= ("The big test:\n\n" ++ test5)+ , testCase "test8" $ test8 @?= "looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong \nword"+ , testCase "test9" $ test9 @?= "\\42"+ ]
tests/unittests.hs view
@@ -3,10 +3,14 @@ import Test.Tasty import qualified Clash.Tests.Core.FreeVars+import qualified Clash.Tests.Core.Subst+import qualified Clash.Tests.Util.Interpolate tests :: TestTree tests = testGroup "Unittests" [ Clash.Tests.Core.FreeVars.tests+ , Clash.Tests.Core.Subst.tests+ , Clash.Tests.Util.Interpolate.tests ] main :: IO ()