001 /* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013 package org.jikesrvm.compilers.opt.ssa; 014 015 import java.util.ArrayList; 016 import java.util.Enumeration; 017 import java.util.HashMap; 018 import java.util.Iterator; 019 import java.util.Stack; 020 import org.jikesrvm.VM; 021 import org.jikesrvm.compilers.opt.ir.IR; 022 import org.jikesrvm.compilers.opt.ir.Instruction; 023 import org.jikesrvm.compilers.opt.ir.operand.ConstantOperand; 024 import org.jikesrvm.compilers.opt.util.GraphNode; 025 026 /** 027 * This class holds the results of global value numbering. 028 * ala Alpern, Wegman and Zadeck, PoPL 88. 029 * See Muchnick p.348 for a discussion (which is quite buggy, should 030 * have stuck to the dragon book, which gives a high-level description of 031 * the correct algorithm on page 143). 032 */ 033 public final class GlobalValueNumberState { 034 /** 035 * Constant used to flag "unknown" value numbers 036 */ 037 public static final int UNKNOWN = -1; 038 /** 039 * Print verbose debugging output? 040 */ 041 private static final boolean DEBUG = false; 042 /** 043 * Assume parameters are not aliased? 044 */ 045 private static final boolean NO_PARAM_ALIAS = false; 046 047 /** 048 * ArrayList of GVCongruenceClass, indexed by value number. 049 */ 050 private final ArrayList<GVCongruenceClass> B; 051 /** 052 * The value graph. 053 */ 054 final ValueGraph valueGraph; 055 /** 056 * Stack used for a work list. 057 */ 058 private final Stack<GVCongruenceClass> workList; 059 060 /** 061 * Construct a structure to hold global value number results for an IR. 062 * 063 * @param ir governing IR 064 */ 065 GlobalValueNumberState(IR ir) { 066 B = new ArrayList<GVCongruenceClass>(); 067 workList = new Stack<GVCongruenceClass>(); 068 valueGraph = new ValueGraph(ir); 069 globalValueNumber(); 070 } 071 072 /** 073 * Compute node congruence over the value number graph. 074 * 075 * <p> Algorithm: Muchnick pp. 348-355 076 * <p> Note: the Muchnick algorithm is buggy. In particular, it 077 * does not put all needed congruence classes on the worklist. 078 * 079 * <p> Two nodes in the value graph are congruent if one of the 080 * following holds: 081 * <ul> 082 * <li> they are the same node 083 * <li> their labels are identical constants 084 * <li> they have the same operators and their operands are 085 * congruent 086 * </ul> 087 * 088 * <p> Optimistic algorithm: 089 * <ul> 090 * <li> Initially assume all nodes with the same label are congruent 091 * <li> start a work list with all congruence classes that 092 * have multiple operands 093 * <li> choose a congruence class from the worklist. partition its 094 * elements into new congruence classes if we can discover that 095 * they are not congruent. 096 * <li> Add any newly created congruence classes to the work list. 097 * <li> (Here's the step Muchnick omits:) 098 * For each class C which has a dependence on any of the newly 099 * created congruence classes, add C to the work list 100 * <li> repeat until work list is empty 101 * </ul> 102 * 103 * <p> The following method breaks Muchnick's algorithm, which will 104 * assign m and n the same value number. Muchnick's problem is that 105 * it does not put the congruence class for 'int_mul' back on the worklist 106 * when we discover, for example, that i is not congruent to k 107 * <pre> 108 * public int foo(int a, int b, int c, int d, int e, int f, int g, int h) { 109 * int i = a+b; 110 * int j = c+d; 111 * int k = e+f; 112 * int l = g+h; 113 * int m = i * j; 114 * int n = k * l; 115 * int o = m/n; 116 * return o; 117 * } 118 * </pre> 119 */ 120 private void globalValueNumber() { 121 if (DEBUG) { 122 VM.sysWrite(valueGraph.toString()); 123 } 124 // initialize the congurence classes 125 initialize(); 126 // initialize the work list 127 initializeWorkList(); 128 // drain the work list 129 while (!workList.empty()) { 130 GVCongruenceClass partition = workList.pop(); 131 partitionClass(partition); 132 } 133 // all done 134 if (DEBUG) { 135 printValueNumbers(); 136 } 137 } 138 139 /** 140 * Merge the congruence classes containing vertices v1 and v2.; 141 */ 142 void mergeClasses(ValueGraphVertex v1, ValueGraphVertex v2) { 143 if (DEBUG) { 144 System.out.println("@@@@ mergeClasses called with v1 = " + v1 + " ; v2 = " + v2); 145 } 146 147 int val1 = v1.getValueNumber(); 148 int val2 = v2.getValueNumber(); 149 if (val1 == val2) return; 150 151 GVCongruenceClass class1 = B.get(val1); 152 153 while (true) { 154 GVCongruenceClass class2 = B.get(val2); 155 Iterator<ValueGraphVertex> i = class2.iterator(); 156 if (!i.hasNext()) break; 157 ValueGraphVertex v = i.next(); 158 if (DEBUG) { 159 System.out.println("@@@@ moving vertex " + v + " from class " + val2 + " to class " + val1); 160 } 161 class1.addVertex(v); 162 class2.removeVertex(v); 163 v.setValueNumber(val1); 164 } 165 166 // Null out entry for val2 167 B.set(val2, null); 168 } 169 170 /** 171 * Definitely-same relation. 172 * @param name1 first variable 173 * @param name2 second variable 174 * @return true iff the value numbers for two variables are equal 175 */ 176 boolean DS(Object name1, Object name2) { 177 ValueGraphVertex v1 = valueGraph.getVertex(name1); 178 ValueGraphVertex v2 = valueGraph.getVertex(name2); 179 return v1.getValueNumber() == v2.getValueNumber(); 180 } 181 182 /** 183 * Definitely-different relation for two value numbers. 184 * Returns true for the following cases: 185 * <ul> 186 * <li> v1 and v2 are both constants, but don't match 187 * <li> v1 and v2 both result from NEW statements, but don't match 188 * <li> one of v1 and v2 is a parameter, and the other results from a 189 * new statement 190 * </ul> 191 * <p> TODO: add more smarts 192 * @param v1 first value number 193 * @param v2 second value number 194 * @return true iff the value numbers for two variables are definitely 195 * different 196 */ 197 boolean DD(int v1, int v2) { 198 if ((v1 == -1) || (v2 == -1)) { 199 return false; 200 } 201 GVCongruenceClass class1 = B.get(v1); 202 GVCongruenceClass class2 = B.get(v2); 203 Object label1 = class1.getLabel(); 204 Object label2 = class2.getLabel(); 205 // if one is a constant, they must both be ... 206 if (isConstant(label1) && !isConstant(label2)) { 207 return false; 208 } 209 if (!isConstant(label1) && isConstant(label2)) { 210 return false; 211 } 212 if (isConstant(label1)) { 213 return (v1 != v2); 214 } 215 // handle DD for allocations 216 if (isBornAtAllocation(label1)) { 217 if (isBornAtAllocation(label2)) { 218 // both are NEW. Are they dd? 219 return (v1 != v2); 220 } else if (class2.containsParameter()) { 221 // one is NEW, other is parameter. They are DD. 222 return true; 223 } 224 } else { 225 if (isBornAtAllocation(label2)) { 226 if (class1.containsParameter()) { 227 // one is NEW, other is parameter. They are DD. 228 return true; 229 } 230 } 231 } 232 // assume parameters are not aliased? 233 if (NO_PARAM_ALIAS) { 234 if (v1 != v2) { 235 if (class1.containsParameter()) { 236 if (class2.containsParameter()) { 237 return true; 238 } 239 } 240 } 241 } 242 243 // if we haven't figured out they're DD, return false; 244 return false; 245 } 246 247 /** 248 * Definitely-different relation. 249 * Returns true for the following cases: 250 * <ul> 251 * <li> name1 and name2 are both constants, but don't match 252 * <li> name1 and name2 both result from NEW statements, but don't match 253 * <li> one of name1 and name2 is a parameter, and the other results from a 254 * new statement 255 * </ul> 256 * <p> TODO: add more smarts 257 * @param name1 name of first object to compare 258 * @param name2 name of second object to compare 259 * @return true iff the value numbers for two variables are definitely 260 * different 261 */ 262 boolean DD(Object name1, Object name2) { 263 ValueGraphVertex v1 = valueGraph.getVertex(name1); 264 ValueGraphVertex v2 = valueGraph.getVertex(name2); 265 return DD(v1.getValueNumber(), v2.getValueNumber()); 266 } 267 268 GVCongruenceClass congruenceClass(Object name) { 269 ValueGraphVertex v = valueGraph.getVertex(name); 270 return B.get(v.getValueNumber()); 271 } 272 273 /** 274 * Return the (integer) value number for a given variable 275 * 276 * @param name name of the variable to look up 277 * @return its value number 278 */ 279 int getValueNumber(Object name) { 280 ValueGraphVertex v = valueGraph.getVertex(name); 281 if (v == null) { 282 return UNKNOWN; 283 } 284 return v.getValueNumber(); 285 } 286 287 /** 288 * Print the value numbers for each node in the value graph. 289 */ 290 void printValueNumbers() { 291 for (Enumeration<GraphNode> e = valueGraph.enumerateVertices(); e.hasMoreElements();) { 292 ValueGraphVertex v = (ValueGraphVertex) e.nextElement(); 293 int valueNumber = v.getValueNumber(); 294 GVCongruenceClass c = B.get(valueNumber); 295 System.out.println(v.getName() + " " + valueNumber + " " + c.getLabel()); 296 } 297 } 298 299 /** 300 * Initialize the congruence classes, assuming that all nodes 301 * with the same label are congruent. 302 */ 303 private void initialize() { 304 // store a map from label -> congruenceClass 305 HashMap<Object, GVCongruenceClass> labelMap = new HashMap<Object, GVCongruenceClass>(10); 306 for (Enumeration<GraphNode> e = valueGraph.enumerateVertices(); e.hasMoreElements();) { 307 ValueGraphVertex v = (ValueGraphVertex) e.nextElement(); 308 Object label = v.getLabel(); 309 GVCongruenceClass c = findOrCreateCongruenceClass(label, labelMap); 310 // add this node to the congruence class 311 c.addVertex(v); 312 // set the value number for the node 313 v.setValueNumber(c.getValueNumber()); 314 } 315 } 316 317 /** 318 * Given a label, return the congruence class for that label. 319 * Create one if needed. 320 * 321 * @param label the label of a congruence class 322 * @param labelMap a mapping from labels to congruence class 323 * @return the congruence class for the label. 324 */ 325 private GVCongruenceClass findOrCreateCongruenceClass(Object label, 326 HashMap<Object, GVCongruenceClass> labelMap) { 327 GVCongruenceClass result = labelMap.get(label); 328 if ((result == null) || (label == null)) { 329 result = createCongruenceClass(label); 330 labelMap.put(label, result); 331 } 332 return result; 333 } 334 335 /** 336 * Given a label, return a new congruence class for that label. 337 * @param label the label of a congruence class 338 * @return the congruence class for the label. 339 */ 340 private GVCongruenceClass createCongruenceClass(Object label) { 341 // create a new congruence class, and update data structures 342 int index = B.size(); 343 GVCongruenceClass result = new GVCongruenceClass(index, label); 344 B.add(result); 345 return result; 346 } 347 348 /** 349 * Initialize the work list. 350 * A congruence class gets put on the work list if any two nodes 351 * in the class point to corresponding targets in separate partitions. 352 */ 353 private void initializeWorkList() { 354 for (GVCongruenceClass c : B) { 355 if (c.size() == 1) { 356 continue; 357 } 358 // store a reference to the first node in c 359 Iterator<ValueGraphVertex> i = c.iterator(); 360 ValueGraphVertex first = i.next(); 361 // now check that each other target matches the first element 362 // if not, add this class to the work list 363 // 364 while (i.hasNext()) { 365 ValueGraphVertex v = i.next(); 366 if (!checkCongruence(first, v)) { 367 workList.push(c); 368 break; 369 } 370 } 371 } 372 } 373 374 /** 375 * Partition a congruence class. 376 * @param partition the class to partition 377 */ 378 private void partitionClass(GVCongruenceClass partition) { 379 // store a reference to the first node in c, which will serve 380 // as a representative for this class 381 Iterator<ValueGraphVertex> i = partition.iterator(); 382 ValueGraphVertex first = i.next(); 383 ArrayList<GVCongruenceClass> newClasses = new ArrayList<GVCongruenceClass>(); 384 // now check each other node in c, to see if it matches the 385 // representative 386 ArrayList<ValueGraphVertex> toRemove = new ArrayList<ValueGraphVertex>(); 387 while (i.hasNext()) { 388 ValueGraphVertex v = i.next(); 389 if (!checkCongruence(first, v)) { 390 // NOT CONGRUENT!! split the partition. first check if 391 // v fits in any other newly created congruence classes 392 int index = findCongruenceMatch(newClasses, v); 393 if (index > -1) { 394 // MATCH FOUND!! place v in newClasses[index] 395 GVCongruenceClass match = B.get(index); 396 match.addVertex(v); 397 v.setValueNumber(match.getValueNumber()); 398 } else { 399 // NO MATCH FOUND!! create a new congruence class 400 // find the appropriate label for the new congruence class 401 // and create a new congruence class with this label 402 GVCongruenceClass c = createCongruenceClass(v); 403 newClasses.add(c); 404 c.addVertex(v); 405 v.setValueNumber(c.getValueNumber()); 406 } 407 // mark v as to be removed from partition 408 // (Can't remove it yet while iterating over the set); 409 toRemove.add(v); 410 } 411 } 412 // remove necessary vertices 413 for (ValueGraphVertex v : toRemove) { 414 partition.removeVertex(v); 415 } 416 // if needed place the original partition back on the work list 417 if ((!newClasses.isEmpty()) && (partition.size() > 1)) { 418 workList.push(partition); 419 } 420 // place any new congruence classes with size > 1 on the worklist 421 // also place any classes which might indirectly be affected 422 for (GVCongruenceClass c : newClasses) { 423 if (c.size() > 1) { 424 workList.push(c); 425 } 426 addDependentClassesToWorklist(c); 427 } 428 } 429 430 /** 431 * Assuming congruence class c has changed: find all other classes 432 * that might be affected, and add them to the worklist 433 * @param c the congruence class that has changed 434 */ 435 private void addDependentClassesToWorklist(GVCongruenceClass c) { 436 // for each element of this congruence class: 437 for (ValueGraphVertex v : c) { 438 // for each vertex which points to v in the value graph 439 for (Enumeration<GraphNode> e = v.inNodes(); e.hasMoreElements();) { 440 ValueGraphVertex in = (ValueGraphVertex) e.nextElement(); 441 int vn = in.getValueNumber(); 442 GVCongruenceClass x = B.get(vn); 443 workList.push(x); 444 } 445 } 446 } 447 448 /** 449 * Does vertex v belong to any congruence class in a vector? 450 * If so, returns the value number of the matching congruence class. 451 * If none found, returns -1. 452 * @param vector a vector of congruence classes 453 * @param v the vertex to search for 454 * @return the value number corresponding to the congruence class 455 * containing v. -1 iff no such class is found. 456 */ 457 private int findCongruenceMatch(ArrayList<GVCongruenceClass> vector, ValueGraphVertex v) { 458 for (GVCongruenceClass klass : vector) { 459 if (checkCongruence(v, klass)) { 460 return klass.getValueNumber(); 461 } 462 } 463 return -1; 464 } 465 466 /** 467 * Does the current state of the algorithm optimistically assume 468 * that a vertex v is congruent to the vertices in a congruence 469 * class? Note: this can return true even if 470 * if the value numbers don't currently match. 471 * @param v the vertex in question 472 * @param c the congurence class to check 473 * @return true or false 474 */ 475 private boolean checkCongruence(ValueGraphVertex v, GVCongruenceClass c) { 476 ValueGraphVertex r = c.getRepresentative(); 477 boolean result = checkCongruence(r, v); 478 return result; 479 } 480 481 /** 482 * Does the current state of the algorithm optimistically assume 483 * that two nodes are congruent? Note: this can return false 484 * even if the value numbers are currently the same. 485 * @param v1 first vertex 486 * @param v2 second vertex 487 */ 488 private boolean checkCongruence(ValueGraphVertex v1, ValueGraphVertex v2) { 489 if (v1 == v2) { 490 return true; 491 } 492 // make sure the two nodes have the same label 493 if (v1.getLabel() != v2.getLabel()) { 494 return false; 495 } 496 // make sure that the operands match 497 int arity = v1.getArity(); 498 for (int i = 0; i < arity; i++) { 499 ValueGraphVertex target1 = v1.getTarget(i); 500 ValueGraphVertex target2 = v2.getTarget(i); 501 // if either target is null, then that particular control 502 // flow path is never realized, so assume TOP 503 if ((target1 == null) || (target2 == null)) { 504 continue; 505 } 506 if (target1.getValueNumber() != target2.getValueNumber()) { 507 return false; 508 } 509 } 510 return true; 511 } 512 513 /** 514 * Does a given label indicate that the object has a constant value? 515 */ 516 private static boolean isConstant(Object label) { 517 return (label instanceof ConstantOperand); 518 } 519 520 /** 521 * Does a given label indicate that the object is created at an 522 * allocation site? 523 */ 524 private static boolean isBornAtAllocation(Object label) { 525 return (label instanceof Instruction); 526 } 527 }