1 cananian 1.1.2.1 // Liveness.java, created Mon Feb 22 23:03:30 1999 by cananian
  2 cananian 1.1.2.1 // Copyright (C) 1999 C. Scott Ananian <cananian@alumni.princeton.edu>
  3 cananian 1.1.2.1 // Licensed under the terms of the GNU GPL; see COPYING for details.
  4 cananian 1.1.2.1 package harpoon.IR.Bytecode;
  5 cananian 1.1.2.1 
  6 cananian 1.1.2.3 import java.util.AbstractSet;
  7 cananian 1.1.2.1 import java.util.BitSet;
  8 cananian 1.1.2.1 import java.util.HashMap;
  9 cananian 1.1.2.1 import java.util.Iterator;
 10 cananian 1.1.2.1 import java.util.Set;
 11 cananian 1.1.2.1 
 12 cananian 1.4     import net.cscott.jutil.FilterIterator;
 13 cananian 1.4     import net.cscott.jutil.UnmodifiableIterator;
 14 cananian 1.4     import net.cscott.jutil.WorkSet;
 15 cananian 1.1.2.1 /**
 16 cananian 1.1.2.1  * <code>Liveness</code> is a local-variable liveness analysis on Bytecode
 17 cananian 1.1.2.1  * form.
 18 cananian 1.1.2.1  * 
 19 cananian 1.1.2.1  * @author  C. Scott Ananian <cananian@alumni.princeton.edu>
 20 cananian 1.4      * @version $Id: Liveness.java,v 1.4 2004/02/08 01:55:10 cananian Exp $
 21 cananian 1.1.2.1  */
 22 cananian 1.1.2.1 public class Liveness  {
 23 cananian 1.1.2.1     /** internal data structure is a hashtable of boolean arrays */
 24 cananian 1.1.2.1     private LiveMap live;
 25 cananian 1.1.2.1     /** Return the liveness of local variable #<code>lv_index</code> at
 26 cananian 1.1.2.1      *  instruction <code>where</code>. */
 27 cananian 1.1.2.1     public boolean isLive(Instr where, int lv_index) {
 28 cananian 1.1.2.1         return live.get(where).isLive(lv_index);
 29 cananian 1.1.2.1     }
 30 cananian 1.1.2.3     /** Return a <code>Collection</code> of live local variables at the
 31 cananian 1.1.2.3      *  given instruction <code>where</code>. */
 32 cananian 1.1.2.3     public Set liveSet(Instr where) {
 33 cananian 1.1.2.3         return live.get(where).asCollection();
 34 cananian 1.1.2.3     }
 35 cananian 1.1.2.1     
 36 cananian 1.1.2.1     /** Creates a <code>Liveness</code>. */
 37 cananian 1.1.2.1     public Liveness(Code bcode) {
 38 cananian 1.1.2.1         int max_locals = bcode.getMaxLocals();
 39 cananian 1.1.2.1         this.live = new LiveMap(max_locals);
 40 cananian 1.1.2.1         /* add all elements to the workset initially. */
 41 cananian 1.1.2.1         WorkSet workset = new WorkSet(bcode.getElementsL());
 42 cananian 1.1.2.1         /* iterate until stable. */
 43 cananian 1.1.2.1         while (!workset.isEmpty()) {
 44 cananian 1.1.2.4             Instr in = (Instr) workset.pop();
 45 cananian 1.1.2.1             LiveSet liveIn = new LiveSet(max_locals);
 46 cananian 1.1.2.1             if (Op.JSR == in.getOpcode() || Op.JSR_W == in.getOpcode()) {
 47 cananian 1.1.2.1                 // jsrs are special: live set of subroutine except where
 48 cananian 1.1.2.1                 // undefined, where we use live set of next instr.
 49 cananian 1.1.2.1                 LiveSet liveNext=live.get(in.next(0));//live set of next instr
 50 cananian 1.1.2.1                 liveIn.or(live.get(in.next(1))); // live set of subroutine.
 51 cananian 1.1.2.1                 for (int i=0; i<max_locals; i++)
 52 cananian 1.1.2.1                     // for those locals which the subroutine doesn't touch...
 53 cananian 1.1.2.1                     if (!liveIn.isDefined(i) && liveNext.isDefined(i))
 54 cananian 1.1.2.1                         // use the liveSet of the next instruction.
 55 cananian 1.1.2.1                         if (liveNext.isLive(i))
 56 cananian 1.1.2.1                             liveIn.markLive(i);
 57 cananian 1.1.2.1                         else
 58 cananian 1.1.2.1                             liveIn.markDead(i);
 59 cananian 1.1.2.1             } else {
 60 cananian 1.1.2.1                 // if it is in the live set of a successor and I don't define
 61 cananian 1.1.2.1                 // it, then it is in the live set here.
 62 cananian 1.1.2.1                 for (Iterator i=in.next.iterator(); i.hasNext(); )
 63 cananian 1.1.2.1                     liveIn.or(live.get((Instr) i.next()));
 64 cananian 1.1.2.1             }
 65 cananian 1.1.2.1             // if it is defined here, then it is dead.
 66 cananian 1.1.2.1             int defs = def(in);
 67 cananian 1.1.2.1             if (defs != -1) {
 68 cananian 1.1.2.1                 liveIn.markDead(defs);
 69 cananian 1.1.2.1                 if (isLong(in))
 70 cananian 1.1.2.1                     liveIn.markDead(defs+1);
 71 cananian 1.1.2.1             }
 72 cananian 1.1.2.1             // if this instruction uses a local variable, it is in the live set
 73 cananian 1.1.2.1             // (note how liveness beats deadness in the case of Op.IINC)
 74 cananian 1.1.2.1             int uses = use(in);
 75 cananian 1.1.2.1             if (uses!=-1) {
 76 cananian 1.1.2.1                 liveIn.markLive(uses);
 77 cananian 1.1.2.1                 if (isLong(in)) 
 78 cananian 1.1.2.1                     liveIn.markLive(uses+1);
 79 cananian 1.1.2.1             }
 80 cananian 1.1.2.2             // optimization hack: try to share LiveSets.
 81 cananian 1.1.2.2             if (in.next.size()>0 && live.get(in.next(0)).equals(liveIn))
 82 cananian 1.1.2.2                 liveIn = live.get(in.next(0)); // share livesets if equal.
 83 cananian 1.1.2.2 
 84 cananian 1.1.2.2             // if live set has changed, add predecessors to work set
 85 cananian 1.1.2.1             boolean changed = live.update(in, liveIn);
 86 cananian 1.1.2.1             if (changed)
 87 cananian 1.1.2.1                 for (Iterator i=in.prev.iterator(); i.hasNext(); )
 88 cananian 1.1.2.1                     workset.add(i.next());
 89 cananian 1.1.2.1         }
 90 cananian 1.1.2.1     }
 91 cananian 1.1.2.1     // opcode properties.
 92 cananian 1.1.2.1     private static int def(Instr in) {
 93 cananian 1.1.2.1         switch(in.getOpcode()) {
 94 cananian 1.1.2.1         case Op.ASTORE:
 95 cananian 1.1.2.1         case Op.ASTORE_0: case Op.ASTORE_1:
 96 cananian 1.1.2.1         case Op.ASTORE_2: case Op.ASTORE_3:
 97 cananian 1.1.2.1         case Op.FSTORE:
 98 cananian 1.1.2.1         case Op.FSTORE_0: case Op.FSTORE_1:
 99 cananian 1.1.2.1         case Op.FSTORE_2: case Op.FSTORE_3:
100 cananian 1.1.2.1         case Op.ISTORE:
101 cananian 1.1.2.1         case Op.ISTORE_0: case Op.ISTORE_1:
102 cananian 1.1.2.1         case Op.ISTORE_2: case Op.ISTORE_3:
103 cananian 1.1.2.1         case Op.DSTORE:
104 cananian 1.1.2.1         case Op.DSTORE_0: case Op.DSTORE_1:
105 cananian 1.1.2.1         case Op.DSTORE_2: case Op.DSTORE_3:
106 cananian 1.1.2.1         case Op.LSTORE:
107 cananian 1.1.2.1         case Op.LSTORE_0: case Op.LSTORE_1:
108 cananian 1.1.2.1         case Op.LSTORE_2: case Op.LSTORE_3:
109 cananian 1.1.2.1         case Op.IINC: // both use *and* definition.
110 cananian 1.1.2.1             return ((OpLocalVariable)((InGen)in).getOperand(0)).getIndex();
111 cananian 1.1.2.1         default:
112 cananian 1.1.2.1             return -1;
113 cananian 1.1.2.1         }
114 cananian 1.1.2.1     }
115 cananian 1.1.2.1     private static int use(Instr in) {
116 cananian 1.1.2.1         switch(in.getOpcode()) {
117 cananian 1.1.2.1         case Op.ALOAD:
118 cananian 1.1.2.1         case Op.ALOAD_0: case Op.ALOAD_1:
119 cananian 1.1.2.1         case Op.ALOAD_2: case Op.ALOAD_3:
120 cananian 1.1.2.1         case Op.FLOAD:
121 cananian 1.1.2.1         case Op.FLOAD_0: case Op.FLOAD_1:
122 cananian 1.1.2.1         case Op.FLOAD_2: case Op.FLOAD_3:
123 cananian 1.1.2.1         case Op.ILOAD:
124 cananian 1.1.2.1         case Op.ILOAD_0: case Op.ILOAD_1:
125 cananian 1.1.2.1         case Op.ILOAD_2: case Op.ILOAD_3:
126 cananian 1.1.2.1         case Op.DLOAD:
127 cananian 1.1.2.1         case Op.DLOAD_0: case Op.DLOAD_1:
128 cananian 1.1.2.1         case Op.DLOAD_2: case Op.DLOAD_3:
129 cananian 1.1.2.1         case Op.LLOAD:
130 cananian 1.1.2.1         case Op.LLOAD_0: case Op.LLOAD_1:
131 cananian 1.1.2.1         case Op.LLOAD_2: case Op.LLOAD_3:
132 cananian 1.1.2.1         case Op.IINC: // both use *and* definition.
133 cananian 1.1.2.1             return ((OpLocalVariable)((InGen)in).getOperand(0)).getIndex();
134 cananian 1.1.2.1         case Op.RET: // use of local variable.
135 cananian 1.1.2.1             return ((OpLocalVariable)((InRet)in).getOperand()).getIndex();
136 cananian 1.1.2.1         default:
137 cananian 1.1.2.1             return -1;
138 cananian 1.1.2.1         }
139 cananian 1.1.2.1     }
140 cananian 1.1.2.1     private static boolean isLong(Instr in) {
141 cananian 1.1.2.1         switch (in.getOpcode()) {
142 cananian 1.1.2.1         case Op.DLOAD:
143 cananian 1.1.2.1         case Op.DLOAD_0: case Op.DLOAD_1:
144 cananian 1.1.2.1         case Op.DLOAD_2: case Op.DLOAD_3:
145 cananian 1.1.2.1         case Op.LLOAD:
146 cananian 1.1.2.1         case Op.LLOAD_0: case Op.LLOAD_1:
147 cananian 1.1.2.1         case Op.LLOAD_2: case Op.LLOAD_3:
148 cananian 1.1.2.1         case Op.DSTORE:
149 cananian 1.1.2.1         case Op.DSTORE_0: case Op.DSTORE_1:
150 cananian 1.1.2.1         case Op.DSTORE_2: case Op.DSTORE_3:
151 cananian 1.1.2.1         case Op.LSTORE:
152 cananian 1.1.2.1         case Op.LSTORE_0: case Op.LSTORE_1:
153 cananian 1.1.2.1         case Op.LSTORE_2: case Op.LSTORE_3:
154 cananian 1.1.2.1             return true;
155 cananian 1.1.2.1         default:
156 cananian 1.1.2.1             return false;
157 cananian 1.1.2.1         }
158 cananian 1.1.2.1     }
159 cananian 1.1.2.1     // liveness bitset
160 cananian 1.1.2.1     private class LiveSet extends java.util.BitSet {
161 cananian 1.1.2.1         LiveSet(int max_locals) { super(2*max_locals); }
162 cananian 1.1.2.1         // treat bits in pairs: (defined, live)
163 cananian 1.1.2.1         // so that: 0X = undefined, 10 = defined but dead, 11 = live.
164 cananian 1.1.2.1         public boolean isDefined(int which_lv) {
165 cananian 1.1.2.1             return get(2*which_lv);
166 cananian 1.1.2.1         }
167 cananian 1.1.2.1         public boolean isLive(int which_lv) {
168 cananian 1.1.2.1             return get(2*which_lv)/*defined*/ && get(2*which_lv+1)/*live*/;
169 cananian 1.1.2.1         }
170 cananian 1.1.2.1         public void markDead(int which_lv) {
171 cananian 1.1.2.1             set(2*which_lv); // defined.
172 cananian 1.1.2.1             clear(2*which_lv+1); // but dead.
173 cananian 1.1.2.1         }
174 cananian 1.1.2.1         public void markLive(int which_lv) {
175 cananian 1.1.2.1             set(2*which_lv); // defined
176 cananian 1.1.2.1             set(2*which_lv+1); // and live.
177 cananian 1.1.2.3         }
178 cananian 1.1.2.3         // collection view.
179 cananian 1.1.2.3         public Set asCollection() {
180 cananian 1.1.2.3             final int lvsize = size()/2;
181 cananian 1.1.2.3             return new AbstractSet() {
182 cananian 1.1.2.3                 public int size() {
183 cananian 1.1.2.3                     int size=0;
184 cananian 1.1.2.3                     for (int i=0; i < lvsize; i++)
185 cananian 1.1.2.3                         if (isLive(i)) size++;
186 cananian 1.1.2.3                     return size;
187 cananian 1.1.2.3                 }
188 cananian 1.1.2.3                 public Iterator iterator() {
189 cananian 1.1.2.3                     // simple integer iterator
190 cananian 1.1.2.5                     Iterator i = new UnmodifiableIterator() {
191 cananian 1.1.2.3                         int i=0;
192 cananian 1.1.2.3                         public boolean hasNext() { return (i < lvsize); }
193 cananian 1.1.2.3                         public Object next() { return new Integer(i++); }
194 cananian 1.1.2.3                     };
195 cananian 1.1.2.3                     // filtered by liveness method.
196 cananian 1.1.2.3                     return new FilterIterator(i, new FilterIterator.Filter() {
197 cananian 1.1.2.3                         public boolean isElement(Object o) {
198 cananian 1.1.2.3                             return isLive(((Integer)o).intValue());
199 cananian 1.1.2.3                         }
200 cananian 1.1.2.3                     });
201 cananian 1.1.2.3                 }
202 cananian 1.1.2.3             };
203 cananian 1.1.2.1         }
204 cananian 1.1.2.1     }
205 cananian 1.1.2.1 
206 cananian 1.1.2.1     // liveness map data structure.
207 cananian 1.1.2.1     private class LiveMap {
208 cananian 1.1.2.1         private final HashMap hm = new HashMap();
209 cananian 1.1.2.1 
210 cananian 1.1.2.1         private final LiveSet EMPTY; // all zero.
211 cananian 1.1.2.1         LiveMap(int max_locals) {
212 cananian 1.1.2.1             this.EMPTY = new LiveSet(max_locals);
213 cananian 1.1.2.1         }
214 cananian 1.1.2.1         LiveSet get(Instr in) {
215 cananian 1.1.2.1             if (!hm.containsKey(in)) return EMPTY;
216 cananian 1.1.2.1             return (LiveSet) hm.get(in);
217 cananian 1.1.2.1         }
218 cananian 1.1.2.1         boolean update(Instr in, LiveSet newset) {
219 cananian 1.1.2.1             if (get(in).equals(newset)) return false;
220 cananian 1.1.2.1             hm.put(in, newset);
221 cananian 1.1.2.1             return true;
222 cananian 1.1.2.1         }
223 cananian 1.1.2.1     }
224 cananian 1.2     }