Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Dependent dynamic rules
1. Composing Source-to-Source
Data-Flow Transformations with
Dependent Dynamic Rewrite Rules
Program Transformation 2004–2005
Eelco Visser
Institute of Information & Computing Sciences
Utrecht University,
The Netherlands
March 3, 2005
3. Part I
Data-Flow Transformation Strategies
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
4. Flow-Sensitive Constant Propagation
(x := 3;
y := x + 1;
if foo(x) then
(y := 2 * x;
x := y - 2)
else
(x := y;
y := 23);
z := x + y)
http://www.strategoxt.org
(x := 3;
y := 4;
if foo(3) then
(y := 6;
x := 4)
else
(x := 4;
y := 23);
z := 4 + y)
Composing Source-to-Source Data-Flow Transformations with
5. x := 3
x := 3
x -> 3
y := x + 1
y := 4
x -> 3
y -> 4
if foo(x)
if foo(3)
x -> 3
y -> 4
y := 2 * x
y := 6
x -> 3
y -> 4
x := y
x := 4
x -> 3
y -> 6
x -> 4
y -> 4
x := y - 2
x := 4
y := 23
y := 23
x -> 4
y -> 6
x -> 4
y -> 23
x -> 4
y z := x + y
z := 4 + y
6.
7. Strategy for Basic Constant Propagation
prop-const = PropConst <+ prop-const-assign
<+ prop-const-declare <+ prop-const-let <+ prop-const-if
<+ prop-const-while <+ (all(prop-const); try(EvalBinOp))
prop-const-assign =
|[ x := <prop-const => e> ]|
; if <is-value> e
then rules( PropConst.x : |[ x ]| -> |[ e ]| )
else rules( PropConst.x :- |[ x ]| ) end
prop-const-declare =
|[ var x := <prop-const => e> ]|
; if <is-value> e
then rules( PropConst+x : |[ x ]| -> |[ e ]| )
else rules( PropConst+x :- |[ x ]| ) end
prop-const-let =
?|[ let d* in e* end ]|; {| PropConst : all(prop-const) |}
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
8. Intersection of Rule Sets
prop-const-if =
|[ if <prop-const> then <id> else <id> ]|
; (|[ if <id> then <prop-const> else <id> ]|
/PropConst |[ if <id> then <id> else <prop-const> ]|)
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
9. Intersection of Rule Sets
x
let var x := 1 var y := z 1
var z := 3 var a := 4 1
in x := x + z;
4
a := 5;
4
if y then
(y := y + 5;
4
z := 8)
4
else
(x := a + 21;
26
y := x + 1;
26
z := a + z);
26
b := a + z;
z := z + x end
-
y
-
z
3
3
3
a
4
4
5
b
-
-
3
8
5
5
-
27
27
-
3
3
8
8
8
8
5
5
5
5
5
5
13
13
http://www.strategoxt.org
let var x := 1 var y := z
var z := 3 var a := 4
in x := 4;
a := 5;
if y then
(y := y + 5;
z := 8)
else
(x := 26;
y := 27;
z := 8);
b := 13;
z := 8 + x end
Composing Source-to-Source Data-Flow Transformations with
10. Fixed-Point Intersection of Rule Sets
let var w := 20 var x := 20
var y := 20 var z := 10
in while SomethingUnknown()
(if x = 20 then w := 20
if y = 20 then x := 20
if z = 20 then y := 20
w; x; y; z end
do
else w := 10;
else x := 10;
else y := 10);
1
2
let var w := 20 var x := 20
var y := 20 var z := 10
in while SomethingUnknown() do
(if x = 20 then w := 20 else w := 10;
if y = 20 then x := 20 else x := 10;
y := 10);
w; x; y; 10 end
http://www.strategoxt.org
3
4
w
20
20
20
20
20
-
x
20
20
20
-
y
20
10
10
10
10
-
z
10
10
10
10
10
10
10
10
10
Composing Source-to-Source Data-Flow Transformations with
11. Fixed-Point Intersection of Rule Sets
prop-const-while =
?|[ while e1 do e2 ]|
; (/PropConst* |[ while <prop-const> do <prop-const> ]|)
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
12. Unreachable Code Elimination
let var x := 0 var y := 0
in x := 10;
while A do
(if x = 10
then dosomething()
else (dosomethingelse();
x := x + 1));
y := x
end
http://www.strategoxt.org
let var x := 0
var y := 0
in x := 10;
while A do
dosomething();
y := 10
end
Composing Source-to-Source Data-Flow Transformations with
13. Unreachable Code Elimination
prop-const-if =
|[ if <prop-const> then <id> else <id> ]|
; (EvalIf; prop-const
<+ (|[ if <id> then <prop-const> else <id> ]|
/PropConst
|[ if <id> then <id> else <prop-const> ]|))
prop-const-while =
?|[ while e1 do e2 ]|
; (|[ while <prop-const> do <id> ]|; EvalWhile
<+ (/PropConst*
|[ while <prop-const> do <prop-const> ]|))
EvalIf : |[ if
EvalIf : |[ if
where
EvalWhile : |[
0 then e1 else e2 ]| -> |[ e2 ]|
i then e1 else e2 ]| -> |[ e1 ]|
<not(eq)>(|[ i ]|, |[ 0 ]|)
while 0 do e ]| -> |[ () ]|
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
14. Dead Code Elimination
(x := foo(b);
y := bar(h);
a := c + 23;
if 4 > x then
(d := b + a;
g := 4 + y)
else
(b := 2;
a := y + 3;
a := 4 + x);
print(a))
{c,b}
{x,c}
{x,c}
{x,a}
{a}
{a}
{x}
{x}
{x}
{a}
http://www.strategoxt.org
(x := foo(b);
a := c + 23;
if not(4> x) then
a := 4 + x;
print(a))
Composing Source-to-Source Data-Flow Transformations with
15. Dead Code Elimination
dce = VarNeeded <+ ElimAssign <+ dce-assign
<+ dce-seq <+ dce-if <+ dce-while <+ all(dce)
ElimAssign :
|[ x := e ]| -> |[ () ]|
where <not(Needed)> |[ x ]|
VarNeeded =
?|[ x ]|
; rules(Needed : |[ x ]|)
dce-assign =
?|[ x := e ]|
; rules(Needed :- |[ x ]|)
; |[ <id> := <dce> ]|
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
16. Dead Code Elimination – Control-Flow
dce-seq =
|[ (<* reverse-filter(dce; not(?|[ () ]|)) >) ]|
dce-if =
(|[ if <id> then <dce> else <id> ]|
Needed/ |[ if <id> then <id> else <dce> ]|)
; |[ if <dce> then <id> else <id> ]|
; try(ElimIf)
dce-while =
|[ while <id> do <id> ]|
; (Needed/* |[ while <dce> do <dce> ]|)
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
17. Part II
Dependencies in Data-Flow Transformation Rules
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
18. Copy Propagation
Replace copies x produced by assignments of the form x := y by
original y
a := b;
c := d + a
http://www.strategoxt.org
a := b;
c := d + b
Composing Source-to-Source Data-Flow Transformations with
19. Copy Propagation
Replace copies x produced by assignments of the form x := y by
original y
a := b;
c := d + b
a := b;
c := d + a
First attempt using dynamic rules (wrong)
copy-prop-assign =
?|[ x := y ]|;
if <not(eq)>(x,y) then
rules( CopyProp.x : |[ x ]| -> |[ y ]| )
else
rules( CopyProp.x :- |[ x ]| )
end
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
20. Problem: Insufficient Dependencies
(a := b;
b := foo();
c := d + a)
(a := b;
b := foo();
c := d + b)
copy-prop-assign =
?|[ x := y ]|;
if <not(eq)>(x,y) then
rules( CopyProp.x : |[ x ]| -> |[ y ]| )
else
rules( CopyProp.x :- |[ x ]| )
end
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
21. Problem: Insufficient Dependencies
(a := b;
b := foo();
c := d + a)
(a := b;
b := foo();
c := d + b)
Problem: rule not undefined when variable in rhs changed
Solution: undefine rule when any of its variables is modified
copy-prop-assign =
?|[ x := y ]|;
if <not(eq)>(x,y) then
rules( CopyProp.x : |[ x ]| -> |[ y ]| )
else
rules( CopyProp.x :- |[ x ]| )
end
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
22. Problem: Free Variable Capture
let var a := bar()
var b := baz()
in a := b;
let var b := foo()
in print(a)
end
end
let var a := bar()
var b := baz()
in a := b;
let var b := foo()
in print(b) // wrong!
end
end
copy-prop-assign =
?|[ x := y ]|;
if <not(eq)>(x,y) then
rules( CopyProp.x : |[ x ]| -> |[ y ]| )
else
rules( CopyProp.x :- |[ x ]| )
end
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
23. Problem: Free Variable Capture
let var a := bar()
var b := baz()
in a := b;
let var b := foo()
in print(a)
end
end
let var a := bar()
var b := baz()
in a := b;
let var b := foo()
in print(b) // wrong!
end
end
Problem: rule not undefined when variables become shadowed
Solution: undefine rule locally when some variable shadowed
copy-prop-assign =
?|[ x := y ]|;
if <not(eq)>(x,y) then
rules( CopyProp.x : |[ x ]| -> |[ y ]| )
else
rules( CopyProp.x :- |[ x ]| )
end
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
24. Problem: Escaping Variables (1)
let var a := bar()
in let var b := foo()
in a := b
end;
print(a)
end
let var a := bar()
in let var b := foo()
in a := b
end;
print(b) // wrong!
end
copy-prop-assign =
?|[ x := y ]|;
if <not(eq)>(x,y) then
rules( CopyProp.x : |[ x ]| -> |[ y ]| )
else
rules( CopyProp.x :- |[ x ]| )
end
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
25. Problem: Escaping Variables (1)
let var a := bar()
in let var b := foo()
in a := b
end;
print(a)
end
let var a := bar()
in let var b := foo()
in a := b
end;
print(b) // wrong!
end
Problem: rule not undefined when a variable goes out of scope
Solution: (re)define rule in local scope
copy-prop-assign =
?|[ x := y ]|;
if <not(eq)>(x,y) then
rules( CopyProp.x : |[ x ]| -> |[ y ]| )
else
rules( CopyProp.x :- |[ x ]| )
end
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
26. Problem: Escaping Variables (2)
let var
var
in let
in
a := bar()
c := baz()
var b := foo()
a := b;
a := c
end;
print(a)
let var
var
in let
in
a := bar()
c := baz()
var b := foo()
a := b;
a := c
end;
print(c) // ok!
end
end
copy-prop-assign = ?|[ x := y ]|;
if <not(eq)>(x,y) then
rules( CopyProp.x : |[ x ]| -> |[ y ]| )
else rules( CopyProp.x :- |[ x ]| ) end
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
27. Problem: Escaping Variables (2)
let var
var
in let
in
a := bar()
c := baz()
var b := foo()
a := b;
a := c
end;
print(a)
let var
var
in let
in
a := bar()
c := baz()
var b := foo()
a := b;
a := c
end;
print(c) // ok!
end
end
Problem: definition in local scope is too restricted
Solution: (re)define rule in innermost scope of all variables
involved
copy-prop-assign = ?|[ x := y ]|;
if <not(eq)>(x,y) then
rules( CopyProp.x : |[ x ]| -> |[ y ]| )
else rules( CopyProp.x :- |[ x ]| ) end
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
28. Common-Subexpression Elimination
(x
y
z
a
z
:=
:=
:=
:=
:=
a + b;
a + b;
a + c;
1;
(a + c) + (a + b))
http://www.strategoxt.org
⇒
(x
y
z
a
z
:=
:=
:=
:=
:=
a + b;
x;
a + c;
1;
(a + c) + (a + b))
Composing Source-to-Source Data-Flow Transformations with
29. Common-Subexpression Elimination
(x
y
z
a
z
:=
:=
:=
:=
:=
a + b;
a + b;
a + c;
1;
(a + c) + (a + b))
⇒
(x
y
z
a
z
:=
:=
:=
:=
:=
a + b;
x;
a + c;
1;
(a + c) + (a + b))
Assignment
x := e
Propagation rule
|[ e ]| -> |[ x ]|
Dependencies in common-subexpression elimination
all variables in assignment x := e
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
30. Common-Subexpression Elimination
cse = cse-assign <+ (all(cse); try(ReplaceExp))
cse-assign =
|[ x := <cse => e> ]|
; where(<undefine-subexpressions> |[ x ]|)
; if <not(is-subterm(||[ x ]|))> |[ e ]| then
rules(ReplaceExp : |[ e ]| -> |[ x ]|)
; where(<register-subexpressions(|e)> |[ x := e ]|)
end
register-subexpressions(|e) =
get-vars; map({y : ?|[ y ]|
; rules(UsedInExp :+ |[ y ]| -> e)})
undefine-subexpressions =
bagof-UsedInExp; map({?e; rules(ReplaceExp :- |[ e ]|)})
get-vars = collect({?|[ x ]|})
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
31. Dependent Dynamic Rules
Declare rule dependencies
R.lab : p1 -> p2
depends on [(lab1,dep1),...,(labn,depn)]
Undefine all rules depending on dep
undefine-R(|dep)
Locally undefine all rules depending on dep
new-R(|lab, dep)
and label current scope with lab
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
32. Copy Propagation – Assignments
copy-prop =
repeat1(CopyProp)
<+ copy-prop-assign
<+ copy-prop-declare
<+ copy-prop-let <+ copy-prop-if <+ copy-prop-while
<+ all(copy-prop)
copy-prop-declare =
|[ var x ta := <copy-prop => e> ]|
; where( new-CopyProp(|x, x) )
; where( try(<copy-prop-assign-aux> |[ x := e ]|) )
copy-prop-assign =
|[ x := <copy-prop => e> ]|
; where( undefine-CopyProp(|x) )
; where( try(copy-prop-assign-aux) )
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
33. Copy Propagation – Propagation Rule
copy-prop-assign-aux =
? |[ x := y ]|
; where( <not(eq)>(x,y) )
; where( innermost-scope-CopyProp => z )
; rules(
CopyProp.z : |[ x ]| -> |[ y ]|
depends on [(x,x), (y,y)]
)
innermost-scope-CopyProp =
get-var-names => vars
; innermost-scope-CopyProp(elem-of(|vars))
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
34. Copy Propagation – Control-Flow
copy-prop-let =
|[ let <*id> in <*id> end ]|
; {| CopyProp : all(copy-prop) |}
copy-prop-if =
|[ if <copy-prop> then <id> else <id> ]|
; ( |[ if <id> then <copy-prop> else <id> ]|
/CopyProp |[ if <id> then <id> else <copy-prop> ]|)
copy-prop-while =
|[ while <id> do <id> ]|
; (/CopyProp* |[ while <copy-prop> do <copy-prop> ]|)
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
35. Common-Subexpression Elimination – Assignments
cse =
cse-assign <+ cse-vardec <+ cse-let <+ cse-if
<+ cse-while <+ all(cse); try(CSE)
cse-vardec =
|[ var x ta := <cse => e> ]|
; new-CSE(|x, x)
; where( try(<cse-assign-aux> |[ x := e ]|) )
cse-assign =
|[ x := <cse => e> ]|
; undefine-CSE(|x)
; where(try(cse-assign-aux))
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
36. Common-Subexpression Elimination – Propagation
cse-assign-aux =
? |[ x := e ]|
; where( <not(oncetd(?|[ x ]|)); pure> |[ e ]| )
; where( get-var-names; map(!(<id>,<id>)) => xs )
; where( innermost-scope-CSE => z )
; rules( CSE.z : |[ e ]| -> |[ x ]| depends on xs )
pure =
?|[ i ]| + ?|[ x ]| + |[ <bo:id>(<pure>, <pure>) ]|
innermost-scope-CSE =
get-var-names => vars
; innermost-scope-CSE(where(<elem>(<id>, vars)))
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
37. Common-Subexpression Elimination – Control-Flow
cse-let =
|[ let <*id> in <*id> end ]|
; {| CSE : all(cse) |}
cse-if =
|[ if <cse> then <id> else <id> ]|
; ( |[ if <id> then <cse> else <id> ]|
/CSE |[ if <id> then <id> else <cse> ]|)
cse-while =
|[ while <id> do <id> ]|
; (/CSE* |[ while <cse> do <cse> ]|)
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
38. Part III
Generic Data-Flow Transformation Strategies
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
39. Generic Data-Flow Transformation Strategies
Data-flow transformation strategies are similar
Factor out underlying strategy
Requires generalization over combinators
new-dynamic-rules(|Rs,x,x)
undefine-dynamic-rules(|Rs,x)
/~Rs1~Rs2/
Allows very concise specifications for specific transformations
Combination of transformations
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
41. Generic Strategy – Assignments
prop-assign =
|[ <id> := <fp> ]|
; (transform(fp)
<+ before
; ?|[ x := e ]|
; undefine-dynamic-rules(|RsDf,x)
; after)
prop-declare =
|[ var <id> := <fp> ]|
; (transform(fp)
<+ before; ?|[ var x := e ]|
; new-dynamic-rules(|RsSc,x,x);after)
prop-let =
?|[ let d* in e* end ]|
; (transform(fp)
<+ {|~RsSc : before; all(fp); after |})
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
42. Generic Strategy – Control Flow
prop-if =
|[ if <fp> then <id> else <id> ]|
; (transform(fp)
<+ before
; (|[ if <id> then <fp> else <id> ]|
/~Rs1~Rs2/ |[ if <id> then <id> else <fp> ]|)
; after)
prop-while =
?|[ while e1 do e2 ]|
; (transform(fp)
<+ before
; /~Rs1~Rs2/* |[ while <fp> do <fp> ]|
; after)
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
43. Instantation: Constant Propagation
prop-const = forward-prop(prop-const-transform, id,
prop-const-after | ["PropConst"],[],[])
prop-const-transform(recur) =
EvalFor <+ EvalIf; recur
<+ |[ while <recur> do <id> ]|; EvalWhile
prop-const-after =
try(prop-const-assign <+ prop-const-declare
<+ PropConst <+ EvalBinOp)
prop-const-assign =
?|[ x := e ]|; where( <is-value> e )
; rules( PropConst.x : |[ x ]| -> |[ e ]|
depends on [(x,x)] )
prop-const-declare =
?|[var x ta := e]|; where(<prop-const-assign>|[x := e]|)
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
44. Instantation: Copy Propagation
copy-prop =
forward-prop(no-transform,id,copy-prop-after
|["CopyProp"],[],[])
copy-prop-after =
try(copy-prop-assign <+ copy-prop-declare
<+ repeat1(CopyProp))
copy-prop-declare =
? |[ var x ta := e ]|
; where(try(<copy-prop-assign> |[ x := e ]|))
copy-prop-assign =
? |[ x := y ]|
; where( <not(eq)> (x, y) )
; where( get-var-dependencies => xs )
; where( innermost-scope-CopyProp => z )
; rules( CopyProp.z : |[ x ]| -> |[ y ]| depends on xs )
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with
45. Instantation: Common-Subexpression Elimination
cse =
forward-prop(no-transform, id, cse-after|["CSE"],[],[])
cse-after =
try(cse-assign <+ cse-declare <+ CSE)
cse-declare =
?|[ var x := e ]|; where( <cse-assign> |[ x := e ]| )
cse-assign
; where(
; where(
; where(
; rules(
= ?|[ x := e ]|
<pure-and-not-trivial(|x)> |[ e ]| )
get-var-dependencies => xs )
innermost-scope-CSE => z )
CSE.z : |[ e ]| -> |[ x ]| depends on xs )
http://www.strategoxt.org
Composing Source-to-Source Data-Flow Transformations with