Presentation is loading. Please wait.

Presentation is loading. Please wait.

Dataflow Analysis, cont.

Similar presentations


Presentation on theme: "Dataflow Analysis, cont."— Presentation transcript:

1 Dataflow Analysis, cont.

2 Announcements HW1 is posted, due January 28th
You can work individually or in teams of 2 Set up teams in Submitty Ask questions on forum! Upload in Submitty Change in office hours: Wed Noon-2pm or by appointment Spring 19 CSCI 4450/6450, A Milanova

3 Outline of Today’s Class
Building CFG from 3-address code Local analysis vs. global analysis The four classical dataflow analysis problems Reaching definitions Live variables Available expressions Very busy expressions Reading: Dragon Book, Chapter 9.2 Spring 19 CSCI 4450/6450, A Milanova

4 Building the Control Flow Graph
sum = 0 i = 1 if i > n goto 15 t1 = addr(a) – 4 t2 = i*4 t3 = t1[t2] t4 = addr(a) – 4 t5 = i*4 t6 = t5[t5] t7 = t3*t6 t8 = sum + t7 sum = t8 i = i + 1 goto 3 sum = 0 i = 1 if i > n goto 15 t1 = addr(a) – 4 t2 = i*4 t3 = t1[t2] t4 = addr(a) – 4 t5 = i*4 t6 = t4[t5] t7 = t3*t6 t8 = sum + t7 sum = t8 i = i + 1 goto 3 T 15. … F The Nodes in the CFG are Basic Blocks. The edges represent transfer of control from one basic block to another. Basic block is the maximal sequence of consecutive three-address code statements: 1. Control can only enter at the first statement. There are no jumps in the middle of the block. 2. Control can only exit at the last statement in the basic block. Spring 19 CSCI 4450/6450, A Milanova

5 Building the Control Flow Graph
Build the CFG from linear 3-address code: Step 1: partition code into basic blocks Basic blocks are the nodes of the CFG Step 2: add control flow edges Aside: in Principles of Software, we built a CFG from “high-level” structural program representation: S ::= x = y Op z | if (B) then S else S | while (B) S | S;S

6 Step 1. Partition Code Into Basic Blocks
1. Determine the leader statements: (i) First program statement (ii) Targets of conditional or unconditional goto’s (iii) Any statement following a goto 2. For each leader, its basic block consists of the leader and all statements up to, but not including, the next leader or the end of the program Spring 19 CSCI 4450/6450, A Milanova

7 Question. Find the Leader Statements
sum = 0 i = 1 if i > n goto 15 t1 = addr(a) – 4 t2 = i*4 t3 = t1[t2] t4 = addr(a) – 4 t5 = i*4 t6 = t5[t5] t7 = t3*t6 t8 = sum + t7 sum = t8 i = i + 1 goto 3 sum = 0 i = 1 if i > n goto 15 t1 = addr(a) – 4 t2 = i*4 t3 = t1[t2] t4 = addr(a) – 4 t5 = i*4 t6 = t5[t5] t7 = t3*t6 t8 = sum + t7 sum = t8 i = i + 1 goto 3 Which statements are the leader statements? 1,3,4 and 15. Why? 1 is the First program statement, and therefore it is a leader. 3 is the target of “goto 3” at line 14 4 follows the “goto 15” at 3. 15 is the target of the goto 15 at 3. It also follows the goto 3 at line 14. Spring 19 CSCI 4450/6450, A Milanova

8 Step 2. Add Control Flow Edges
There is a directed edge from basic block B1 to block B2 if B2 can immediately follow B1 in some execution sequence Determine edges as follows: There is an edge from B1 to B2 if B2 follows B1 in three address code, and B1 does not end in an unconditional goto There is an edge from B1 to B2 if there is a goto from the last statement in B1 to the first statement in B2 Dead code. Spring 19 CSCI 4450/6450, A Milanova

9 Question. Add Control Flow Edges
sum = 0 i = 1 if i > n goto 15 t1 = addr(a) – 4 t2 = i*4 t3 = t1[t2] t4 = addr(a) – 4 t5 = i*4 t6 = t5[t5] t7 = t3*t6 t8 = sum + t7 sum = t8 i = i + 1 goto 3 Spring 19 CSCI 4450/6450, A Milanova

