úÎbĸWœŒ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcd e f g h i j k l m n o p q r s t u v w x y z { | } ~  €  ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ (c) Sebastian Graf 2018ISCsgraf1337@gmail.comportableNone7<FTzChooses an appropriate  for a given key type.MonoMapGs should all be ordered maps, which feature efficient variants of the  and  combinators. This unifies  Data.Maybe, Data.IntMap.Strict, Data.Map.Strict and Data.POMap.Strictg under a common type class, for which instances can delegate to the most efficient variant available. Because of D, this class lends itself well to approximating monotone functions.(The default implementation delegates to Œ€, so when there is no specially crafted map data-structure for your key type, all you need to do is to make sure it satisfies . Then you can doimport Data.IntSetinstance MonoMapKey IntSet*to make use of the default implementation.BThe particular ordered map implementation to use for the key type k.(The default implementation delegates to Œ.^Key point of this interface! Note that it returns a list of lower bounds, to account for the  case. Delegates to Ž. Delegates to .       (c) Sebastian Graf 2018ISCsgraf1337@gmail.comportableNone0<æ7Highest priority node and lowest element of the domain k first.‘’“”•–—˜™š›œžŸ‘’ (c) Sebastian Graf 2018ISCsgraf1337@gmail.comportableNone0<X  ĄĸŖ¤Ĩϧ¨Š Ą (c) Sebastian Graf 2018ISCsgraf1337@gmail.comportableNonešĒĢŦ­Ž¯Ē°ą˛(c) Sebastian Graf 2018ISCsgraf1337@gmail.comportableNone&'+,-;<=>?FSTVh0Š  Currying as b# witnesses the isomorphism between  Arrows as b and Products as -> bO. It is defined as a type class rather than by recursion on a singleton for asX so all of that these conversions are inlined at compile time for concrete arguments.Using IsBase we can define notions of  ParamTypes and  ReturnTypes. which *reduce* under positive information IsBase t ~ 'True even though the shape of t is not formally exposedIsBase t is 'True whenever t is *not* a function space. Products [] corresponds to (),  Products [a] corresponds to a, Products [a1,..,an] corresponds to (a1, (..,( an)..)).`So, not quite a right fold, because we want to optimize for the empty, singleton and pair case.Arrows [a1,..,an] r corresponds to a1 -> .. -> an -> r$ Version of FoldrV taking a defunctionalised argument so that we can use partially applied functions.%On Lists& On Booleans'All p as ensures that the constraint p is satisfied by all the types in asZ. (Types is between scare-quotes here because the code is actually kind polymorphic) !"#$%&'('&%$#"! (+*)(c) Sebastian Graf 2018ISCsgraf1337@gmail.comportableNone <>?AFTVh‘ß, Builds on .< by providing a way to track dependencies without explicit 7E management. Essentially, this allows to specify a build plan for a 1 through calls to - in analogy to fix or mfix.-CThis is the closest we can get to an actual fixed-point combinator.We need to provide a 6ŋ for detecting the fixed-point as well as a function to be iterated. In addition to returning a better approximation of itself in terms of itself, it can return an arbitrary value of type a.. Because the iterated function might want to -U additional times (think of nested let bindings), the return values are wrapped in mdat.Finally, the arbitrary a" value is returned, in analogy to a in 'mfix :: MonadFix m => (a -> m a) -> m a..'A monad with a single impure primitive 0# that expresses a dependency on a 7 of a data-flow graph.The associated /1 type is the abstract domain in which we denote 7s.eThink of it like memoization on steroids. You can represent dynamic programs with this quite easily::{[ transferFib :: forall m . (MonadDependency m, Domain m ~ Int) => Node -> LiftedFunc Int m! transferFib (Node 0) = return 0! transferFib (Node 1) = return 1V transferFib (Node n) = (+) <$> dependOn @m (Node (n-1)) <*> dependOn @m (Node (n-2))& -- sparing the negative n error case:}$We can construct a description of a 1 with this  transferFib function::{X dataFlowProblem :: forall m . (MonadDependency m, Domain m ~ Int) => DataFlowProblem mJ dataFlowProblem = DFP transferFib (const (eqChangeDetector @(Domain m))):}We regard the ordinary fib2 function a solution to the recurrence modeled by  transferFib::{ fib :: Int -> Int fib 0 = 0 fib 1 = 1! fib n = fib (n-1) + fib (n - 2):}E.g., under the assumption of fibj being total (which is true on the domain of natural numbers), it computes the same results as the least  fixed-point7 of the series of iterations of the transfer function  transferFib.!Ostensibly, the nth iteration of  transferFib substitutes each dependOn with  transferFib? repeatedly for n times and finally substitutes all remaining dependOns with a call to ŗ.Computing a solution by fixed-point iteration† in a declarative manner is the purpose of this library. There potentially are different approaches to computing a solution, but in Datafix.Worklist‰ we offer an approach based on a worklist algorithm, trying to find a smart order in which nodes in the data-flow graph are reiterated.ŽThe concrete MonadDependency depends on the solution algorithm, which is in fact the reason why there is no satisfying data type in this module: We are only concerned with  declaring data-flow problems here.’The distinguishing feature of data-flow graphs is that they are not necessarily acyclic (data-flow graphs of dynamic programs always are!), but  8https://en.wikipedia.org/wiki/Kleene_fixed-point_theoremunder certain conditions, even have solutions when there are cycles.ÛCycles occur commonly in data-flow problems of static analyses for programming languages, introduced through loops or recursive functions. Thus, this library mostly aims at making the life of compiler writers easier./The abstract domain in which 7Ås of the data-flow graph are denoted. When this is a synonym for a function, then all functions of this domain are assumed to be monotone wrt. the (at least) partial order of all occuring types!NIf you can't guarantee monotonicity, try to pull non-monotone arguments into 7s.0ŽExpresses a dependency on a node of the data-flow graph, thus introducing a way of trackable recursion. That's similar to how you would use   to abstract over recursion.1'Models a data-flow problem, where each 7 is mapped to its denoting 5] and a means to detect when the iterated transfer function reached a fixed-point through a 6.3A transfer function per each 7" of the modeled data-flow problem.4A 6 for each 7T of the modeled data-flow problem. In the simplest case, this just delegates to an ´ instance.5Data-flow problems denote 79s in the data-flow graph by monotone transfer functions.This type alias alone carries no semantic meaning. However, it is instructive to see some examples of how this alias reduces to a normal form: ´ LiftedFunc Int m ~ m Int LiftedFunc (Bool -> Int) m ~ Bool -> m Int LiftedFunc (a -> b -> Int) m ~ a -> b -> m Int LiftedFunc (a -> b -> c -> Int) m ~ a -> b -> c -> m Int m" will generally be an instance of .' and the type alias effectively wraps m around domainw's return type. The result is a function that produces its return value while potentially triggering side-effects in m!, which amounts to depending on 5 s of other 7 s for the . case.69A function that checks points of some function with type domain for changes. If this returns ĩ7, the point of the function is assumed to have changed.SAn example is worth a thousand words, especially because of the type-level hackery:5cd = (\a b -> even a /= even b) :: ChangeDetector Int}This checks the parity for changes in the abstract domain of integers. Integers of the same parity are considered unchanged.cd 4 5Truecd 7 13False&Now a (quite bogus) pointwise example:Jcd = (\x fx gx -> x + abs fx /= x + abs gx) :: ChangeDetector (Int -> Int) cd 1 (-1) 1False cd 15 1 2Truecd 13 35 (-35)FalseThis would consider functions id and negate unchanged, so the sequence iterate negate :: Int -> Int- would be regarded immediately as convergent:f x = iterate negate x !! 0let g x = iterate negate x !! 1cd 123 (f 123) (g 123)False7>This is the type we use to index nodes in the data-flow graph..The connection between syntactic things (e.g. Ids) and 7gs is made implicitly in code in analysis templates through an appropriate allocation mechanism as in  NodeAllocator.:!Shorthand that partially applies - to an ;.;A 6 that delegates to the ´ instance of the node values.<A 6 that always returns ĩ.ŽUse this when recomputing a node is cheaper than actually testing for the change. Beware of cycles in the resulting dependency graph, though!,-./0123456789:;<789561234./0,-:;<,-./01234789(c) Sebastian Graf 2018ISCsgraf1337@gmail.comportableNoneKšķ@&A state monad wrapping a mapping from 7 to some v+ which we will instantiate to appropriate 5s.AAllocates the next 74, which is greater than any nodes requested before.1The value stored at that node is the result of a @+ computation which may already access the 7ˆ associated with that value. This is important for the case of recursive let, where the denotation of an expression depends on itself.B4Runs the allocator, beginning with an empty mapping.@AB@AB@ļˇ(c) Sebastian Graf 2018ISCsgraf1337@gmail.comportableNone ;<=>?FKTVhĸŽFConstructs a build plan for a 1 by tracking allocation of 7 s mapping to 6s and transfer functions.G'(root, max, dfp) = buildProblem builder' executes the build plan specified by builder and returns the resulting 1 dfp, as well as the root 71 denoting the transfer function returned by the F action and the max7imum node of the problem as a proof for its denseness.FGFGF¸š(c) Sebastian Graf 2018ISCsgraf1337@gmail.comportableNone0<FT­Ĩ LBAbstracts over the concrete representation of the data-flow graph.%There are two instances: The default  for sparse graphs based on an IntMap and " for the dense case, storing the Node mapping in a .PDiff between two  s.TMThe data associated with each point in the transfer function of a data-flow Node.V The value at this point. Can be ē only when a loop was detected.W6Points this point of the transfer function depends on.XPoints depending on this point.YBThe number of times this point has been updated through calls to updateNodeValue.Z The default T.[Computes the diff between two  s.LMONPQSRTUYXWVZ[TUVWXYZPQRS[LMNOLMNOPQRSTUVWXY(c) Sebastian Graf 2018ISCsgraf1337@gmail.comportableNone<FT˛^4Reference to a dense data-flow graph representation.ģ@Models the points of a transfer function of a single data-flow Node._Allocates a new dense graph ^.^_^_^ŧ(c) Sebastian Graf 2018ISCsgraf1337@gmail.comportableNone<FTļUa5Reference to a sparse data-flow graph representation.Ŋ'Models a data-flow graph as a map from Node3s to associated points of their transfer function.bAllocates a new sparse graph a.ababaž (c) Sebastian Graf 2018ISCsgraf1337@gmail.comportableNone&'+<>?FKQTV]hQ†dlExpresses that iteration should or shouldn't stop after a point has been iterated a finite number of times.enWill keep on iterating until a precise, yet conservative approximation has been reached. Make sure that your domain satisfies the  7https://en.wikipedia.org/wiki/Ascending_chain_conditionascending chain condition:, e.g. that fixed-point iteration always comes to a halt!fFor when your domaine doesn't satisfy the ascending chain condition or when you are sensitive about solution performance.The ŋIeger determines the maximum number of iterations of a single point of a 7ƒ (with which an entire function with many points may be associated) before iteration aborts in that point by calling the supplied g. The responsibility of the g[ is to find a sufficiently conservative approximation for the current value at that point. When your  is an instance of Ā, wĸ might be a worthwhile option. A more sophisticated solution would trim the current value to a certain cut-off depth, depending on the first parameter, instead.gļA function that computes a sufficiently conservative approximation of a point in the abstract domain for when the solution algorithm decides to have iterated the node often enough.When domain is a )'BoundedMeetSemilattice'/'BoundedLattice'@, the simplest abortion function would be to constantly return Á.As is the case for 5 and 6x, this carries little semantic meaning if viewed in isolation, so here are a few examples for how the synonym expands: ą AbortionFunction Int ~ Int -> Int AbortionFunction (String -> Int) ~ String -> Int -> Int AbortionFunction (a -> b -> c -> PowerSet) ~ a -> b -> c -> PowerSet -> PowerSet =E.g., the current value of the point is passed in (the tuple (a, b, c, PowerSet)U) and the function returns an appropriate conservative approximation in that point.hSpecifies the density- of the problem, e.g. whether the domain of 73s can be confined to a finite range, in which case † tries to use a  Data.Vector6 based graph representation rather than one based on  Data.IntMap.k)A constraint synonym for the constraints m and its associated / have to suffice.IThis is actually a lot less scary than you might think. Assuming we got  +http://i.cs.hku.hk/~bruno/papers/hs2017.pdfquantified class constraints,  Datafixable" is a specialized version of this: ¯type Datafixable m = ( forall r. Currying (ParamTypes (Domain m)) r , MonoMapKey (Products (ParamTypes (Domain m))) , BoundedJoinSemiLattice (ReturnType (Domain m)) ) Now, let's assume a concrete  Domain m ~ String -> Bool -> Int , so that  (String -> Bool -> Int) expands to the type-level list '[String, Bool] and  '[String, Bool] reduces to (String, Bool)..Then this constraint makes sure we are able to Curry the domain of String -> Bool -> r for all r to e.g. (String, Bool) -> r . See a. This constraint should always be discharged automatically by the type-checker as soon as  and  ReturnTypes reduce for the /1 argument, which happens when the concrete . m is known.,(Actually, we do this for multiple concrete rA because of the missing support for quantified class constraints)We want to use a  0https://en.wikipedia.org/wiki/Monotonic_functionmonotone map of (String, Bool) to Int (the ReturnType (Domain m) ). This is ensured by the  (String, Bool) constraint.uThis constraint has to be discharged manually, but should amount to a single line of boiler-plate in most cases, see .WNote that the monotonicity requirement means we have to pull non-monotone arguments in Domain m into the 7 portion of the 1.nFor fixed-point iteration to work at all, the values which we iterate naturally have to be instances of ÂL. That type-class allows us to start iteration from a most-optimistic Ãj value and successively iterate towards a conservative approximation using the '(/)' operator.lThe iteration state of 'DependencyM'/'solveProblem'.nHConstant. The specification of the data-flow problem we ought to solve.o@Constant. Whether to abort after a number of iterations or not.p,Contextual state. The set of points in the domain of 7s currently in the call stack.q^Constant ref to stateful graph. The data-flow graph, modeling dependencies between data-flow 7%s, or rather specific points in the domain of each 7.rQConstant (but the the wrapped set is stateful). The set of points the currently d node references so far.sOConstant (but the the wrapped queue is stateful). Unstable nodes that will be  d by the …list algorithm.t The concrete . for this worklist-based solver.JThis essentially tracks the current approximation of the solution to the 1 as mutable state while †G makes sure we will eventually halt with a conservative approximation.uWhy does this use Ä? Actually, we only need STS here, but that means we have to carry around the state thread in type signatures.;This ultimately leaks badly into the exported interface in †W: Since we can't have universally quantified instance contexts (yet!), we can' write w(forall s. Datafixable (DependencyM s graph domain)) => (forall s. DataFlowProblem (DependencyM s graph domain)) -> ...+ and have to instead have the isomorphic …(forall s r. (Datafixable (DependencyM s graph domain) => r) -> r) -> (forall s. DataFlowProblem (DependencyM s graph domain)) -> ...0 and urge all call sites to pass a meaningless Å parameter.bAlso, this means more explicit type signatures as we have to make clear to the type-checker that s@ is universally quantified in everything that touches it, e.g. #Analyses.StrAnal.LetDn.buildProblem from the test suite.So, bottom line: We resort to Ä and ÆE and promise not to launch missiles. In particular, we don't export u. and also there must never be an instance of MonadIO for this.wAborts iteration of a value by Įantly returning the Á element of the assumed Ā of the .4The first of the two major functions of this module.recompute node args" iterates the value of the passed node at the point argsK by invoking its transfer function. It does so in a way that respects the d.5This function is not exported, and is only called by … and €4, for when the iteration strategy decides that the nodeF needs to be (and can be) re-iterated. It performs tracking of which 7Vs the transfer function depended on, do that the worklist algorithm can do its magic.šCompute an optimistic approximation for a point of a given node that is as precise as possible, given the other points of that node we already computed.#E.g., it is always valid to return Ö from this, but in many cases we can be more precise since we possibly have computed points for the node that are lower bounds to the current point.‚scheme 1 (see  Œhttps://github.com/sgraf812/journal/blob/09f0521dbdf53e7e5777501fc868bb507f5ceb1a/datafix.md.html#how-an-algorithm-that-can-do-3-looks-like).-Let the worklist algorithm figure things out.ƒscheme 2 (see  Œhttps://github.com/sgraf812/journal/blob/09f0521dbdf53e7e5777501fc868bb507f5ceb1a/datafix.md.html#how-an-algorithm-that-can-do-3-looks-like). Descend into \bot nodes when there is no cycle to discover the set of reachable nodes as quick as possible. Do *not* descend into unstable, non-(bot) nodes.„As long as the supplied Maybeb expression returns "Just _", the loop body will be called and passed the value contained in the Č. Results are discarded. Taken from .…ODefined as 'work = whileJust_ highestPriorityUnstableNode (uncurry recompute)'.Tries to dequeue the } and s the value of one of its sV points, until the worklist is empty, indicating that a fixed-point has been reached.†%Computes a solution to the described 1A by iterating transfer functions until a fixed-point is reached.AIt does do by employing a worklist algorithm, iterating unstable 7 s only. 7,s become unstable when the point of another 7 their transfer function € ed changed.The sole initially unstable 7% is the last parameter, and if your domain% is function-valued (so the returned U expands to a function), then any further parameters specify the exact point in the 7+s transfer function you are interested in.1If your problem only has finitely many different 7s , consider using the F API (e.g. - + ‡6) for a higher-level API that let's you forget about 7Ms and instead let's you focus on building more complex data-flow frameworks.‡evalDenotation denot ib returns a value in domain& that is described by the denotation denot.It does so by building up the 1 corresponding to denot) and solving the resulting problem with †e, the documentation of which describes in detail how to arrive at a stable denotation and what the d ib is for.ˆThis allows us to solve &MonadDependency m => DataFlowProblem m descriptions with †. The /$ is extracted from a type parameter.†The description of the DataFlowProblem to solve.,Describes if the algorithm is free to use a j, Vector1-based graph representation or has to go with a i one based on IntMap.kWhether the solution algorithm should respect a maximum bound on the number of iterations per point. Pass e if you don't care.The Node? that is initially assumed to be unstable. This should be the Node you are interested in, e.g. Node 42( if you are interested in the value of fib 42 for a hypothetical  fibProblem , or the NodeP denoting the root expression of your data-flow analysis you specified via the DataFlowProblem.‡oA build plan for computing the denotation, possibly involving fixed-point iteration factored through calls to -.kWhether the solution algorithm should respect a maximum bound on the number of iterations per point. Pass e if you don't care.$dfeghijklmsronqptuvwxyz{|}~€‚ƒ„…†‡%tulmnopqrsvkˆhijgwdefxyz{|}~€‚ƒ„…†‡defhijlmnopqrstu(c) Sebastian Graf 2018ISCsgraf1337@gmail.comportableNoneSū defhjikt†‡ tkhijdef†‡(c) Sebastian Graf 2018ISCsgraf1337@gmail.comportableNone <>?FTVUĻ8 !"#$%&'(,-./0123456789:;<@ABFGdefhjikt†‡(c) Sebastian Graf 2018ISCsgraf1337@gmail.comportableNoneW–É !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPPQRSTUVWXYZ[\]^_`abcde ffghiijklmnopqrsrs t u v w x y z { | | } ~  €  ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ Œ  Ž I   ‘  ’ “ ” • – — ˜™š›œžŸ ĄĸŖ¤ Ĩ Ļ < §      " # % $ & ( ¨ Š     Ē " Ģ Ĩ Ŧ ­ Ž ¯ ° ą Ŧ ˛ ŗĸ´ĩļšēXģ^ŧĸŖŊžŋļšœÁœÁÜÁĜÁÅļšÆĸŖĮĸČÉĸŖĘĸŖËĖ&datafix-0.0.0.1-3Z24QEWnNOCIXTe7BKF8o1Datafix.MonoMapDatafix.Utils.TypeLevelDatafix.DescriptionDatafix.NodeAllocatorDatafix.ProblemBuilderDatafix.Worklist.GraphDatafix.Worklist.Graph.DenseDatafix.Worklist.Graph.SparseDatafix.Worklist.InternalDatafix.IntArgsMonoMapDatafix.IntArgsMonoSetDatafix.Utils.GrowableVector Data.FunctionfixDatafix.Graph.SparseRefDatafix.Graph.DenseDataIOVectorControl.Monad.Loops whileJust_Datafix.WorklistDatafixDatafix.Tutorial MonoMapKeyMonoMapempty singletoninsertdeletelookuplookupLT lookupMin differencekeys insertWithinsertLookupWithKeyupdateLookupWithKeyalteradjust$fMonoMapKeyInt$fMonoMapKey()Apply Constant1 Constant0FunctionCurryinguncurryscurrys ReturnType' ReturnType ParamTypes' ParamTypesIsBaseProductsArrowsConstantConsMap1ConsMap0MapFoldr'FoldrIfAll arrowsAxiom $fCurrying:b $fCurrying:b0 $fCurrying[]b MonadDatafixdatafixMonadDependencyDomaindependOnDataFlowProblemDFP dfpTransferdfpDetectChange LiftedFuncChangeDetectorNode unwrapNode datafixEqeqChangeDetectoralwaysChangeDetector$fEqNode $fOrdNode $fShowNode NodeAllocator allocateNode runAllocator$fFunctorNodeAllocator$fApplicativeNodeAllocator$fMonadNodeAllocatorProblemBuilder buildProblem$fMonadDatafixmProblemBuilder$fFunctorProblemBuilder$fApplicativeProblemBuilder$fMonadProblemBuilderGraphRef updatePointDiffaddedremoved PointInfovalue references referrers iterationsemptyPointInfo computeDiff$fShowPointInfo $fEqPointInfonewRef $fGraphRefRefIterationBound NeverAbort AbortAfterAbortionFunctionDensitySparseDense DatafixableEnvproblemiterationBound callStackgraphreferencedPointsunstable DependencyMDM initialEnv abortWithTop zoomIORefzoomReferencedPoints zoomUnstableenqueueUnstabledeleteUnstablehighestPriorityUnstableNodewithCall recomputeoptimisticApproximationscheme1scheme2work solveProblemevalDenotation$fMonadDependencyDependencyM$fFunctorDependencyM$fApplicativeDependencyM$fMonadDependencyM%pomaps-0.0.0.4-BqdeXjU65CNCCKrhLO792LData.POMap.InternalPOMap%lattices-1.7.1-5tbMr7doxLiH0VY4J60OofAlgebra.PartialOrd PartialOrdcontainers-0.5.10.2Data.IntMap.InternalIntMapbaseGHC.BaseMaybehighestPriorityNodesIntArgsMonoMapnothingIfEmptyIntArgsMonoSetSetmembertoListGrowableVectornewlengthpushBackwritefreezebufferlenGHC.Errerrorghc-prim GHC.ClassesEq GHC.TypesTrueunwrapNodeAllocatorunwrapProblemBuilderNothingPointMapGraphIntAlgebra.LatticeBoundedMeetSemiLatticetopBoundedJoinSemiLatticebottomIOid GHC.IO.UnsafeunsafePerformIOconstJust