//
// Souffle - A Datalog Compiler
// Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved
// Licensed under the Universal Permissive License v 1.0 as shown at:
// - https://opensource.org/licenses/UPL
// - <souffle root>/licenses/SOUFFLE-UPL.txt

//
// Computes the summary flow functions (Fsgen and Fskill) using the following equation:

// out[s] = Fsgen[S] \/ (initial[start] - Fskill[S]), start is the start node
//
// Fskill[S] = Fskill[P] \/ kill[S], P is predecessor of S
//
// Fsgen[S] = [ Fsgen[P] - Fskill[p] ] \/ gen[S],  P is predecessor of S
//


.comp Graph<Node>{

      //
      // input relations
      //

      //
      // Set of edges
      //
      .decl edge(src:Node,des:Node)

      //
      // Start node
      //
      .decl start(node:Node)


      //
      // intermediate relations
      //

      //
      // Set of nodes
      //
      .decl node(node:Node)

      node(node) :- edge(node,_).
      node(node) :- edge(_,node).

}

.comp Gen_Kill_Summary<Node,InfoLattice>{

      //
      // input relations
      //

      //
      // Gen function
      //
      .decl gen(node:Node,inf:InfoLattice)

      //
      // Kill function
      //
      .decl kill(node:Node,inf:InfoLattice)

      //
      // start node value
      //
      .decl startvalue(inf:InfoLattice)


      //
      // output relations
      //

      //
      // Out
      //
      .decl out(node:Node,inf:InfoLattice)

}

.comp Gen_Kill_Union_Summary<Node,InfoLattice> : Gen_Kill_Summary<Node,InfoLattice>, Graph<Node>{

     //
     // Kill summary function
     //
     .decl fs_kill(node:Node,inf:InfoLattice)

     //
     // Gen summary function
     //
     .decl fs_gen(node:Node,inf:InfoLattice)

     //
     // Rules for fs_gen
     //
     fs_gen(node,inf) :- gen(node,inf).
     fs_gen(node,inf) :- fs_gen(pred,inf), !kill(node,inf), edge(pred,node).


     //
     // Rules for fs_kill
     //
     fs_kill(node,inf) :- kill(node,inf).
     fs_kill(node,inf) :- fs_kill(pred,inf), edge(pred,node).


     //
     // output relations
     //

     out(node,inf) :- fs_gen(node,inf).
     out(node,inf) :- node(node), startvalue(inf), !fs_kill(node,inf).

}


.comp Gen_Kill_Intersection_Summary<Node,InfoLattice> : Gen_Kill_Summary<Node,InfoLattice>, Graph<Node>{


      .init   gen_kill_union = Gen_Kill_Union_Summary<Node,InfoLattice>

      gen_kill_union.start(n) :- start(n).

      gen_kill_union.edge(x,y) :- edge(x,y).

      gen_kill_union.gen(node,inf) :- kill(node,inf), !gen(node,inf).

      gen_kill_union.kill(node,inf) :- gen(node,inf).

      gen_kill_union.startvalue(inf) :- startvalue(inf).


      //
      // output relations
      //
      out(node,inf) :- node(node), universe(inf), !gen_kill_union.out(node,inf).
}


.comp Gen_Kill_Union_Summary_Backward<Node,InfoLattice> : Gen_Kill_Summary<Node,InfoLattice>, Graph<Node>{


      .init gen_kill_union = Gen_Kill_Union_Summary<Node,InfoLattice>


      //
      //  in
      //

      gen_kill_union.start(n) :- start(n).

      gen_kill_union.edge(src,des) :- edge(des,src).

      gen_kill_union.gen(node,inf) :- gen(node,inf).

      gen_kill_union.kill(node,inf) :- kill(node,inf).

      gen_kill_union.startvalue(inf) :- startvalue(inf).

      //
      // output relations
      //

      out(node,inf) :- gen_kill_union.out(node,inf).
}

.comp Gen_Kill_Intersection_Summary_Backward<Node,InfoLattice> : Gen_Kill_Summary<Node,InfoLattice>, Graph<Node>{


      //
      // input relations
      //


      .init gen_kill_intersection = Gen_Kill_Intersection_Summary<Node,InfoLattice>


      //
      //  in
      //
      gen_kill_intersection.start(n) :- start(n).

      gen_kill_intersection.edge(src,des) :- edge(des,src).

      gen_kill_intersection.gen(node,inf) :- gen(node,inf).

      gen_kill_intersection.kill(node,inf) :- kill(node,inf).

      gen_kill_intersection.startvalue(inf) :- startvalue(inf).

      //
      // output relations
      //

      out(node,inf) :- gen_kill_intersection.out(node,inf).

}


//
// Set of Edge
//

.decl edge(x:number, y:number)
.input edge()

//
// Gen sets
//

.decl gen(x:number, i:symbol)
.input gen()

//
// Use sets
//

.decl use(x:number, i:symbol)
.input use()

//
// Kill sets
//

.decl kill(x:number, i:symbol)
.input kill()


//
// Universe  Lattice
//
.decl universe(inf:symbol)
universe(inf) :- gen(_,inf).
universe(inf) :- kill(_,inf).


//
// Instantiates data flow analysis  components and tests example 2.5 in
// "Data Flow Analysis" book authored by P.Khedker et al.
//

.init Available_Expressions = Gen_Kill_Intersection_Summary<number,symbol>

Available_Expressions.edge(x,y) :- edge(x,y).
Available_Expressions.gen(x,i) :- gen(x,i).
Available_Expressions.kill(x,i) :- kill(x,i).
//Available_Expressions.startvalue(i) :- gen(1,i), !kill(1,i).
Available_Expressions.startvalue(inf) :-  universe(inf),!kill(1,inf).
Available_Expressions.start(1).



.init Reaching_Definitions = Gen_Kill_Union_Summary<number,symbol>

Reaching_Definitions.edge(x,y) :- edge(x,y).
Reaching_Definitions.gen(x,i) :- gen(x,i).
Reaching_Definitions.kill(x,i) :- kill(x,i).
Reaching_Definitions.startvalue(i) :- gen(1,i), !kill(1,i).
Reaching_Definitions.start(1).




.init Live_Variables = Gen_Kill_Union_Summary_Backward<number,symbol>

Live_Variables.edge(x,y) :- edge(x,y).
Live_Variables.gen(x,i) :- use(x,i).
Live_Variables.kill(x,i) :- kill(x,i).
Live_Variables.startvalue(i) :- use(8,i), !kill(8,i).
Live_Variables.start(8).


//
// Output
//

.decl available_expressions(n:number, i:symbol)
.output available_expressions()
available_expressions(x,i) :- Available_Expressions.out(x,i).


.decl reaching_definitions(n:number, i:symbol)
.output reaching_definitions()
reaching_definitions(x,i) :- Reaching_Definitions.out(x,i).


.decl live_variables(n:number, i:symbol)
.output live_variables()
live_variables(x,i) :- Live_Variables.out(x,i).

