A more sophisticated TXL solution to [[TIL Chairmarks]] #3.1, Move all invariant assigned computations outside of while loops.
This is a more sophisticated version of [[Lift Invariant Assignments Using TXL]], which uses renaming of masking assignments in a loop to expose more opportunities to move assigned computations out of the loop. See the original for the basics of code motion in TXL, and this one for the renaming aspect.
-- Main.JamesCordy - 04 Nov 2005
_File "TILcodemotion2.Txl"_
% Lift independent assigned computations outside of TIL while loops
% J.R. Cordy, November 2005
% This more sophisitcated TXL program will optimize a TIL program by moving all
% assigned computations not dependent on computation inside while loops to the outside.
% The process works in two steps:
% (1) Renaming of masking assignments (subsequent assignments to a variable),
% which exposes hidden opportunities for code motion, and
% (2) Lifting of independent assignments, exactly as in the simpler assignment
% only case in TILcodemotion.Txl
% Finally, declarations are synthesized and inserted for any new variables introduced
% by the renaming.
% Based on the TIL grammar
include "TIL.Grm"
% Two main parts - the actual renaming and lifting, then the introduction of missing
% declarations for any introduced renamed variables
function main
replace [program]
P [program]
by
P [renameAndLift]
[declareNewVariables]
end function
% Main rule: Rename masking assignments and lift them outside loops until no more can be
% renamed and no more can belifted
rule renameAndLift
replace [program]
P [program]
% We continue as long as a match for either transform can be found - the form [?rule]
% in TXL means test if a pattern match for the rule can be found
% In TXL, the composition of two conditionals is "or", so this guard tests whether
% either rule can find a match
where
P [?renameMaskingAssignments]
[?liftLoopAssignments]
% If so, apply the rules and continue until the condition above fails
by
P [renameMaskingAssignments]
[liftLoopAssignments]
end rule
% Ruleset 1: Rename any masking assignments (i.e., re-assignments to the same variable)
% This exposes hidden opportunities to move assigned computations out of the loop
rule renameMaskingAssignments
% Find every while loop
replace [statement*]
while Expn [expression] do
Body [statement*]
'end
Rest [statement*]
% Keep renaming until there are no more to rename
where
Body [?renameAssignment Body]
by
while Expn do
Body [renameAssignment Body]
'end
Rest
end rule
% Rename repeated assignments
rule renameAssignment Body [statement*]
% Find an assignment
replace [statement*]
X [id] := E [expression];
Rest [statement*]
% Construct the context it appears in
construct PreContext [statement*]
Body [deleteAssignmentAndRest X]
% Rename any subsequent assignment to X, if possible
where
Rest [?renameAssignmentsTo X PreContext]
by
X := E;
Rest [renameAssignmentsTo X PreContext]
end rule
% Rename any subsequent assignment to X, if possible
rule renameAssignmentsTo X [id] PreContext [statement*]
% Find a subsequent assignment to X
replace [statement*]
X := E [expression];
Rest [statement*]
% It only makes sense to rename it if its effect doesn't wrap around ...
where not
PreContext [refers X]
% ... and it is not an iteration
where not
E [refers X]
% ... and it is not assigned again
where not
Rest [assigns X]
% If all that is ok, then rename it
construct NewX [id]
X [!]
by
NewX := E;
Rest [$ X NewX] % [$ X NewX] means substitute NewX for X everywhere in Rest
end rule
% Ruleset 2: Lift all independent assignments out of loops
% This is exactly the same ruleset as in TILcodemotion.Txl - we could use a TXL "include"
% statement to reuse the original
rule liftLoopAssignments
% Find every loop
replace [statement*]
while Expn [expression] do
Body [statement*]
'end
Rest [statement*]
% Construct a list of all the top-level assignments in it
construct AllAssignments [statement*]
Body [deleteNonAssignments]
% Construct a copy of the loop to work on
construct LiftedLoop [statement*]
while Expn do
Body
'end
% Only proceed if there are assignments left that can be lifted out
% The [?loopLift] form tests if the pattern of the [loopLift] rule can be matched -
% "each AllAssignments" tests this for any of the top-level internal assignments
where
LiftedLoop [?loopLift Body each AllAssignments]
% If the above guard succeeds, some can be moved out, so go ahead and move them,
% replacing the original loop with the result
by
LiftedLoop [loopLift Body each AllAssignments]
[. Rest]
end rule
% Attempt to lift a given assignment outside the loop
function loopLift Body [statement*] Assignment [statement]
deconstruct Assignment
X [id] := E [expression];
% Extract a list of all the identifiers used in the expression
construct IdsInExpression [id*]
_ [^ E]
% Replace the loop and its contents
replace [statement*]
Loop [statement*]
% We can only lift the assignment out if all the identifiers in its
% expression are not assigned in the loop ...
where not
Loop [assigns each IdsInExpression]
% ... and X itself is assigned only once
deconstruct * Body
X := _ [expression];
Rest [statement*]
where not
Rest [assigns X]
% ... and the the effect of it does not wrap around the loop
construct PreContext [statement*]
Body [deleteAssignmentAndRest X]
where not
PreContext [refers X]
% Now lift out the assignment
by
Assignment
Loop [deleteAssignment Assignment]
end function
% Utility rules used above
% Delete a given assignment statement from a scope
function deleteAssignment Assignment [statement]
replace * [statement*]
Assignment
Rest [statement*]
by
Rest
end function
% Delete all non-assignment statements in a scope -
% given a scope, yields the assignments only
rule deleteNonAssignments
replace [statement*]
S [statement]
Rest [statement*]
deconstruct not S
_ [assignment_statement]
by
Rest
end rule
% Delete everything in a scope from the assignment to X on -
% given a scope and X, yields the context of the first assignment to X
function deleteAssignmentAndRest X [id]
replace * [statement*]
X := E [expression];
Rest [statement*]
by
% nada
end function
% Condition - given a scope, does the scope assign to the identifier?
function assigns Id [id]
match * [assignment_statement]
Id := Expn [expression];
end function
% Condition - given a scope, does the scope refer to the identifier?
function refers Id [id]
match * [id]
Id
end function
% Ruleset 3: Declare any renaming variables we introduced
rule declareNewVariables
replace [program]
P [program]
% Continue until we can't find any more
where
P [?declareNewVariable P]
by
P [declareNewVariable P]
end rule
function declareNewVariable P [program]
replace * [statement*]
X [id] := E [expression];
MoreStatements [statement*]
deconstruct not * [declaration] P
'var X;
by
'var X;
X := E;
MoreStatements
end function
_Example run:_ "lift2.til" is a meaningless little program intended to demonstrate the difference between the simple assignment code motion solution of [[Lift Invariant Assignments using TXL]] and the more sophisticated one above. We first run the simple solution, and no opportunities for code motion are found. Then we run the solution above, which renames masking assignments to expose two computations that can be moved out of the loop.
cat lift2.til
var a; var b; var c;
var x; var y; var z;
while 1 do
x := a + b;
y := x;
a := y + 3;
x := b + c;
y := x - 2;
z := x + a * y;
end
txl lift2.til TILcodemotion.Txl
TXL v10.4a (15.6.05) (c)1988-2005 Queen's University at Kingston
Compiling TILcodemotion.Txl ...
Parsing lift2.til ...
Transforming ...
var a;
var b;
var c;
var x;
var y;
var z;
while 1 do
x := a + b;
y := x;
a := y + 3;
x := b + c;
y := x - 2;
z := x + a * y;
end
txl lift2.til TILcodemotion2.Txl
TXL v10.4a (15.6.05) (c)1988-2005 Queen's University at Kingston
Compiling TILcodemotion2.Txl ...
Parsing lift2.til ...
Transforming ...
var a;
var b;
var c;
var x;
var y;
var z;
var x4;
x4 := b + c;
var y2;
y2 := x4 - 2;
while 1 do
x := a + b;
y := x;
a := y + 3;
z := x4 + a * y2;
end