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; 014 015 import org.jikesrvm.VM; 016 import org.jikesrvm.compilers.opt.driver.CompilerPhase; 017 import org.jikesrvm.compilers.opt.ir.Binary; 018 import org.jikesrvm.compilers.opt.ir.GuardCarrier; 019 import org.jikesrvm.compilers.opt.ir.GuardResultCarrier; 020 import org.jikesrvm.compilers.opt.ir.Move; 021 import org.jikesrvm.compilers.opt.ir.NullCheck; 022 import org.jikesrvm.compilers.opt.ir.BasicBlock; 023 import org.jikesrvm.compilers.opt.ir.IR; 024 import org.jikesrvm.compilers.opt.ir.Instruction; 025 import org.jikesrvm.compilers.opt.ir.Operator; 026 import org.jikesrvm.compilers.opt.ir.Operators; 027 import org.jikesrvm.compilers.opt.ir.operand.MemoryOperand; 028 import org.jikesrvm.compilers.opt.ir.operand.Operand; 029 030 import static org.jikesrvm.compilers.opt.ir.Operators.GUARD_COMBINE; 031 import static org.jikesrvm.compilers.opt.ir.Operators.GUARD_MOVE; 032 import static org.jikesrvm.compilers.opt.ir.Operators.NULL_CHECK; 033 import org.vmmagic.unboxed.Offset; 034 035 /** 036 * This module performs two tasks: 037 * <ul> 038 * <li> (1) When possible, it folds null checks into the first load/store 039 * that is being guarded by the null check 040 * <li> (2) It removes all validation registers from the IR 041 * </ul> 042 * 043 * <p> Doing (1) more or less implies either (a) doing (2) or 044 * (b) making large changes to the MIR operator set such that 045 * all load/stores produce validation results. 046 * Although this would be possible, it would not be a trivial change. 047 * So, until we have an urgent need to preserve guard operands all 048 * the way through the MIR, we'll take the easy way out. 049 */ 050 public class NullCheckCombining extends CompilerPhase { 051 052 /** 053 * Return this instance of this phase. This phase contains no 054 * per-compilation instance fields. 055 * @param ir not used 056 * @return this 057 */ 058 @Override 059 public CompilerPhase newExecution(IR ir) { 060 return this; 061 } 062 063 @Override 064 public final String getName() { 065 return "NullCheckCombining"; 066 } 067 068 /** 069 * Perform nullcheck combining and validation register removal. 070 * 071 * @param ir the IR to transform 072 */ 073 @Override 074 public void perform(IR ir) { 075 for (BasicBlock bb = ir.firstBasicBlockInCodeOrder(); bb != null; bb = bb.nextBasicBlockInCodeOrder()) { 076 if (!bb.isEmpty()) { 077 Instruction lastInstr = bb.lastInstruction(); 078 079 boolean combined; 080 boolean remaining; 081 // (1) Combine null checks in bb into the first load/store in 082 // bb they guard. 083 // Restrict this to respect PEI ordering. 084 // Only do locally, since we don't understand control flow here. 085 // We could be more aggressive about moving PEIs past stores 086 // by determining which stores actually update global or 087 // handler-visible state. 088 do { 089 combined = remaining = false; 090 Instruction activeNullCheck = null; 091 Operand activeGuard = null; 092 for (Instruction instr = bb.firstRealInstruction(), 093 nextInstr = null; instr != lastInstr; instr = nextInstr) { 094 nextInstr = instr.nextInstructionInCodeOrder(); 095 Operator op = instr.operator(); 096 if (op == GUARD_MOVE) { 097 if (activeGuard != null && Move.getVal(instr).similar(activeGuard)) { 098 activeGuard = Move.getResult(instr); 099 } 100 } else if (op == GUARD_COMBINE) { 101 if (activeGuard != null && 102 (Binary.getVal1(instr) == activeGuard || Binary.getVal2(instr) == activeGuard)) { 103 activeGuard = null; 104 } 105 } else if (op == NULL_CHECK) { 106 remaining |= (activeGuard == null); 107 activeGuard = NullCheck.getGuardResult(instr); 108 activeNullCheck = instr; 109 } else if (isExplicitStore(instr, op)) { 110 if (instr.isPEI()) { 111 // can't reorder PEI's 112 // NOTE: don't mark remaining, since we'd hit the same problem instr again. 113 activeGuard = null; 114 } else { 115 if (activeGuard != null && canFold(instr, activeGuard, true)) { 116 instr.markAsPEI(); 117 activeNullCheck.remove(); 118 activeGuard = null; 119 combined = true; 120 } 121 remaining |= (activeGuard == null); 122 activeGuard = null; // don't attempt to move PEI past a store; could do better. 123 } 124 } else if (isExplicitLoad(instr, op)) { 125 if (activeGuard != null && canFold(instr, activeGuard, false)) { 126 instr.markAsPEI(); 127 activeNullCheck.remove(); 128 activeGuard = null; 129 combined = true; 130 } else if (instr.isPEI()) { 131 // can't reorder PEI's 132 // NOTE: don't mark remaining, since we'd hit the same problem instr again. 133 activeGuard = null; 134 } 135 } else { 136 if (op.isImplicitStore() || op.isPEI()) { 137 // NOTE: don't mark remaining, since we'd hit the same problem instr again. 138 activeGuard = null; // don't reorder PEI's; be conservative about stores. 139 } 140 } 141 } 142 } while (combined & remaining); 143 144 // (2) Blow away all validation registers in bb. 145 for (Instruction instr = bb.firstRealInstruction(), nextInstr = null; instr != lastInstr; instr = nextInstr) 146 { 147 nextInstr = instr.nextInstructionInCodeOrder(); 148 Operator op = instr.operator(); 149 if (op == GUARD_MOVE || op == GUARD_COMBINE) { 150 instr.remove(); 151 } else { 152 if (GuardResultCarrier.conforms(op)) { 153 GuardResultCarrier.setGuardResult(instr, null); 154 } 155 if (GuardCarrier.conforms(op)) { 156 GuardCarrier.setGuard(instr, null); 157 } 158 } 159 } 160 } 161 } 162 } 163 164 private boolean isExplicitStore(Instruction s, Operator op) { 165 if (op.isExplicitStore()) return true; 166 for (int i = 0, n = s.getNumberOfDefs(); i < n; i++) { 167 if (s.getOperand(i) instanceof MemoryOperand) return true; 168 } 169 return false; 170 } 171 172 private boolean isExplicitLoad(Instruction s, Operator op) { 173 if (op.isExplicitLoad()) return true; 174 int numOps = s.getNumberOfOperands(); 175 int numUses = s.getNumberOfUses(); 176 for (int i = numOps - numUses; i < numOps; i++) { 177 if (s.getOperand(i) instanceof MemoryOperand) { 178 return true; 179 } 180 } 181 return false; 182 } 183 184 private boolean canFold(Instruction s, Operand activeGuard, boolean isStore) { 185 if (GuardCarrier.conforms(s) && GuardCarrier.hasGuard(s) && activeGuard.similar(GuardCarrier.getGuard(s))) { 186 if (!VM.ExplicitlyGuardLowMemory) return true; 187 // TODO: In theory, lowMemory is protected even on AIX. 188 // However, enabling this causes a large number of failures. 189 // Figure out why that is the case and enable some variant of this. 190 // if (isStore) return true; // Even on AIX low memory is write protected 191 return VM.BuildForPowerPC && Operators.helper.canFoldNullCheckAndLoad(s); 192 } 193 for (int i = 0, n = s.getNumberOfOperands(); i < n; i++) { 194 Operand op = s.getOperand(i); 195 if (op instanceof MemoryOperand) { 196 MemoryOperand memOp = (MemoryOperand) op; 197 if (activeGuard.similar(memOp.guard)) { 198 return !VM.ExplicitlyGuardLowMemory || isStore || ((memOp.index == null) && (memOp.disp.sLT(Offset.zero()))); 199 } 200 } 201 } 202 return false; 203 } 204 } 205 206 207