Basic Blocks
Basic Blocks
Flow of control can only enter the basic block thru the first instruction in the block.
No jumps into the middle of the block
Control will leave the block without halting or branching except possibly at the last
instruction in the block
Basic blocks act as nodes for flow graphs where edges indicate the flow of execution. If an edge
exists from block b1 to block b2 it means that instructions in b2 are to be executed after b1
Leader is the first instruction in the block. We start with the leader and group all instructions into
one block till we encounter another leader (excluding next leader).
Ex for i=1,10 do
For j=1,10 do
a[I,j]=0.0
for i=1,10 do
a[i,i]=1.0
here a is 10X10 matrix .each element takes 8 bytes and is stored as row major order
if lower index is 1
a[i,j]=base+((i-l)*nc)*w+(j-l)*w
base+(i*nc+j)w –l*nc*w-l*w)
base+(i*10+j)8-1*10*8-1*8
base+(i*10+j)8-88
1)i=1 //B1
2)j=1 //B2
3)t1=10*I //B3
4)t2=t1+j
5)t3=8*t2
6)t4=t3-88
7)a[t4]=0.0
8)j=j+1
10)i=i+1 //B4
12)i=1 //B5
13)t5=i-1 //B6
14)t6=88*t5
15)a[t6]=1.0
16)i=i+1
Block no Instructions
B1 i=1
B2 j=1
B3 t1=10*i
t2=t1+j
t3=8*t2
t4=t3-88
a[t4]=0.0
j=j+1
if j<=10 goto B3
B4 i=i+1
if i<=10 goto B2
B5 i=1
B6 t5=i-1
t6=88*t5
a[t6]=1.0
i=i+1
if i<=10 goto B6
Machine instructions involving registers are faster & shorter than the instructions involving operands
in memory.
We want to keep variables in registers for as long as possible, to avoid having to reload them
whenever they are needed.
When a variable isn’t needed any more we free the register to reuse it for other variables.
We must know if a particular value will be used later in the basic block.
If, after computing a value X, we will soon be using the value again, we should keep it in a register.
If the value has no further use in the block ,we can reuse the register for assigning to another
variable
Applications of nextuse:
It can be used to assign space for temporaries and to assign register to variables
Suppose
and if control flows from i to j ,and along the path if x is not modified then we say that
x is live at statement j
nextuse of x in i is j
This (liveness & next use)has to be done for every three address statement in the block
Algorithm:
Input: block B
Assumptions: Initially all non temporary variables (pgm variables) in symbol table(ST) are live on exit
Output: at each statement i:x=y+z in B ,we attach to I the liveness and next use information of x,y
and z
Method:
Block
x=y+z
t=x+y
z=x+t
We start with the last statement in B and scan backwards to the beginning of B.
i. Attach to the stmt I the information currently in ST regarding next use and liveness of
x,y,z
ii. In the ST set x to not live and no next use
iii. In the ST set y,z to live and next use to i
+ is generic for TAC of the form x=+y or x=y same as above ignoring z(2 & 3 order)
x,y,z are live on exit t is not
First approach:
Second approach
Line1 Col X:D Y:L(1) Z: L(1) T:D Before executing stmt 1 observe
(1) the nextuse It says y & z will be
used in stmt 1 so don’t empty the
register assigned for y&z
1 x=y+z
(2) T:D X:L(2) Y:L(2) Z:D Before executing stmt 2 observe
the nextuse It says y & x will be
used in stmt 2 so don’t empty the
register assigned for y & x
2 t=x+y
(3) Z:D X:L(3) T:L(3) Y:L(0) Before executing stmt 3 observe
the nextuse It says x & t will be
used in stmt 3 so don’t empty the
register assigned for x & t
3 z=x+t
(0) z:L(0) x:L(0) t:D Y:l(0)
Here jumps to specific instruction in basic block is replaced by block number. bcoz after constructing
the flow graph ,blocks may undergo substantial changes to instructions in blocks that may call for
fixing the targets of the jumps everytime target was changed.
Loops:
Since most of the execution time is spent in loops, it is important that we generate efficient code for
loops.
if and only if ,there is a node in L called loop entry with the property that no other node in L has a
predecessor outside L
Advantages:
Executes faster
Optimization Techniques:
Strength reduction
Code movement
we can apply many local optimization techniques by constructing DAG from Basic blocks .
Procedure:
There is a node in the DAG for each initial values of the variables appearing in basic
block(BB)
There is a node N associated with each statement s within the block. The children of N are
those nodes corresponding to statements that are the last definitions prior to s of the
operands used by s
Node N is labeled by the operator
applied at s and also attached to N is the list of variables for which it is the last definition
within the block.
Certain nodes are designated output nodes. These are the nodes whose variables are live on
exit from the blocks.that is ,their values may be used later in another block of the flow graph
TAC
1) a=b+c
2) b=a-d
3) c=b+c
4) d=a-d
new TAC: between 2nd & 4th a & d doesn’t change,so one of them can be removed.But since d is
reused b can be eliminated
d=a-d
c=d+c
b=d
ii) when looking for common subexpressions,we are really looking for expressions that are
guaranteed to compute the same value,no matter how that value is computed.
Below stmt 1 & 4 computes same value bcoz sum remains the same(b0+c0). Eventhough at the
surface level they are different(2nd & 3rd modify b & c)
a=b+c
b=b–d
c=c+d
e=b+c
from fig
b=b0-d0,
c=c0+d0
This can be detected when algebraic identities are applied to the DAG
We delete from DAG any root (node with no ancestor) that has no live variables attached.
If applied repeatedly , this will remove all nodes from the DAG that correspond to dead code
If in fig above assume a & b are live but c & e are not, we can immediately remove the root labeled
e.
The roots labeled a & b remain,since they have live variables attached
Ex a=a+d
a=d
d=d+c
d=a+d
d=c
a=d
d=c
Constant Propagation
Ex x = 3;
y = x + y;
x=3
y=3+y
int x = 14;
int y = 7 - x / 2;
return y * (28 / x + 2);
Propagating x yields:
int x = 14;
int y = 7 - 14 / 2;
return y * (28 / 14 + 2);
Loop collapsing
int a[100][300];
Here is the code fragment after the loop has been collapsed.
int a[100][300];
int *p = &a[0][0];
After modification
a=10;
while(i<10)
{
i=i+1;
}
For x=a[i] create node with operator =[] and two children representing initial value of array a 0 and
index i.variable x becomes a label of this new node
For a[j]=y a new node is created with operator[]= and three children representing a 0,j & y. No {}
Node []= kills all currently constructed nodes whose value depends on a0.
A node that has been killed will not receive any more labels that is it cannot become common
subexpression
Sometimes a node must be killed even though none of its children have an array like a0 attached
likewise a node can kill ,if it has a descendent that is an array ,even though none of its children are
array nodes.
b=12+a beginning address of 4th element For efficiency reasons b is representing some
x=b[i] position in an array a
b[j]=y If elements are 4 bytes long,b represents 4th
z=b[i] element of a. If i & j represent same value then
b[i] & b[j] represent same location
a Therefore it is important to have third
0 4 8 12 statement b[j]=y
Kill the node with x as its attached child
After possible optimizations by manipulating DAG once constructed ,we may reconstitute the TAC
for BB from which we built the DAG
we prefer to compute the result into a variable that is live on exit from the block
however ,if global live variable information is not available ,we assume every program variable is live
except temporary
if more than one live variable is attached ,then we have to introduce copy stmts to give correct value
to each of these live variable(global optimization may look after the redundancy)
While reconstructing BB from DAG we need to not only worry about what variables will be used to
hold values but also about the order of computation
Rules are
Order of instructions must respect the order of nodes in the DAG. That is we cannot
compute a nodes value until we have computed a value for each of its children
Assignments to an array must follow all previous assignments to or evaluations from the
same array according to the order of these instructions in the original BB
Evaluations of array must follow any previous assignments to the same array
It keeps track of what values are in what registers so that it can avoid unnecessary loads & stores
Uses of registers
Assumption:
Each IC operator has exactly one machine instruction that takes operands in register & perform
operation, leaving the result in register
OP reg1,reg2,reg3 /*reg1 is dest register holding result ,reg1 & reg2 holds the two
operands*/
determines what loads are necessary to get needed operands into register(Loads)
After generating loads it will generate the TAI for operation itself(operation)
If there is a need to store the result into memory location,it will also generate the store(stores)
Inorder to make decision, we need to have a data structure that tells about
whether a memory location attached to a variable is having latest value or not(may not be stored
from reg back to memory)
Register Descriptors(RD):
For each available register , RD keeps track of the variable names whose current value is in that
register.
Initially all register descriptors are empty,as the code generation progresses, each register will hold
the value of zero or more names
Address Descriptors(AD): For each program variable an AD keeps track of the location/s
where the current value of that variable can be found
Here location may be register, memory address, a stack location or combination .The information
can be stored in ST against variable name
Function has access to all register & address descriptors for all the variables of BB
May also have access to certain useful data flow information about liveness on exit of block
2. if y is not in Ry(as per RD for Ry),then issue LD Ry,y´,where y´ is one of the memory location for y
according to AD of y
3. similarly for z
Machine Instructions for copy stmts: For TAI of the form x=y (y not exist,exist)
Here we assume that getReg will always choose the same register for both x & y
Ending BB:
Here variables used by the block may wind up with their only location being a register
If the variable is a temporary used only within block, it’s fine .when block ends we can ignore
temporary and assume register is empty
If variable is live on exit from the block or if we don’t know which variables are live on exit then we
need to assume that the value of the variable is needed later
In that case,for each variable x whose location descriptor does not say that its value is located in
memory location for x,we must generate
ST x,R where R is a register where x value exists at the end of the block
4. copy stmt x=y, after generating the load for y into register Ry if needed and after managing
descriptors as for load stmt (change the RD for register Ry so it holds only y
,change the AD for y by adding register Ry as an additional location
,remove Ry from AD of any variable other than y)
3)v=t+u
4)a=d
5)d=v+u
policy is used
LD R3,c a t c
u=a-c Sub R1,R1,R3 R1 is reused bcoz a is
exists in locn a
u t c a b c,R3 d R2 R1
chosen R1(a)
R3(c).
longer needed(
u t v a B c d R2 R1 R3
chosen R3( c ) could
a=d LD R2,d
Not in reg ,hence load
(a=d)
d
u a,d v R2 B c d,R2 R1 R3
Chosen r2(t) to load d
available
d=v+u ADD
R1,R3,R1 R1 is reused since u is
needed
d a v R2 B c R1 R3
Chosen R1(u) to store
chosen R3(v).
memory on exit
restore
Exit ST a ,R2 d a v a,R2 B c d,R1 R3
ST d,R1
Design Of Function getReg:
Consider x=y + z
We have to pick register for y & z, issues for y & z are same.
so let us consider Ry selection(y reg exist,(! & empty reg) ,else 1!2!)
a)Since new value of x is being computed, a register that holds only x is always acceptable
choice for Rx
This statement holds even if x is one of y and z bcoz x as operand is utilized and the new
value is stored in Rx(bcoz our machine instruction allows two registers to be the same in
one instruction) Ex ADD R1,R1,R2(x=x+z)
b)if y is not used after instruction I in the sense described for v (IIIc) and Ry holds only y
after being loaded if necessary Ry can be used as Rx