10 Local Analysis vs. Global Analysis
Local analysis: analysis within basic block Enables optimizations such as local common subexpression elimination, dead code elimination, constant propagation, copy propagation, etc. Global analysis: beyond the basic block Enables optimizations such as global common subexpression elimination, dead code elimination, constant propagation, loop optimizations, etc. Global analysis refers to analysis beyond a basic block. The scope of the global analysis can be one procedure, a set of procedures, a class, a package, the entire program, a program plus standard libraries, etc. For the lectures that follow, I use “global analysis” to mean analysis within one procedure. Spring 19 CSCI 4450/6450, A Milanova

11 Local Analysis: Local Common Subexpression Elimination
1.a = y+2 y+2 is “available” in a after execution of statement 1 2.z = x+w y+2 in a, x+w in z 3.x = y+2 y+2 (y+2 is available in a, but x+w is no longer available) 4.z = b+c y+2, b+c 5.b = y+2 y+2 (y+2 is available in a, but b+c is no longer available) Here the compiler performs (local) dataflow analysis which determines that expression y+2 is “available” at 3 (i.e., it has been computed earlier), and there is no point to re-compute it at 3. This particular dataflow analysis propagates dataflow facts called “available expressions” along the execution chain. We have that y+2 is available right after 1 because it has just been computed at 1. Etc… At 3, y+2 is available, and the analysis concludes that we can substitute “a” for y+2. Spring 19 CSCI 4450/6450, A Milanova

12 Question. Run Local Common Subexpression Elimination
t2 = a [ t1 ] t3 = 4 * i t4 = b [ t3 ] t5 = t2 * t4 t6 = prod * t5 prod = t6 t7 = i + 1 i = t7 if i <= 20 goto 1 We can perform Common Subexpression Elimination and eliminate 3 t3 = 4*i. We can perform Copy propagation at 6-7: prod = prod*t5 We can perform Copy propagation at 8-9: I = i+1 Spring 19 CSCI 4450/6450, A Milanova

13 Local Analysis: Dead Code Elimination
1.a = y+2 (a,1) 2.z = x+w (a,1),(z,2) 3.x = a (a,1),(z,2),(x,3) 4.z = b+c (a,1),(x,3),(z,4) z is redefined at 4, and was never used on the way from 2 to 4; thus 2.z=x+w is “dead code” 5.b = a (a,1),(x,3),(z,4), (b,5) In this case, the compiler uses (local) dataflow analysis to assert that statement 2 is dead code. In this case, the analysis propagates dataflow facts called “reaching definitions”. (a,1) denotes the definition of variable “a” at line 1. Spring 19 CSCI 4450/6450, A Milanova

14 After Local Common Subexpression and Dead Code Elimination
a = y ’. a = y + 2 z = x + w 2’. x = a x = y ’. z = b + c z = b + c 4’. b = a b = y + 2 2. z = x+w is dead code because z is recomputed at line 4. The value computed at 2 is not used anywhere. z is recomputed at 4, and the value at 4 is the one exposed to outside of the basic block. Spring 19 CSCI 4450/6450, A Milanova

15 Local Constant Propagation
t1 = 1 Assume a, k, t3, and t4 are used beyond basic block: a = t1 1’. a = 1 t2 = 1 + a 2’. k = 2 k = t2 3’. t4 = 8.2 t3 = cvttoreal(k) 4’. t3 = 8.2 t4 = t3 t3 = t4 David Gries’ algorithm: Process 3-address statements in order Check if operand is constant; if so, substitute If all operands are constant: Do operation, and add (LHS,value) to map If not all operands constant: Delete (LHS,value) entry from map

16 Arrays and Pointers Make Things Harder
Consider: 1. x = a[k]; 2. a[j] = y; 3. z = a[k]; Can we transform this code into: 3. z = x; Does the store into a[j] affect the load of a[k]? An assignment through an array or a pointer can kill all the nodes. Also, a procedure call in a basic block kills all the nodes. Introducing ordering edges 1. any evaluation of or assignment to an element of array “a” must follow the previous assignment to an element of that array if there is one. 2. any assignment to an element of array “a” must follow any previous evaluation of “a”. 3. any use of any identifier must follow the previous procedure call or indirect pointer assignment. 4. any proc. Call or indirect assignment must follow all previous evaluations of any identifier. We can do a similar fix for variable access through pointers. Spring 19 CSCI 4450/6450, A Milanova

