-- GHC API
import CoreSyn
import qualified CoreUtils
+import qualified BasicTypes
import qualified Type
+import qualified TysWiredIn
import qualified Id
import qualified Var
import qualified Name
+import qualified DataCon
import qualified VarSet
import qualified CoreFVs
import qualified Class
-- Perform this transform everywhere
betatop = everywhere ("beta", beta)
+--------------------------------
+-- Case of known constructor simplification
+--------------------------------
+-- If a case expressions scrutinizes a datacon application, we can
+-- determine which alternative to use and remove the case alltogether.
+-- We replace it with a let expression the binds every binder in the
+-- alternative bound to the corresponding argument of the datacon. We do
+-- this instead of substituting the binders, to prevent duplication of
+-- work and preserve sharing wherever appropriate.
+knowncase, knowncasetop :: Transform
+knowncase context expr@(Case scrut@(App _ _) bndr ty alts) | not bndr_used = do
+ case collectArgs scrut of
+ (Var f, args) -> case Id.isDataConId_maybe f of
+ -- Not a dataconstructor? Don't change anything (probably a
+ -- function, then)
+ Nothing -> return expr
+ Just dc -> do
+ let (altcon, bndrs, res) = case List.find (\(altcon, bndrs, res) -> altcon == (DataAlt dc)) alts of
+ Just alt -> alt -- Return the alternative found
+ Nothing -> head alts -- If the datacon is not present, the first must be the default alternative
+ -- Double check if we have either the correct alternative, or
+ -- the default.
+ if altcon /= (DataAlt dc) && altcon /= DEFAULT then error ("Normalize.knowncase: Invalid core, datacon not found in alternatives and DEFAULT alternative is not first? " ++ pprString expr) else return ()
+ -- Find out how many arguments to drop (type variables and
+ -- predicates like dictionaries).
+ let (tvs, preds, _, _) = DataCon.dataConSig dc
+ let count = length tvs + length preds
+ -- Create a let expression that binds each of the binders in
+ -- this alternative to the corresponding argument of the data
+ -- constructor.
+ let binds = zip bndrs (drop count args)
+ change $ Let (Rec binds) res
+ _ -> return expr -- Scrutinee is not an application of a var
+ where
+ is_used (_, _, expr) = expr_uses_binders [bndr] expr
+ bndr_used = or $ map is_used alts
+
+-- Leave all other expressions unchanged
+knowncase c expr = return expr
+-- Perform this transform everywhere
+knowncasetop = everywhere ("knowncase", knowncase)
+
--------------------------------
-- Cast propagation
--------------------------------
-- Perform this transform everywhere
argproptop = everywhere ("argprop", argprop)
+--------------------------------
+-- Non-representable result inlining
+--------------------------------
+-- This transformation takes a function (top level binding) that has a
+-- non-representable result (e.g., a tuple containing a function, or an
+-- Integer. The latter can occur in some cases as the result of the
+-- fromIntegerT function) and inlines enough of the function to make the
+-- result representable again.
+--
+-- This is done by first normalizing the function and then "inlining"
+-- the result. Since no unrepresentable let bindings are allowed in
+-- normal form, we can be sure that all free variables of the result
+-- expression will be representable (Note that we probably can't
+-- guarantee that all representable parts of the expression will be free
+-- variables, so we might inline more than strictly needed).
+--
+-- The new function result will be a tuple containing all free variables
+-- of the old result, so the old result can be rebuild at the caller.
+--
+-- We take care not to inline dictionary id's, which are top level
+-- bindings with a non-representable result type as well, since those
+-- will never become VHDL signals directly. There is a separate
+-- transformation (inlinedict) that specifically inlines dictionaries
+-- only when it is useful.
+inlinenonrepresult, inlinenonrepresulttop :: Transform
+
+-- Apply to any (application of) a reference to a top level function
+-- that is fully applied (i.e., dos not have a function type) but is not
+-- representable. We apply in any context, since non-representable
+-- expressions are generally left alone and can occur anywhere.
+inlinenonrepresult context expr | not (is_fun expr) =
+ case collectArgs expr of
+ (Var f, args) | not (Id.isDictId f) -> do
+ repr <- isRepr expr
+ if not repr
+ then do
+ body_maybe <- Trans.lift $ getNormalized_maybe True f
+ case body_maybe of
+ Just body -> do
+ let (bndrs, binds, res) = splitNormalizedNonRep body
+ if has_free_tyvars res
+ then
+ -- Don't touch anything with free type variables, since
+ -- we can't return those. We'll wait until argprop
+ -- removed those variables.
+ return expr
+ else do
+ -- Get the free local variables of res
+ global_bndrs <- Trans.lift getGlobalBinders
+ let interesting var = Var.isLocalVar var && (var `notElem` global_bndrs)
+ let free_vars = VarSet.varSetElems $ CoreFVs.exprSomeFreeVars interesting res
+ let free_var_types = map Id.idType free_vars
+ let n_free_vars = length free_vars
+ -- Get a tuple datacon to wrap around the free variables
+ let fvs_datacon = TysWiredIn.tupleCon BasicTypes.Boxed n_free_vars
+ let fvs_datacon_id = DataCon.dataConWorkId fvs_datacon
+ -- Let the function now return a tuple with references to
+ -- all free variables of the old return value. First pass
+ -- all the types of the variables, since tuple
+ -- constructors are polymorphic.
+ let newres = mkApps (Var fvs_datacon_id) (map Type free_var_types ++ map Var free_vars)
+ -- Recreate the function body with the changed return value
+ let newbody = mkLams bndrs (Let (Rec binds) newres)
+ -- Create the new function
+ f' <- Trans.lift $ mkFunction f newbody
+
+ -- Call the new function
+ let newapp = mkApps (Var f') args
+ res_bndr <- Trans.lift $ mkBinderFor newapp "res"
+ -- Create extractor case expressions to extract each of the
+ -- free variables from the tuple.
+ sel_cases <- Trans.lift $ mapM (mkSelCase (Var res_bndr)) [0..n_free_vars-1]
+
+ -- Bind the res_bndr to the result of the new application
+ -- and each of the free variables to the corresponding
+ -- selector case. Replace the let body with the original
+ -- body of the called function (which can still access all
+ -- of its free variables, from the let).
+ let binds = (res_bndr, newapp):(zip free_vars sel_cases)
+ let letexpr = Let (Rec binds) res
+
+ -- Finally, regenarate all uniques in the new expression,
+ -- since the free variables could otherwise become
+ -- duplicated. It is not strictly necessary to regenerate
+ -- res, since we're moving that expression, but it won't
+ -- hurt.
+ letexpr_uniqued <- Trans.lift $ genUniques letexpr
+ change letexpr_uniqued
+ Nothing -> return expr
+ else
+ -- Don't touch representable expressions or (applications of)
+ -- dictionary ids.
+ return expr
+ -- Not a reference to or application of a top level function
+ _ -> return expr
+-- Leave all other expressions unchanged
+inlinenonrepresult c expr = return expr
+-- Perform this transform everywhere
+inlinenonrepresulttop = everywhere ("inlinenonrepresult", inlinenonrepresult)
+
+
--------------------------------
-- Function-typed argument extraction
--------------------------------
-- What transforms to run?
-transforms = [inlinedicttop, inlinetopleveltop, classopresolutiontop, argproptop, funextracttop, etatop, betatop, castproptop, letremovesimpletop, letrectop, letremovetop, retvalsimpltop, letflattop, scrutsimpltop, scrutbndrremovetop, casesimpltop, caseremovetop, inlinenonreptop, appsimpltop, letremoveunusedtop, castsimpltop]
+transforms = [inlinedicttop, inlinetopleveltop, inlinenonrepresulttop, knowncasetop, classopresolutiontop, argproptop, funextracttop, etatop, betatop, castproptop, letremovesimpletop, letrectop, letremovetop, retvalsimpltop, letflattop, scrutsimpltop, scrutbndrremovetop, casesimpltop, caseremovetop, inlinenonreptop, appsimpltop, letremoveunusedtop, castsimpltop]
-- | Returns the normalized version of the given function, or an error
-- if it is not a known global binder.