{-# LANGUAGE CPP #-} module ADropUndet(aDropUndet) where import qualified Data.Map.Lazy as M import Error(ErrMsg(..), ErrorHandle, bsErrorUnsafe, internalError) import PPrint import Position import Backend import ASyntax import ASyntaxUtil import IntLit(ilHex) import IntegerUtil import Flags import Prim -- During elaboration, undetermined values may have been annotated with -- recommended values (but left as undetermined). Here we replace them -- with the recommended value. -- -- For undetermined values without recommendations, we have two options: -- leave it for the backend to optimize or pick a value now (so that all -- backends see the same value, to produce identical simulation results). -- This choice is specified by the user with the -opt-undetermined-vals flag. -- If a value is picked, use to value given by the -unspecified-to flag. -- -- This stage also applies an if-expr optimization [see XXX comments]. -- That is, if one branch of an if-expr is Undet, then it picks the value -- to be the same as the other branch. This is not safe to do when the -- other branch contains the result of an ActionValue method or foreign -- task, unless -opt-undetermined-vals is given. (See the "canFixUndet" -- function in ITransform, which is doing a simillar check). -- aDropUndet :: ErrorHandle -> Flags -> APackage -> APackage aDropUndet errh flags apkg = let defmap = M.fromList $ map (\d -> (adef_objid d, adef_expr d)) $ apkg_local_defs apkg in mapAExprs (fixUndet errh flags defmap) apkg fixUndet :: ErrorHandle -> Flags -> M.Map AId AExpr -> AExpr -> AExpr fixUndet errh flags defmap = g where -- whether to pick values for undets without a recommended value b = optUndet flags -- the value that undets should be set to tgt = unSpecTo flags -- construct a map of whether each def contains an AVValue avmap :: M.Map AId Bool avmap = M.map (hasNoActionValue avmap) defmap -- recursively apply "f" to an expression g = exprMap f -- handle an expression f :: AExpr -> Maybe AExpr -- if the Undet has a recommended value, use it f (ASAny _ (Just e)) = Just (g e) -- if it doesn't, and the backends are not allowed to diverge, -- then replace it with a constant f (ASAny t Nothing) | (not b) = Just (mkUnspec errh tgt t) -- XXX This is a fixup for ITransform not picking the best value. -- XXX The long-term solution is to not pick values in ITransform, -- XXX but to wait until the Path Graph is available. f (APrim i t PrimIf [_, tt, ff]) = let tt' = g tt ff' = g ff in case (tt,ff) of ((ASAny _ _), _) | canFixUndet flags avmap ff' -> Just ff' (_, (ASAny _ _)) | canFixUndet flags avmap tt' -> Just tt' _ -> Nothing -- otherwise, don't change it f _ = Nothing mkUnspec :: ErrorHandle -> String -> AType -> AExpr mkUnspec errh tgt t = let sz = aSize t val = case tgt of "0" -> 0 "1" -> (2^sz) -1 "A" -> aaaa sz _ -> -- this situation should have been rejected when -- we checked command-line flags bsErrorUnsafe errh [(noPosition, ENoOptUndetNoXZ tgt)] in ASInt defaultAId t (ilHex val) canFixUndet :: Flags -> M.Map AId Bool -> AExpr -> Bool canFixUndet flags avmap e = if (optUndet flags && (backend flags == Just Verilog)) then True else hasNoActionValue avmap e hasNoActionValue :: M.Map AId Bool -> AExpr -> Bool hasNoActionValue avm (APrim { ae_args = es }) = all (hasNoActionValue avm) es hasNoActionValue avm (AMethCall { ae_args = es }) = all (hasNoActionValue avm) es hasNoActionValue avm (AMethValue {}) = False hasNoActionValue avm (ANoInlineFunCall { ae_args = es }) = all (hasNoActionValue avm) es hasNoActionValue avm (AFunCall { ae_args = es }) = all (hasNoActionValue avm) es hasNoActionValue avm (ATaskValue {}) = False hasNoActionValue avm (ASPort {}) = True hasNoActionValue avm (ASParam {}) = True hasNoActionValue avm (ASDef { ae_objid = i }) = case (M.lookup i avm) of Just b -> b Nothing -> internalError ("hasNoActionValue: ASDef: " ++ ppReadable i) hasNoActionValue avm (ASInt {}) = True hasNoActionValue avm (ASReal {}) = True hasNoActionValue avm (ASStr {}) = True hasNoActionValue avm (ASAny { ae_val = Nothing }) = True -- XXX if the recommended value has an AV, we could just drop it hasNoActionValue avm (ASAny { ae_val = Just e }) = hasNoActionValue avm e hasNoActionValue avm (ASClock {}) = True hasNoActionValue avm (ASReset {}) = True hasNoActionValue avm (ASInout {}) = True hasNoActionValue avm (AMGate {}) = True