17 Local Analysis vs. Global Analysis
Local analysis is generally easy – a single path from basic block entry to basic block exit Global analysis is hard – multiple paths, across basic blocks Control flow splits and merges at if-then-else Loops! Local analysis refers to analysis of one basic block. This analysis is “easy” because control flow is linear --- control enters the basic block at the first statement, executes the statements in the basic block in order, and exits at the last. Global analysis is “difficult” because usually there are multiple execution paths from the start of the procedure to any given point N in the procedure (in fact, usually, there are infinitely many paths from start to N). The analysis must take into account _every_ execution path. The analysis tries to prove properties that hold at N such as for example, “At point N, variable i always has the value of 5“. In order to prove such a property, the analysis must assert that the property holds along any execution path from the start of the procedure to N. Suppose for example, that we have designed an analysis that asserted that at point N: x = i*i, i is always 5; however, the analysis is incorrect, because there is one path such that if execution took this path i would be 0 at N. If the compiler uses this analysis, it will conclude that it was safe to substitute the code at N with x = 25. But this is wrong of course! Spring 19 CSCI 4450/6450, A Milanova

18 Dataflow Analysis Collects information for all inputs, along all execution paths Control splits and control merges Loops (control goes back) Dataflow analysis is a powerful framework We can define many different kinds of dataflow analysis Dafaflow analysis is a framework (general technique) for collecting information about the flow of data along execution paths. Spring 19 CSCI 4450/6450, A Milanova

19 Dataflow Analysis Control-flow graph (CFG): G = (N, E, 1)
Nodes are basic blocks Data Dataflow equations out(j) = (in(j) – kill(j)) gen(j) (gen and kill are parameters) Merge operator V in(j) = V out(i) i is predecessor of j Entry node: 1 2 3 4 5 6 How do we write a specific dataflow analysis? First, we have to define what kind of data it will be collecting: available expressions, reaching definitions, something else (pretty much anything!). Second, we have to define the data-flow equations. There is a data-flow equation out(i) = gen(i) U (in(i)-kill(i)) associated to each basic block (i.e., node in the CFG). This data-flow equation reflects the effect of the basic block on the data that we are collecting: each statement generates some new data, and “kills” some incoming data. gen and kill are parameters to the framework. Third, we choose the merge operator V: how do we combine data coming from different paths at merge nodes? There is another equation in(i) = V out(j) --- it collects the incoming data into node i by merging (appropriately) outgoing data over all of i’s predecessors. 7 Exit node: 8 9 10

20 Four Classical Dataflow Problems
Reaching definitions (Reach) Live uses of variables (Live) Available expressions (Avail) Very busy expressions (VeryB) Reach and the dual Live enable several classical optimizations such as dead code elimination, as well as dataflow-based testing Avail enables global common subexpression elimination VeryB enables conservative code motion Spring 19 CSCI 4450/6450, A Milanova

21 Reaching Definitions Definition A statement that may change the value of a variable (e.g., x=y+z) (x,k) denotes definition of x at node k A definition (x,k) reaches node n if there is a path from k to n, free of a definition of x This slide defines the terms “definition” and “reaches”. k x = … x = … n … = x Spring 19 CSCI 4450/6450, A Milanova

22 Live Uses of Variables Use Appearance of a variable as an operand of a 3-address statement (e.g., x in y=x+4) A use of a variable x at node n is live on exit from k, if there is a path from k to n clear of definition of x Defines “use” and “live on exit”. k x = … x = … n … = x Spring 19 CSCI 4450/6450, A Milanova

23 Def-use Relations Use-def chain links a use of x to a definition of x that reaches that use Def-use chain links a definition to a use that it reaches k x = … x = … n … = x Spring 19 CSCI 4450/6450, A Milanova

24 Def-use Enable Optimizations
Dead code elimination (Def-use) Code motion (Use-def) Constant propagation (Use-def) Strength reduction (Use-def) Test elision (Use-def) Copy propagation (Def-use) Aside: Def-use enables dataflow-based testing. (We mentioned in Principles)

