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 */ 013package org.jikesrvm.compilers.opt.ssa; 014 015import static org.jikesrvm.compilers.opt.ir.Operators.PI; 016 017import java.lang.reflect.Constructor; 018import java.util.Enumeration; 019 020import org.jikesrvm.VM; 021import org.jikesrvm.compilers.opt.OptOptions; 022import org.jikesrvm.compilers.opt.OptimizingCompilerException; 023import org.jikesrvm.compilers.opt.driver.CompilerPhase; 024import org.jikesrvm.compilers.opt.ir.BasicBlock; 025import org.jikesrvm.compilers.opt.ir.BoundsCheck; 026import org.jikesrvm.compilers.opt.ir.GuardedUnary; 027import org.jikesrvm.compilers.opt.ir.IR; 028import org.jikesrvm.compilers.opt.ir.IRTools; 029import org.jikesrvm.compilers.opt.ir.IfCmp; 030import org.jikesrvm.compilers.opt.ir.InlineGuard; 031import org.jikesrvm.compilers.opt.ir.Instruction; 032import org.jikesrvm.compilers.opt.ir.Move; 033import org.jikesrvm.compilers.opt.ir.NullCheck; 034import org.jikesrvm.compilers.opt.ir.Operator; 035import org.jikesrvm.compilers.opt.ir.TypeCheck; 036import org.jikesrvm.compilers.opt.ir.operand.Operand; 037import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand; 038 039/** 040 * This pass inserts PI nodes (Effectively copies) 041 * on branch edges, to introduce new names for analysis 042 */ 043public final class PiNodes extends CompilerPhase { 044 045 /** 046 * Should we insert PI nodes for array references after bounds-checks 047 * and null-checks? 048 * <p>TODO: if this is false, then null-check elimination 049 * will be ineffective. 050 * <p>TODO: prove that null-check elimination is 051 * sound before turning this on again. 052 */ 053 static final boolean CHECK_REF_PI = false; 054 055 /** 056 * Should we insert (true) or delete (false) PI nodes? 057 */ 058 final boolean insertion; 059 060 /** 061 * Are we adding pi nodes for type checks only? This is for GNOSYS 062 * analysis right now. 063 */ 064 final boolean typeChecks; 065 066 /** 067 * Should this phase be performed? 068 * Only perform this when we are doing an SSA-based optimization 069 * that can benefit from PI nodes. 070 */ 071 @Override 072 public boolean shouldPerform(OptOptions options) { 073 return typeChecks; 074 } 075 076 /** 077 * Constructor for this compiler phase 078 */ 079 private static final Constructor<CompilerPhase> constructor = 080 getCompilerPhaseConstructor(PiNodes.class, new Class[]{Boolean.TYPE, Boolean.TYPE}); 081 082 /** 083 * Get a constructor object for this compiler phase 084 * @return compiler phase constructor 085 */ 086 @Override 087 public Constructor<CompilerPhase> getClassConstructor() { 088 return constructor; 089 } 090 091 /** 092 * A String representation of this phase 093 * @return a string representation 094 */ 095 @Override 096 public String getName() { 097 return "Pi Nodes " + insertion; 098 } 099 100 /** 101 * Should we print the IR either before or after this phase? 102 * @param options controlling compiler options 103 * @param before control for the query 104 */ 105 @Override 106 public boolean printingEnabled(OptOptions options, boolean before) { 107 return false; 108 } 109 110 /** 111 * Create the phase. 112 * 113 * @param insert If true, we insert PI nodes, If false, we remove them. 114 */ 115 public PiNodes(boolean insert) { 116 this.insertion = insert; 117 this.typeChecks = false; 118 } 119 120 /** 121 * Create the phase. 122 * 123 * @param insert If true, we insert PI nodes, If false, we remove them. 124 * @param typeChecks If true, we insert PI nodes only for type checks. 125 */ 126 public PiNodes(boolean insert, boolean typeChecks) { 127 super(new Object[]{insert, typeChecks}); 128 this.insertion = insert; 129 this.typeChecks = typeChecks; 130 } 131 132 @Override 133 public void perform(IR ir) { 134 if (insertion) { 135 if (!typeChecks) { 136 insertPiIfNodes(ir); 137 insertPiBcNodes(ir); 138 insertPiNullCheckNodes(ir); 139 } else { 140 insertPiCheckCastNodes(ir); 141 } 142 // invalidate SSA state 143 ir.actualSSAOptions = null; 144 } else { 145 cleanUp(ir); 146 } 147 } 148 149 /** 150 * Insert PI nodes corresponding to compare operations. 151 * Pi-nodes are represented as dummy assignments with a single 152 * argument inserted along each outedge of the conditional. 153 * 154 * @param ir the governing IR 155 */ 156 private void insertPiIfNodes(IR ir) { 157 Enumeration<Instruction> e = ir.forwardInstrEnumerator(); 158 while (e.hasMoreElements()) { 159 Instruction instr = e.nextElement(); 160 // TODO: what other compareops generate useful assertions? 161 if (IfCmp.conforms(instr) || InlineGuard.conforms(instr)) { 162 163 BasicBlock thisbb = instr.getBasicBlock(); 164 // only handle the "normal" case 165 if (thisbb.getNumberOfNormalOut() != 2) { 166 continue; 167 } 168 // insert new basic blocks on each edge out of thisbb 169 Enumeration<BasicBlock> outBB = thisbb.getNormalOut(); 170 BasicBlock out1 = outBB.nextElement(); 171 BasicBlock new1 = IRTools.makeBlockOnEdge(thisbb, out1, ir); 172 BasicBlock out2 = outBB.nextElement(); 173 BasicBlock new2 = IRTools.makeBlockOnEdge(thisbb, out2, ir); 174 175 // For these types of IfCmp's, the Pi Node is not actually 176 // needed yet. For now the only functionality needed is the 177 // blocks made on the outgoing edges. 178 if (InlineGuard.conforms(instr)) continue; 179 180 RegisterOperand ifGuard = IfCmp.getGuardResult(instr); 181 182 if (VM.VerifyAssertions) { 183 VM._assert(ifGuard != null); 184 } 185 // get compared variables 186 Operand a = IfCmp.getVal1(instr); 187 Operand b = IfCmp.getVal2(instr); 188 // determine which block is "taken" on the branch 189 BasicBlock takenBlock = IfCmp.getTarget(instr).target.getBasicBlock(); 190 boolean new1IsTaken = false; 191 if (takenBlock == new1) { 192 new1IsTaken = true; 193 } 194 195 // insert the PI-node instructions for a and b 196 if (a.isRegister() && 197 !a.asRegister().getRegister().isPhysical() && 198 (a.asRegister().getRegister().isInteger() || a.asRegister().getRegister().isAddress())) { 199 // insert pi-nodes only for variables, not constants 200 Instruction s = GuardedUnary.create(PI, (RegisterOperand) a.copy(), a.copy(), null); 201 RegisterOperand sGuard = (RegisterOperand) ifGuard.copy(); 202 if (new1IsTaken) { 203 sGuard.setTaken(); 204 } else { 205 sGuard.setNotTaken(); 206 } 207 GuardedUnary.setGuard(s, sGuard); 208 new1.prependInstruction(s); 209 s = s.copyWithoutLinks(); 210 sGuard = (RegisterOperand) ifGuard.copy(); 211 if (new1IsTaken) { 212 sGuard.setNotTaken(); 213 } else { 214 sGuard.setTaken(); 215 } 216 GuardedUnary.setGuard(s, sGuard); 217 new2.prependInstruction(s); 218 } 219 if (b.isRegister() && 220 !b.asRegister().getRegister().isPhysical() && 221 (b.asRegister().getRegister().isInteger() || b.asRegister().getRegister().isAddress())) { 222 Instruction s = GuardedUnary.create(PI, (RegisterOperand) b.copy(), b.copy(), null); 223 RegisterOperand sGuard = (RegisterOperand) ifGuard.copy(); 224 if (new1IsTaken) { 225 sGuard.setTaken(); 226 } else { 227 sGuard.setNotTaken(); 228 } 229 GuardedUnary.setGuard(s, sGuard); 230 new1.prependInstruction(s); 231 s = s.copyWithoutLinks(); 232 sGuard = (RegisterOperand) ifGuard.copy(); 233 if (new1IsTaken) { 234 sGuard.setNotTaken(); 235 } else { 236 sGuard.setTaken(); 237 } 238 GuardedUnary.setGuard(s, sGuard); 239 new2.prependInstruction(s); 240 } 241 } 242 } 243 } 244 245 /** 246 * Insert Pi nodes for boundchecks. 247 * 248 * <p>Each boundcheck Arr, Index will be followed by 249 * <pre> PI Index, Index </pre> 250 * 251 * @param ir the governing IR 252 */ 253 private void insertPiBcNodes(IR ir) { 254 Instruction nextInst = null; 255 // for each instruction in the IR 256 for (Instruction instr = ir.firstInstructionInCodeOrder(); instr != null; instr = nextInst) { 257 // can't use iterator, since we modify instruction stream 258 nextInst = instr.nextInstructionInCodeOrder(); 259 if (BoundsCheck.conforms(instr)) { 260 // Create a pi node for the index. 261 Operand index = BoundsCheck.getIndex(instr); 262 // create the instruction and insert it 263 if (index.isRegister() && !index.asRegister().getRegister().isPhysical()) { 264 Instruction s = GuardedUnary.create(PI, (RegisterOperand) index.copy(), index.copy(), null); 265 RegisterOperand sGuard = (RegisterOperand) BoundsCheck.getGuardResult(instr).copy(); 266 sGuard.setBoundsCheck(); 267 GuardedUnary.setGuard(s, sGuard); 268 instr.insertAfter(s); 269 } 270 if (CHECK_REF_PI) { 271 // Create a pi node for the array. 272 Operand array = BoundsCheck.getRef(instr); 273 // create the instruction and insert it 274 if (array.isRegister() && !array.asRegister().getRegister().isPhysical()) { 275 Instruction s = GuardedUnary.create(PI, (RegisterOperand) array.copy(), array.copy(), null); 276 RegisterOperand sGuard = (RegisterOperand) BoundsCheck.getGuardResult(instr).copy(); 277 sGuard.setBoundsCheck(); 278 GuardedUnary.setGuard(s, sGuard); 279 instr.insertAfter(s); 280 } 281 } 282 } 283 } 284 } 285 286 /** 287 * Insert Pi nodes for null check operations. 288 * 289 * <p>Each checkcast obj will be followed by 290 * <pre> PI obj, obj </pre> 291 * 292 * @param ir the governing IR 293 */ 294 private void insertPiNullCheckNodes(IR ir) { 295 if (!CHECK_REF_PI) return; 296 Instruction nextInst = null; 297 // for each instruction in the IR 298 for (Instruction instr = ir.firstInstructionInCodeOrder(); instr != null; instr = nextInst) { 299 // can't use iterator, since we modify instruction stream 300 nextInst = instr.nextInstructionInCodeOrder(); 301 if (NullCheck.conforms(instr)) { 302 // get compared variables 303 Operand obj = NullCheck.getRef(instr); 304 // create the instruction and insert it 305 if (obj.isRegister()) { 306 RegisterOperand lval = (RegisterOperand) obj.copy(); 307 Instruction s = GuardedUnary.create(PI, lval, obj.copy(), null); 308 RegisterOperand sGuard = (RegisterOperand) NullCheck.getGuardResult(instr).copy(); 309 sGuard.setNullCheck(); 310 GuardedUnary.setGuard(s, sGuard); 311 instr.insertAfter(s); 312 } 313 } 314 } 315 } 316 317 /** 318 * Insert Pi nodes for checkcast operations. 319 * 320 * <p>Each checkcast obj will be followed by 321 * <pre> ref_move obj, obj </pre> 322 * 323 * @param ir the governing IR 324 */ 325 private void insertPiCheckCastNodes(IR ir) { 326 Instruction nextInst = null; 327 // for each instruction in the IR 328 for (Instruction instr = ir.firstInstructionInCodeOrder(); instr != null; instr = nextInst) { 329 // can't use iterator, since we modify instruction stream 330 nextInst = instr.nextInstructionInCodeOrder(); 331 if (TypeCheck.conforms(instr)) { 332 // get compared variables 333 Operand obj = TypeCheck.getRef(instr); 334 // create the instruction and insert it 335 if (obj.isRegister()) { 336 RegisterOperand lval = (RegisterOperand) obj.copy(); 337 lval.clearDeclaredType(); 338 if (lval.getType().isLoaded() && lval.getType().isClassType() && lval.getType().peekType().asClass().isFinal()) { 339 lval.setPreciseType(TypeCheck.getType(instr).getTypeRef()); 340 } else { 341 lval.clearPreciseType(); 342 lval.setType(TypeCheck.getType(instr).getTypeRef()); 343 } 344 Instruction s = GuardedUnary.create(PI, lval, obj.copy(), null); 345 s.position = instr.position; 346 s.bcIndex = instr.bcIndex; 347 Operand iGuard = TypeCheck.getGuard(instr); 348 if (iGuard != null) { 349 Operand sGuard = iGuard.copy(); 350 GuardedUnary.setGuard(s, sGuard); 351 } 352 instr.insertAfter(s); 353 } 354 } 355 } 356 } 357 358 /** 359 * Change all PI nodes to INT_MOVE instructions 360 * <p> Side effect: invalidates SSA state 361 * 362 * @param ir the governing IR 363 */ 364 static void cleanUp(IR ir) { 365 for (Enumeration<Instruction> e = ir.forwardInstrEnumerator(); e.hasMoreElements();) { 366 Instruction s = e.nextElement(); 367 if (s.operator() == PI) { 368 RegisterOperand result = GuardedUnary.getResult(s); 369 Operator mv = IRTools.getMoveOp(result.getType()); 370 Operand val = GuardedUnary.getVal(s); 371 Move.mutate(s, mv, result, val); 372 } 373 } 374 // invalidate SSA state 375 ir.actualSSAOptions = null; 376 } 377 378 /* 379 * TODO Convert to JavaDoc and add missing tags. 380 * <p> 381 * Get the instruction a Pi node is linked to. 382 * <strong>PRECONDITION: </strong> register lists computed and valid. 383 */ 384 public static Instruction getGenerator(Instruction def) { 385 if (def.operator() != PI) { 386 throw new OptimizingCompilerException("Not a PI Node!"); 387 } 388 Operand g = GuardedUnary.getGuard(def); 389 Instruction link = g.asRegister().getRegister().defList.instruction; 390 return link; 391 } 392 393 /* 394 * TODO Convert to JavaDoc and add missing tags. 395 * <p> 396 * Is an instruction a Pi node linked to the <em>not taken</em> edge of 397 * a conditional branch instruction? 398 */ 399 public static boolean isNotTakenPi(Instruction def) { 400 if (def.operator() != PI) { 401 return false; 402 } 403 Operand g = GuardedUnary.getGuard(def); 404 return g.asRegister().isNotTaken(); 405 } 406 407 /* 408 * TODO Convert to JavaDoc and add missing tags. 409 * <p> 410 * Is an instruction a Pi node linked to the <em>taken</em> edge of 411 * a conditional branch instruction? 412 */ 413 public static boolean isTakenPi(Instruction def) { 414 if (def.operator() != PI) { 415 return false; 416 } 417 Operand g = GuardedUnary.getGuard(def); 418 return g.asRegister().isTaken(); 419 } 420 421 /* 422 * TODO Convert to JavaDoc and add missing tags. 423 * <p> 424 * Is an instruction a Pi node linked to a bounds-check? 425 */ 426 public static boolean isBoundsCheckPi(Instruction def) { 427 if (def.operator() != PI) { 428 return false; 429 } 430 Operand g = GuardedUnary.getGuard(def); 431 return g.asRegister().isBoundsCheck(); 432 } 433 434 /* 435 * TODO Convert to JavaDoc and add missing tags. 436 * <p> 437 * Is an instruction a Pi node linked to a null-check? 438 */ 439 public static boolean isNullCheckPi(Instruction def) { 440 if (def.operator() != PI) { 441 return false; 442 } 443 Operand g = GuardedUnary.getGuard(def); 444 return g.asRegister().isNullCheck(); 445 } 446}