From 51d354ce61da0edb59d5f1c2f47f47d406aee9db Mon Sep 17 00:00:00 2001
From: Regis Blanc <regwblanc@gmail.com>
Date: Mon, 11 Jan 2016 17:57:14 +0100
Subject: [PATCH] progress on effect analysis

---
 .../scala/leon/xlang/AntiAliasingPhase.scala  | 183 ++++++++++++++----
 1 file changed, 145 insertions(+), 38 deletions(-)

diff --git a/src/main/scala/leon/xlang/AntiAliasingPhase.scala b/src/main/scala/leon/xlang/AntiAliasingPhase.scala
index d1fe18eb9..e6466d799 100644
--- a/src/main/scala/leon/xlang/AntiAliasingPhase.scala
+++ b/src/main/scala/leon/xlang/AntiAliasingPhase.scala
@@ -9,9 +9,9 @@ import leon.purescala.Definitions._
 import leon.purescala.Expressions._
 import leon.purescala.ExprOps._
 import leon.purescala.DefOps._
-import leon.purescala.Extractors.IsTyped
 import leon.purescala.Types._
 import leon.purescala.Constructors._
+import leon.purescala.Extractors._
 import leon.xlang.Expressions._
 
 object AntiAliasingPhase extends TransformationPhase {
@@ -23,27 +23,30 @@ object AntiAliasingPhase extends TransformationPhase {
 
     var updatedFunctions: Map[FunDef, (FunDef, Seq[ValDef])] = Map()
 
+    println("effects: " + effectsAnalysis(pgm).filter(p => p._2.size > 0).map(p => p._1.id + ": " + p._2))
+
     for {
       fd <- pgm.definedFunctions
     } {
-      updateFunDef(fd).foreach{ case (nfd, mvs) => {
+      updateFunDef(fd)(ctx).foreach{ case (nfd, mvs) => {
         updatedFunctions += (fd -> ((nfd, mvs)))
       }}
     }
-    println(updatedFunctions)
+
+    //println(updatedFunctions)
 
     for {
       fd <- pgm.definedFunctions
     } {
-      fd.body = fd.body.map(bd => updateInvocations(bd, updatedFunctions))
+      fd.body = fd.body.map(bd => updateInvocations(bd, updatedFunctions)(ctx))
     }
 
     val res = replaceFunDefs(pgm)(fd => updatedFunctions.get(fd).map(_._1), (fi, fd) => None)
-    println(res._1)
+    //println(res._1)
     res._1
   }
 
-  private def updateInvocations(body: Expr, updatedFunctions: Map[FunDef, (FunDef, Seq[ValDef])]): Expr = {
+  private def updateInvocations(body: Expr, updatedFunctions: Map[FunDef, (FunDef, Seq[ValDef])])(ctx: LeonContext): Expr = {
     val freshBody = postMap {
       case fi@FunctionInvocation(fd, args) => updatedFunctions.get(fd.fd).map{ case (nfd, modifiedParams) => {
         val modifiedArgs: Seq[Variable] = 
@@ -66,11 +69,7 @@ object AntiAliasingPhase extends TransformationPhase {
     freshBody
   }
 
-  private def updateFunDef(fd: FunDef): Option[(FunDef, Seq[ValDef])] = {
-    var isAliasing = false
-    //TODO: in the future, any object with vars could be aliased and so
-    //      we will need a general property
-
+  private def updateFunDef(fd: FunDef)(ctx: LeonContext): Option[(FunDef, Seq[ValDef])] = {
     val aliasedParams: Seq[Identifier] = 
       fd.params.filter(vd => vd.getType match {
         case ArrayType(_) => {
@@ -85,33 +84,37 @@ object AntiAliasingPhase extends TransformationPhase {
       }).map(_.id)
 
 
-    if(aliasedParams.isEmpty) None else {
+    val explicitBody = fd.body.map(bd => makeSideEffectsExplicit(bd, aliasedParams)(ctx))
+
+    if(aliasedParams.isEmpty) {
+      None
+    } else {
       val freshLocalVars: Seq[Identifier] = aliasedParams.map(v => v.freshen)
       val rewritingMap: Map[Identifier, Identifier] = aliasedParams.zip(freshLocalVars).toMap
 
       val newBody = fd.body.map(body => {
 
-        val freshBody = postMap {
-          case au@ArrayUpdate(a, i, v) =>
-            val Variable(id) = a
-            rewritingMap.get(id).map(freshId =>
-              Assignment(freshId, ArrayUpdated(freshId.toVariable, i, v).setPos(au)).setPos(au)
-            )
-          case _ => None
-          //case as@ArraySelect(a, i) =>
+        //val freshBody = postMap {
+        //  case au@ArrayUpdate(a, i, v) =>
+        //    val Variable(id) = a
+        //    rewritingMap.get(id).map(freshId =>
+        //      Assignment(freshId, ArrayUpdated(freshId.toVariable, i, v).setPos(au)).setPos(au)
+        //    )
+        //  case _ => None
+        //  //case as@ArraySelect(a, i) =>
 
-          //case l@Let(i, IsTyped(v, ArrayType(_)), b) =>
-          //  LetVar(i, v, b).setPos(l)
-        }(body)
+        //  //case l@Let(i, IsTyped(v, ArrayType(_)), b) =>
+        //  //  LetVar(i, v, b).setPos(l)
+        //}(body)
 
-        val freshBody2 = replaceFromIDs(rewritingMap.map(p => (p._1, p._2.toVariable)), freshBody) 
+        val freshBody = replaceFromIDs(rewritingMap.map(p => (p._1, p._2.toVariable)), body) 
 
         //WARNING: only works if side effects in Tuples are extracted from left to right,
         //         in the ImperativeTransformation phase.
-        val finalBody: Expr = Tuple(freshBody2 +: freshLocalVars.map(_.toVariable))
+        val finalBody: Expr = Tuple(freshBody +: freshLocalVars.map(_.toVariable))
 
         freshLocalVars.zip(aliasedParams).foldLeft(finalBody)((bd, vp) => {
-          LetVar(vp._1, Variable(vp._2), bd)
+          Let(vp._1, Variable(vp._2), bd)
         })
 
       })
@@ -134,6 +137,7 @@ object AntiAliasingPhase extends TransformationPhase {
 
       val newFunDef = new FunDef(fd.id.freshen, fd.tparams, fd.params, newReturnType)
       newFunDef.addFlags(fd.flags)
+      newFunDef.setPos(fd)
       newFunDef.body = newBody
       newFunDef.precondition = fd.precondition
       newFunDef.postcondition = newPostcondition
@@ -142,20 +146,123 @@ object AntiAliasingPhase extends TransformationPhase {
     }
   }
 
+  //We turn all local val of mutable objects into vars and explicit side effects
+  //using assignments. We also make sure that no aliasing is being done.
+  private def makeSideEffectsExplicit(body: Expr, aliasedParams: Seq[Identifier])(ctx: LeonContext): Expr = {
+    preMapWithContext[Set[Identifier]]((expr, bindings) => expr match {
+
+      case up@ArrayUpdate(a, i, v) => {
+        val ra@Variable(id) = a
+        if(bindings.contains(id))
+          (Some(Assignment(id, ArrayUpdated(ra, i, v).setPos(up)).setPos(up)), bindings)
+        else
+          (None, bindings)
+      }
+
+      case l@Let(id, IsTyped(v, ArrayType(_)), b) => {
+        v match {
+          case FiniteArray(_, _, _) => ()
+          case FunctionInvocation(_, _) => ()
+          case _ => ctx.reporter.fatalError(l.getPos, "Cannot alias array")
+        }
+
+        val varDecl = LetVar(id, v, b).setPos(l)
+        (Some(varDecl), bindings + id)
+      }
+
+      case l@LetVar(id, IsTyped(v, ArrayType(_)), b) => {
+        v match {
+          case FiniteArray(_, _, _) => ()
+          case FunctionInvocation(_, _) => ()
+          case _ => ctx.reporter.fatalError(l.getPos, "Cannot alias array")
+        }
+
+        (None, bindings + id)
+      }
+        
+      case _ => (None, bindings)
+    })(body, Set())
+  }
+
+  //TODO: in the future, any object with vars could be aliased and so
+  //      we will need a general property
+
+  private type Effects = Map[FunDef, Set[Int]]
+
+  private def effectsAnalysis(pgm: Program): Effects = {
+
+    //currently computed effects (incremental)
+    var effects: Map[FunDef, Set[Int]] = Map()
+    //missing dependencies for a function for its effects to be complete
+    var missingEffects: Map[FunDef, Set[FunctionInvocation]] = Map()
+
+    def effectsFullyComputed(fd: FunDef): Boolean = !missingEffects.isDefinedAt(fd)
 
-  //private def rec(expr: Expr): Expr = {
-  //  simplePostTransform {
-  //    case fi@FunctionInvocation(tfd, args) =>
-  //      
+    for {
+      fd <- pgm.definedFunctions
+      body <- fd.body
+    } {
+
+      val mutableParams = fd.params.filter(vd => vd.getType match {
+        case ArrayType(_) => true
+        case _ => false
+      })
+      val mutatedParams = mutableParams.filter(vd => exists {
+        case ArrayUpdate(Variable(a), _, _) => a == vd.id
+        case _ => false
+      }(body))
+      val mutatedParamsIndices = fd.params.zipWithIndex.flatMap{
+        case (vd, i) if mutatedParams.contains(vd) => Some(i)
+        case _ => None
+      }.toSet
+      effects = effects + (fd -> mutatedParamsIndices)
+
+      val missingCalls: Set[FunctionInvocation] = functionCallsOf(body).filterNot(fi => fi.tfd.fd == fd)
+      if(missingCalls.nonEmpty)
+        missingEffects += (fd -> missingCalls)
+    }
 
-  //  }(expr)
-  //}
+    def rec(): Unit = {
+      val previousMissingEffects = missingEffects
+
+      for{ (fd, calls) <- missingEffects } {
+        var newMissingCalls: Set[FunctionInvocation] = calls
+        for(fi <- calls) {
+          val mutatedArgs = invocEffects(fi)
+          val mutatedFunParams: Set[Int] = fd.params.zipWithIndex.flatMap{
+            case (vd, i) if mutatedArgs.contains(vd.id) => Some(i)
+            case _ => None
+          }.toSet
+          effects += (fd -> (effects(fd) ++ mutatedFunParams))
+
+          if(effectsFullyComputed(fi.tfd.fd)) {
+            newMissingCalls -= fi
+          }
+        }
+        if(newMissingCalls.isEmpty)
+          missingEffects = missingEffects - fd
+        else
+          missingEffects += (fd -> newMissingCalls)
+      }
+
+      if(missingEffects != previousMissingEffects) {
+        rec()
+      }
+    }
+
+
+    def invocEffects(fi: FunctionInvocation): Set[Identifier] = {
+      val mutatedParams: Set[Int] = effects(fi.tfd.fd)
+      fi.args.zipWithIndex.flatMap{
+        case (Variable(id), i) if mutatedParams.contains(i) => Some(id)
+        case _ => None
+      }.toSet
+    }
+
+
+    rec()
+    effects
+  }
 
-  
-  /*
-   * It seems ArrayTransformation is really a solution to local aliasing, by changing
-   * val to var. This AntiAliasingPhase should try to generalize this solution
-   * accross function calls.
-   */
 
 }
-- 
GitLab