25 Question. What are the Def-use Chains that start at 2?
Answer: (2,3) (2,5) (2,6) 1. sum = 0 2. i = 1 T 3. if i > n goto 15 F 4. t1 = addr(a)–4 … 5. t2 = i * 4 … 6. i = i + 1 What are the def-use chains that start from 2? 2-3 2-5 2-6 Spring 19 CSCI 4450/6450, A Milanova

26 Def-use Enables Dead Code Elimination
1. sum = 0 2. i = 1 T 3. if t2 > t9 goto 15 F 4. t3 = t1[t2] 5. t7 = t3 * t3 6. sum = sum + t7 7. t2 = t2 + 4 After code motion, strength reduction, test elision and constant propagation, the def-use links from 2.i=1 disappear. Thus, 2.i=1 becomes dead code. Spring 19 CSCI 4450/6450, A Milanova

27 Use-def Enables Constant Propagation
What are the use-def chains that originate at 6? 1. i = 1 2. i = 2 Answer: (6,1) (6,5) 3. i = 3 4. p = i*2 5. i = 1 What are the Use-def chains that start at 6? 6-1 6-5 6. q = 5*i+3 = 8 Spring 19 CSCI 4450/6450, A Milanova

28 “Hot” Applications of Def-use
Security! Encryption inference To encrypt data in MapReduce programs E.g., sums weight, BP over patients to compute average. Use of weight, BP is +, thus AH Protocol inference for multi-party computation Different protocols implement operations at different costs Spring 19 CSCI 4450/6450, A Milanova

29 Problem 1. Reaching Definitions (Reach)
Problem statement: for each CFG node n, compute the set of definitions (x,k) that may reach n First, define data (i.e., the dataflow facts) to propagate Primitive dataflow facts are definitions (x,k) Reach propagates sets of definitions, e.g., {(i,1),(p,4)} Spring 19 CSCI 4450/6450, A Milanova

30 Reaching Definitions (Reach)
Next, define the dataflow equations (i.e., effect of code at node j on incoming dataflow facts) kill(j): all definitions of (x,_) gen(j): this definition of x,(x,j) j: x = y+z out(j) = (in(j) – kill(j)) gen(j) When defining the dataflow equations, we need to “fill in” the parameters: the “kill” and “gen” sets for each node. Let us have 3-address statement a = b op c at node j. This statement “kill” all definitions of “a” reaching j. The statement “generates” the new definition (a,j). E.g., if in(4) = {(x,1),(y,2),(x,3)} Node 4 is: x = y+z Then out(4) = {(y,2),(x,4)} j Spring 19 CSCI 4450/6450, A Milanova

31 Reaching Definitions (Reach)
Next, define the merge operator V (i.e., how to combine data from incoming paths) For Reach, V is the set union in(j) = { out(i) | i is predecessor of j } E.g., if out(2) = {(x,1),(y,2)} and out(3) = {(x,3)} and 2 and 3 are predecessors of 4 in(4) = {(x,1),(x,3),(y,2)} When defining the dataflow equations, we need to “fill in” the parameters: the “kill” and “gen” sets for each node. Let us have 3-address statement a = b op c at node j. This statement “kill” all definitions of “a” reaching j. The statement “generates” the new definition (a,j). j Spring 19 CSCI 4450/6450, A Milanova

32 Reach: Dataflow Equations
in(1) = Ø 1.x=5 out(1) = (in(1) - Dx) {(x,1)} in(2) = out (1) 2.y=1 out(2) = (in(2) - Dy) {(y,2)} in(3) = out(2) out(6) 3.x>=2 out(3) = in(3) in(4) = out(3) 4.y=x*y out(4) = (in(4) - Dy) {(y,4)} 5.x=x-1 in(5) = out(4) Here Dx and Dy denote the set of all definitions of x and y, respectively. Dx = { (x,1), (x,5) }. Dy = { (y,2), (y,4) } What are the definitions that reach node 2? Node 3? Node 5? Node 3: { (x,1),(y,2),(x,4),(y,5) }, Node 2: { (x,1) }, Node 5: { (y,4),(x,1),(x,5) } This are the dataflow equations for Reaching Definitions. We will learn how to solve them a little later. The solution of the dataflow equation gives us the reaching definitions information we wanted. out(5) = (in(5) - Dx) {(x,5)} 6.goto 3 in(6) = out(5) out(6) = in(6) 7. … in(7) = out(3)

