1 /* 2 * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 package org.graalvm.compiler.lir; 26 27 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.CONST; 28 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.HINT; 29 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.OUTGOING; 30 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG; 31 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.STACK; 32 33 import java.util.ArrayList; 34 import java.util.EnumSet; 35 36 import jdk.internal.vm.compiler.collections.EconomicSet; 37 import org.graalvm.compiler.asm.Label; 38 import org.graalvm.compiler.core.common.GraalOptions; 39 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase; 40 import org.graalvm.compiler.debug.GraalError; 41 import org.graalvm.compiler.lir.asm.CompilationResultBuilder; 42 import org.graalvm.compiler.lir.framemap.FrameMap; 43 44 import jdk.vm.ci.code.Register; 45 import jdk.vm.ci.code.RegisterSaveLayout; 46 import jdk.vm.ci.code.StackSlot; 47 import jdk.vm.ci.meta.AllocatableValue; 48 import jdk.vm.ci.meta.Constant; 49 import jdk.vm.ci.meta.Value; 50 51 /** 52 * A collection of machine-independent LIR operations, as well as interfaces to be implemented for 53 * specific kinds or LIR operations. 54 */ 55 public class StandardOp { 56 57 /** 58 * A block delimiter. Every well formed block must contain exactly one such operation and it 59 * must be the last operation in the block. 60 */ 61 public interface BlockEndOp { 62 } 63 64 public interface NullCheck { 65 Value getCheckedValue(); 66 67 LIRFrameState getState(); 68 } 69 70 public interface ImplicitNullCheck { 71 boolean makeNullCheckFor(Value value, LIRFrameState nullCheckState, int implicitNullCheckLimit); 72 } 73 74 public interface LabelHoldingOp { 75 Label getLabel(); 76 } 77 78 /** 79 * LIR operation that defines the position of a label. 80 */ 81 public static final class LabelOp extends LIRInstruction implements LabelHoldingOp { 82 public static final LIRInstructionClass<LabelOp> TYPE = LIRInstructionClass.create(LabelOp.class); 83 public static final EnumSet<OperandFlag> incomingFlags = EnumSet.of(REG, STACK); 84 85 /** 86 * In the LIR, every register and variable must be defined before it is used. For method 87 * parameters that are passed in fixed registers, exception objects passed to the exception 88 * handler in a fixed register, or any other use of a fixed register not defined in this 89 * method, an artificial definition is necessary. To avoid spill moves to be inserted 90 * between the label at the beginning of a block an an actual definition in the second 91 * instruction of a block, the registers are defined here in the label. 92 */ 93 @Def({REG, STACK}) private Value[] incomingValues; 94 private final Label label; 95 private final boolean align; 96 private int numbPhis; 97 98 public LabelOp(Label label, boolean align) { 99 super(TYPE); 100 this.label = label; 101 this.align = align; 102 this.incomingValues = Value.NO_VALUES; 103 this.numbPhis = 0; 104 } 105 106 public void setPhiValues(Value[] values) { 107 setIncomingValues(values); 108 setNumberOfPhis(values.length); 109 } 110 111 private void setNumberOfPhis(int numPhis) { 112 assert numbPhis == 0; 113 numbPhis = numPhis; 114 } 115 116 public int getPhiSize() { 117 return numbPhis; 118 } 119 120 public void setIncomingValues(Value[] values) { 121 assert this.incomingValues.length == 0; 122 assert values != null; 123 this.incomingValues = values; 124 } 125 126 public int getIncomingSize() { 127 return incomingValues.length; 128 } 129 130 public Value getIncomingValue(int idx) { 131 assert checkRange(idx); 132 return incomingValues[idx]; 133 } 134 135 public void clearIncomingValues() { 136 incomingValues = Value.NO_VALUES; 137 } 138 139 public void addIncomingValues(Value[] values) { 140 if (incomingValues.length == 0) { 141 setIncomingValues(values); 142 return; 143 } 144 int t = incomingValues.length + values.length; 145 Value[] newArray = new Value[t]; 146 System.arraycopy(incomingValues, 0, newArray, 0, incomingValues.length); 147 System.arraycopy(values, 0, newArray, incomingValues.length, values.length); 148 incomingValues = newArray; 149 } 150 151 private boolean checkRange(int idx) { 152 return idx < incomingValues.length; 153 } 154 155 @Override 156 public void emitCode(CompilationResultBuilder crb) { 157 if (align) { 158 crb.asm.align(GraalOptions.LoopHeaderAlignment.getValue(crb.getOptions())); 159 } 160 crb.asm.bind(label); 161 } 162 163 @Override 164 public Label getLabel() { 165 return label; 166 } 167 168 /** 169 * @return true if this label acts as a PhiIn. 170 */ 171 public boolean isPhiIn() { 172 return getPhiSize() > 0; 173 } 174 175 public void forEachIncomingValue(InstructionValueProcedure proc) { 176 for (int i = 0; i < incomingValues.length; i++) { 177 incomingValues[i] = proc.doValue(this, incomingValues[i], OperandMode.DEF, incomingFlags); 178 } 179 } 180 } 181 182 /** 183 * LIR operation that is an unconditional jump to a {@link #destination()}. 184 */ 185 public static class JumpOp extends LIRInstruction implements BlockEndOp { 186 public static final LIRInstructionClass<JumpOp> TYPE = LIRInstructionClass.create(JumpOp.class); 187 public static final EnumSet<OperandFlag> outgoingFlags = EnumSet.of(REG, STACK, CONST, OUTGOING); 188 189 @Alive({REG, STACK, CONST, OUTGOING}) private Value[] outgoingValues; 190 191 private final LabelRef destination; 192 193 public JumpOp(LabelRef destination) { 194 this(TYPE, destination); 195 } 196 197 protected JumpOp(LIRInstructionClass<? extends JumpOp> c, LabelRef destination) { 198 super(c); 199 this.destination = destination; 200 this.outgoingValues = Value.NO_VALUES; 201 } 202 203 @Override 204 public void emitCode(CompilationResultBuilder crb) { 205 if (!crb.isSuccessorEdge(destination)) { 206 crb.asm.jmp(destination.label()); 207 } 208 } 209 210 public LabelRef destination() { 211 return destination; 212 } 213 214 public void setPhiValues(Value[] values) { 215 assert this.outgoingValues.length == 0; 216 assert values != null; 217 this.outgoingValues = values; 218 } 219 220 public int getPhiSize() { 221 return outgoingValues.length; 222 } 223 224 public Value getOutgoingValue(int idx) { 225 assert checkRange(idx); 226 return outgoingValues[idx]; 227 } 228 229 public void clearOutgoingValues() { 230 outgoingValues = Value.NO_VALUES; 231 } 232 233 private boolean checkRange(int idx) { 234 return idx < outgoingValues.length; 235 } 236 } 237 238 /** 239 * Marker interface for a LIR operation that is a conditional jump. 240 */ 241 public interface BranchOp extends BlockEndOp { 242 } 243 244 /** 245 * Marker interface for a LIR operation that moves a value to {@link #getResult()}. 246 */ 247 public interface MoveOp { 248 249 AllocatableValue getResult(); 250 251 // Checkstyle: stop 252 static MoveOp asMoveOp(LIRInstruction op) { 253 return (MoveOp) op; 254 } 255 // Checkstyle: resume 256 257 static boolean isMoveOp(LIRInstruction op) { 258 return op.isMoveOp(); 259 } 260 } 261 262 /** 263 * Marker interface for a LIR operation that moves some non-constant value to another location. 264 */ 265 public interface ValueMoveOp extends MoveOp { 266 267 AllocatableValue getInput(); 268 269 // Checkstyle: stop 270 static ValueMoveOp asValueMoveOp(LIRInstruction op) { 271 return (ValueMoveOp) op; 272 } 273 // Checkstyle: resume 274 275 static boolean isValueMoveOp(LIRInstruction op) { 276 return op.isValueMoveOp(); 277 } 278 } 279 280 /** 281 * Marker interface for a LIR operation that loads a {@link #getConstant()}. 282 */ 283 public interface LoadConstantOp extends MoveOp { 284 285 Constant getConstant(); 286 287 // Checkstyle: stop 288 static LoadConstantOp asLoadConstantOp(LIRInstruction op) { 289 return (LoadConstantOp) op; 290 } 291 // Checkstyle: resume 292 293 static boolean isLoadConstantOp(LIRInstruction op) { 294 return op.isLoadConstantOp(); 295 } 296 } 297 298 /** 299 * An operation that saves registers to the stack. The set of saved registers can be 300 * {@linkplain #remove(EconomicSet) pruned} and a mapping from registers to the frame slots in 301 * which they are saved can be {@linkplain #getMap(FrameMap) retrieved}. 302 */ 303 public interface SaveRegistersOp { 304 305 /** 306 * Determines if the {@link #remove(EconomicSet)} operation is supported for this object. 307 */ 308 boolean supportsRemove(); 309 310 /** 311 * Prunes {@code doNotSave} from the registers saved by this operation. 312 * 313 * @param doNotSave registers that should not be saved by this operation 314 * @return the number of registers pruned 315 * @throws UnsupportedOperationException if removal is not {@linkplain #supportsRemove() 316 * supported} 317 */ 318 int remove(EconomicSet<Register> doNotSave); 319 320 /** 321 * Gets a map from the saved registers saved by this operation to the frame slots in which 322 * they are saved. 323 * 324 * @param frameMap used to {@linkplain FrameMap#offsetForStackSlot(StackSlot) convert} a 325 * virtual slot to a frame slot index 326 */ 327 RegisterSaveLayout getMap(FrameMap frameMap); 328 329 } 330 331 /** 332 * A LIR operation that does nothing. If the operation records its position, it can be 333 * subsequently {@linkplain #replace(LIR, LIRInstruction) replaced}. 334 */ 335 public static final class NoOp extends LIRInstruction { 336 public static final LIRInstructionClass<NoOp> TYPE = LIRInstructionClass.create(NoOp.class); 337 338 /** 339 * The block in which this instruction is located. 340 */ 341 final AbstractBlockBase<?> block; 342 343 /** 344 * The block index of this instruction. 345 */ 346 final int index; 347 348 public NoOp(AbstractBlockBase<?> block, int index) { 349 super(TYPE); 350 this.block = block; 351 this.index = index; 352 } 353 354 public void replace(LIR lir, LIRInstruction replacement) { 355 ArrayList<LIRInstruction> instructions = lir.getLIRforBlock(block); 356 assert instructions.get(index).equals(this) : String.format("Replacing the wrong instruction: %s instead of %s", instructions.get(index), this); 357 instructions.set(index, replacement); 358 } 359 360 public void remove(LIR lir) { 361 ArrayList<LIRInstruction> instructions = lir.getLIRforBlock(block); 362 assert instructions.get(index).equals(this) : String.format("Removing the wrong instruction: %s instead of %s", instructions.get(index), this); 363 instructions.remove(index); 364 } 365 366 @Override 367 public void emitCode(CompilationResultBuilder crb) { 368 if (block != null) { 369 throw new GraalError(this + " should have been replaced"); 370 } 371 } 372 } 373 374 @Opcode("BLACKHOLE") 375 public static final class BlackholeOp extends LIRInstruction { 376 public static final LIRInstructionClass<BlackholeOp> TYPE = LIRInstructionClass.create(BlackholeOp.class); 377 378 @Use({REG, STACK, CONST}) private Value value; 379 380 public BlackholeOp(Value value) { 381 super(TYPE); 382 this.value = value; 383 } 384 385 @Override 386 public void emitCode(CompilationResultBuilder crb) { 387 // do nothing, just keep value alive until at least here 388 } 389 } 390 391 public static final class BindToRegisterOp extends LIRInstruction { 392 public static final LIRInstructionClass<BindToRegisterOp> TYPE = LIRInstructionClass.create(BindToRegisterOp.class); 393 394 @Use({REG}) private Value value; 395 396 public BindToRegisterOp(Value value) { 397 super(TYPE); 398 this.value = value; 399 } 400 401 @Override 402 public void emitCode(CompilationResultBuilder crb) { 403 // do nothing, just keep value alive until at least here 404 } 405 } 406 407 @Opcode("SPILLREGISTERS") 408 public static final class SpillRegistersOp extends LIRInstruction { 409 public static final LIRInstructionClass<SpillRegistersOp> TYPE = LIRInstructionClass.create(SpillRegistersOp.class); 410 411 public SpillRegistersOp() { 412 super(TYPE); 413 } 414 415 @Override 416 public boolean destroysCallerSavedRegisters() { 417 return true; 418 } 419 420 @Override 421 public void emitCode(CompilationResultBuilder crb) { 422 // do nothing, just keep value alive until at least here 423 } 424 } 425 426 public static final class StackMove extends LIRInstruction implements ValueMoveOp { 427 public static final LIRInstructionClass<StackMove> TYPE = LIRInstructionClass.create(StackMove.class); 428 429 @Def({STACK, HINT}) protected AllocatableValue result; 430 @Use({STACK}) protected AllocatableValue input; 431 432 public StackMove(AllocatableValue result, AllocatableValue input) { 433 super(TYPE); 434 this.result = result; 435 this.input = input; 436 } 437 438 @Override 439 public void emitCode(CompilationResultBuilder crb) { 440 throw new GraalError(this + " should have been removed"); 441 } 442 443 @Override 444 public AllocatableValue getInput() { 445 return input; 446 } 447 448 @Override 449 public AllocatableValue getResult() { 450 return result; 451 } 452 } 453 454 }