33 Reach: Solution of Equations
in(1) = Ø 1.x=5 out(1) = {(x,1)} in(2) = {(x,1)} 2.y=1 out(2) = {(x,1), (y,2)} in(3) = {(x,1),(x,5),(y,2),(y,4)} 3.x>=2 out(3) = {(x,1),(x,5),(y,2),(y,4)} in(4) = {(x,1),(x,5),(y,2),(y,4)} 4.y=x*y out(4) = {(x,1),(x,5),(y,4)} in(5) = {(x,1),(x,5),(y,4)} 5.x=x-1 This slide presents the solution of the dataflow equations we defined on the previous slide. out(5) = {(x,5),(y,4)} 6.goto 3 in(6) = {(x,5),(y,4)} in(6) = {(x,5),(y,4)} 7. … in(7) = {(x,1),(x,5),(y,2),(y,4)}

34 Reaching Definitions in(i1) in(i2) in(i3) i1 i2 i3 in(j) j
Forward, may dataflow problem Usually, when we define data-flow equations, we skip the “out”. We define the data-flow equation for in(j) in terms of the in’s of j’s predecessors. Spring 19 CSCI 4450/6450, A Milanova

35 Problem 2. Live Uses of Variables (Live)
We say that a variable x is “live on exit from node j” if there is a live use of x on exit from j (recall the definition of “live use of x on exit from j”) Problem statement: for each node n, compute the set of variables that may be live on exit from n. We say that variable x is “live at exit of node i”, if there is a use of x which is live on exit from i (recall the definition of live use of x). Or in other words, x is live at exit from i, if there is a path from i to some use of x at n, and the path is free of a definition of x. Variable x is not live at the exit from label 1. the first assignment is redundant. Both x and y are live at the exit of 3. 1. x=2; 2. y=4; 3. x=1; if (y>x) then 5. z=y; else 6. z=y*y; 7. x=z; What variables are live on exit from statement 3? Statement 1?

36 Live Uses of Variables (Live)
Problem statement: for each node n, compute the set of variables that may be live on exit from n. inLV(j)= (outLV(j) – killLV(j)) genLV(j) j: x = y+z outLV(j) = { inLV(i) | i is a successor of j } We say that variable x is “live at exit of node i”, if there is a use of x which is live on exit from i (recall the definition of live use of x). Or in other words, x is live at exit from i, if there is a path from i to some use of x at n, and the path is free of a definition of x. Variable x is not live at the exit from label 1. the first assignment is redundant. Both x and y are live at the exit of 3. Q: What are the primitive dataflow facts? Q: What is genLV(j)? Q: What is killLV(j)? Spring 19 CSCI 4450/6450, A Milanova

37 Live Example 1.x=2 2.y=4 3.x=1 4.(y>x) T F 5.z=y 6.z=y*y 7.x=z

38 Live Uses of Variables (Live)
Data Primitive facts: variables x Propagates sets: {x,y,z} Dataflow equations. At j: x = y+z killLV(j): {x} genLV(j): {y,z} Merge operator: set union Spring 19 CSCI 4450/6450, A Milanova

39 Live Uses of Variables Backward, may dataflow problem j out(j) i1 i2
out(i1) out(i2) out(i3) Spring 19 CSCI 4450/6450, A Milanova

40 Available Expressions
An expression x op y is available at program point n if every path from entry to n evaluates x op y, and after every evaluation prior to reaching n, there are NO subsequent assignments to x or y 1 x op y x = … y = … x op x x = … y = … x op y x = … y = … n Spring 19 CSCI 4450/6450, A Milanova

41 Problem 3. Available Expressions (Avail)
Problem statement: For every node n, compute the set of expressions that are available at n 1 x op y x = … y = … x op x x = … y = … x op y x = … y = … n Spring 19 CSCI 4450/6450, A Milanova

42 Avail Enables Global Common Subexpressions
q=a*b z=a*b r=2*z u=a*b z=u/2 W=a*b Cannot be eliminated because a*b is not available on all paths. w=a*b Spring 19 CSCI 4450/6450, A Milanova

43 Avail Enables Global Common Subexpressions
Can we eliminate w=a*b? t1=a*b z=t1 r=2*z t1=a*b q=t1 u=t1 z=u/2 w=a*b Spring 19 CSCI 4450/6450, A Milanova

44 Available Expressions (Avail)
Data? Primitive dataflow facts are expressions, e.g., x+y, a*b, a+2 Analysis propagates sets of expressions, e.g., {x+y,a*b} Dataflow equations at j: x = y op z? outAE(j) = (inAE(j) – killAE(j)) genAE(j) killAE(j): all expressions with operand x: (x op _),(_ op x) genAE(j): new expression: {(y op z)}

45 Available Expressions (Avail)
Merge operator? For Avail, it is set intersection inAE(j) = { outAE(i) | i is predecessor of j } j Spring 19 CSCI 4450/6450, A Milanova

46 Available Expressions (Avail)
in(i1) in(i2) in(i3) i1 i2 i3 in(j) j x=y+z Is it a forward or a backward problem? Is it a may problem or a must problem? Forward, must dataflow problem Spring 19 CSCI 4450/6450, A Milanova

47 Example 1.y=a+b 2.x=a*b 3.if y<=a*b 4.a=a+1 5.x=a*b 6.goto 3 7. …
What is the data that is propagated: sets of expressions e.g., {a+b, a*b} Forward or backward: forward Equations: out(i) = (in(i) – kill(i)) U gen(i) What is gen(i): all expressions computed at i whose operands are not defined at i. E.g., a*b is generated at 5, but a+1 is not generated at 4 because a was defined at 4! What is kiil(i): all expressions that have an operand defined at i are killed. E.g., a+b and a*b are killed at 4. 4. Is it a must or a may problem: A MUST problem. In order for an expression to be available at node n, it must be available on all paths to n. Thus, in(i) = intersection of out(j) over all predecessors j of i. 6.goto 3 7. …

48 Very Busy Expressions An expression x op y is very busy at node n, if along EVERY path from n to the end of the program, we come to a computation of x op y BEFORE any redefinition of x or y. n X = … Y = … t1=X op Y X = … Y = … t1=X op Y X = … Y = … t1=X op Y Spring 19 CSCI 4450/6450, A Milanova

49 Problem 4. Very Busy Expressions (VeryB)
Problem Statement: For each node n, compute the set of expressions that are very busy on exit from n. Q: What is the data? Q: What are the equations? Q: What is genVB(i)? Q: What is killVB(i)? Q: What is the merge operator? j: x = y+z

50 Very Busy Expressions (VeryB)
Data? Primitive dataflow facts are expressions, e.g., x+y, a*b Analysis propagates sets of expressions, e.g., {x+y,a*b} Dataflow equations at j: x = y op z? in(j) = gen(j) (out(j) – kill(j)) kill(j): all expressions with operand x: (x op _),(_ op x) gen(j): new expression: {(y op z)}

51 Very Busy Expressions (VeryB)
Merge operator? For VeryB, it is set intersection outVB(j) = { inVB(i) | i is successor of j } j Spring 19 CSCI 4450/6450, A Milanova

52 Very Busy Expressions j Backward, must dataflow problem outVB(j) i1 i2
outVB(i1) outVB(i2) outVB(i3) Spring 19 CSCI 4450/6450, A Milanova

53 Dataflow Analysis Problems
May Analyses Must Analyses Forward Analyses Reaching Definitions Available Expressions Backward Analyses Live Uses of Variables Very Busy Expressions Spring 19 CSCI 4450/6450, A Milanova

54 Similarities In all cases, analysis operates on a finite set D of primitive dataflow facts: Reach: D is the set of all definitions in the program: e.g., {(x,1),(y,2),(x,4),(y,5)} Avail and VeryB: D is the set of all arithmetic expressions: e.g., { a+b,a*b,a+1} Live: D is the set of all variables e.g., { x,y,z } Solution at node n is a subset of D (a definition either reaches node n or it does not reach node n) Spring 19 CSCI 4450/6450, A Milanova

55 Similarities Dataflow equations (i.e., transfer functions) for forward problems have generic form: out(j) = Fj(in(j)) = (in(j) – kill(j)) gen(j) = (in(j) pres(j)) gen(j) in(j) = { V out(i) | i is predecessor of j } Note: pres(j) is the complement of kill(j), D – kill(j) Note: What makes the 4 classical problems special is that sets pres(j) and gen(j) do not depend on in(j) Set union and set intersection can be implemented as logical OR and AND respectively


Download ppt "Dataflow Analysis, cont."

Similar presentations


Ads by Google