| /* |
| * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| package org.graalvm.compiler.lir; |
| |
| import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.CONST; |
| import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.HINT; |
| import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.OUTGOING; |
| import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG; |
| import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.STACK; |
| |
| import java.util.EnumSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.graalvm.compiler.asm.Label; |
| import org.graalvm.compiler.core.common.cfg.AbstractBlockBase; |
| import org.graalvm.compiler.debug.GraalError; |
| import org.graalvm.compiler.lir.asm.CompilationResultBuilder; |
| import org.graalvm.compiler.lir.framemap.FrameMap; |
| import org.graalvm.compiler.lir.ssa.SSAUtil; |
| |
| import jdk.vm.ci.code.Register; |
| import jdk.vm.ci.code.RegisterSaveLayout; |
| import jdk.vm.ci.code.StackSlot; |
| import jdk.vm.ci.meta.AllocatableValue; |
| import jdk.vm.ci.meta.Constant; |
| import jdk.vm.ci.meta.Value; |
| |
| /** |
| * A collection of machine-independent LIR operations, as well as interfaces to be implemented for |
| * specific kinds or LIR operations. |
| */ |
| public class StandardOp { |
| |
| /** |
| * A block delimiter. Every well formed block must contain exactly one such operation and it |
| * must be the last operation in the block. |
| */ |
| public interface BlockEndOp { |
| void setOutgoingValues(Value[] values); |
| |
| int getOutgoingSize(); |
| |
| Value getOutgoingValue(int idx); |
| |
| int addOutgoingValues(Value[] values); |
| |
| void clearOutgoingValues(); |
| |
| void forEachOutgoingValue(InstructionValueProcedure proc); |
| |
| /** |
| * The number of {@link SSAUtil phi} operands in the {@link #getOutgoingValue outgoing} |
| * array. |
| */ |
| int getPhiSize(); |
| } |
| |
| public interface NullCheck { |
| Value getCheckedValue(); |
| |
| LIRFrameState getState(); |
| } |
| |
| public interface ImplicitNullCheck { |
| boolean makeNullCheckFor(Value value, LIRFrameState nullCheckState, int implicitNullCheckLimit); |
| } |
| |
| /** |
| * LIR operation that defines the position of a label. |
| */ |
| public static final class LabelOp extends LIRInstruction { |
| public static final LIRInstructionClass<LabelOp> TYPE = LIRInstructionClass.create(LabelOp.class); |
| public static final EnumSet<OperandFlag> incomingFlags = EnumSet.of(REG, STACK); |
| |
| /** |
| * In the LIR, every register and variable must be defined before it is used. For method |
| * parameters that are passed in fixed registers, exception objects passed to the exception |
| * handler in a fixed register, or any other use of a fixed register not defined in this |
| * method, an artificial definition is necessary. To avoid spill moves to be inserted |
| * between the label at the beginning of a block an an actual definition in the second |
| * instruction of a block, the registers are defined here in the label. |
| */ |
| @Def({REG, STACK}) private Value[] incomingValues; |
| private final Label label; |
| private final boolean align; |
| private int numbPhis; |
| |
| public LabelOp(Label label, boolean align) { |
| super(TYPE); |
| this.label = label; |
| this.align = align; |
| this.incomingValues = Value.NO_VALUES; |
| this.numbPhis = 0; |
| } |
| |
| public void setPhiValues(Value[] values) { |
| setIncomingValues(values); |
| setNumberOfPhis(values.length); |
| } |
| |
| private void setNumberOfPhis(int numPhis) { |
| assert numbPhis == 0; |
| numbPhis = numPhis; |
| } |
| |
| /** |
| * @see BlockEndOp#getPhiSize |
| */ |
| public int getPhiSize() { |
| return numbPhis; |
| } |
| |
| public void setIncomingValues(Value[] values) { |
| assert this.incomingValues.length == 0; |
| assert values != null; |
| this.incomingValues = values; |
| } |
| |
| public int getIncomingSize() { |
| return incomingValues.length; |
| } |
| |
| public Value getIncomingValue(int idx) { |
| assert checkRange(idx); |
| return incomingValues[idx]; |
| } |
| |
| public void clearIncomingValues() { |
| incomingValues = Value.NO_VALUES; |
| } |
| |
| public void addIncomingValues(Value[] values) { |
| if (incomingValues.length == 0) { |
| setIncomingValues(values); |
| return; |
| } |
| int t = incomingValues.length + values.length; |
| Value[] newArray = new Value[t]; |
| System.arraycopy(incomingValues, 0, newArray, 0, incomingValues.length); |
| System.arraycopy(values, 0, newArray, incomingValues.length, values.length); |
| incomingValues = newArray; |
| } |
| |
| private boolean checkRange(int idx) { |
| return idx < incomingValues.length; |
| } |
| |
| @Override |
| public void emitCode(CompilationResultBuilder crb) { |
| if (align) { |
| crb.asm.align(crb.target.wordSize * 2); |
| } |
| crb.asm.bind(label); |
| } |
| |
| public Label getLabel() { |
| return label; |
| } |
| |
| /** |
| * @return true if this label acts as a PhiIn. |
| */ |
| public boolean isPhiIn() { |
| return getPhiSize() > 0; |
| } |
| |
| public void forEachIncomingValue(InstructionValueProcedure proc) { |
| for (int i = 0; i < incomingValues.length; i++) { |
| incomingValues[i] = proc.doValue(this, incomingValues[i], OperandMode.DEF, incomingFlags); |
| } |
| } |
| } |
| |
| public abstract static class AbstractBlockEndOp extends LIRInstruction implements BlockEndOp { |
| public static final LIRInstructionClass<AbstractBlockEndOp> TYPE = LIRInstructionClass.create(AbstractBlockEndOp.class); |
| public static final EnumSet<OperandFlag> outgoingFlags = EnumSet.of(REG, STACK, CONST, OUTGOING); |
| |
| @Alive({REG, STACK, CONST, OUTGOING}) private Value[] outgoingValues; |
| private int numberOfPhis; |
| |
| protected AbstractBlockEndOp(LIRInstructionClass<? extends AbstractBlockEndOp> c) { |
| super(c); |
| this.outgoingValues = Value.NO_VALUES; |
| } |
| |
| public void setPhiValues(Value[] values) { |
| setOutgoingValues(values); |
| setNumberOfPhis(values.length); |
| } |
| |
| private void setNumberOfPhis(int numPhis) { |
| assert numberOfPhis == 0; |
| numberOfPhis = numPhis; |
| } |
| |
| @Override |
| public int getPhiSize() { |
| return numberOfPhis; |
| } |
| |
| @Override |
| public void setOutgoingValues(Value[] values) { |
| assert this.outgoingValues.length == 0; |
| assert values != null; |
| this.outgoingValues = values; |
| } |
| |
| @Override |
| public int getOutgoingSize() { |
| return outgoingValues.length; |
| } |
| |
| @Override |
| public Value getOutgoingValue(int idx) { |
| assert checkRange(idx); |
| return outgoingValues[idx]; |
| } |
| |
| @Override |
| public void clearOutgoingValues() { |
| outgoingValues = Value.NO_VALUES; |
| } |
| |
| @Override |
| public int addOutgoingValues(Value[] values) { |
| if (outgoingValues.length == 0) { |
| setOutgoingValues(values); |
| return values.length; |
| } |
| int t = outgoingValues.length + values.length; |
| Value[] newArray = new Value[t]; |
| System.arraycopy(outgoingValues, 0, newArray, 0, outgoingValues.length); |
| System.arraycopy(values, 0, newArray, outgoingValues.length, values.length); |
| outgoingValues = newArray; |
| return t; |
| } |
| |
| private boolean checkRange(int idx) { |
| return idx < outgoingValues.length; |
| } |
| |
| @Override |
| public void forEachOutgoingValue(InstructionValueProcedure proc) { |
| for (int i = 0; i < outgoingValues.length; i++) { |
| outgoingValues[i] = proc.doValue(this, outgoingValues[i], OperandMode.ALIVE, outgoingFlags); |
| } |
| } |
| } |
| |
| /** |
| * LIR operation that is an unconditional jump to a {@link #destination()}. |
| */ |
| public static class JumpOp extends AbstractBlockEndOp { |
| public static final LIRInstructionClass<JumpOp> TYPE = LIRInstructionClass.create(JumpOp.class); |
| |
| private final LabelRef destination; |
| |
| public JumpOp(LabelRef destination) { |
| this(TYPE, destination); |
| } |
| |
| protected JumpOp(LIRInstructionClass<? extends JumpOp> c, LabelRef destination) { |
| super(c); |
| this.destination = destination; |
| } |
| |
| @Override |
| public void emitCode(CompilationResultBuilder crb) { |
| if (!crb.isSuccessorEdge(destination)) { |
| crb.asm.jmp(destination.label()); |
| } |
| } |
| |
| public LabelRef destination() { |
| return destination; |
| } |
| } |
| |
| /** |
| * Marker interface for a LIR operation that is a conditional jump. |
| */ |
| public interface BranchOp extends BlockEndOp { |
| } |
| |
| /** |
| * Marker interface for a LIR operation that moves a value to {@link #getResult()}. |
| */ |
| public interface MoveOp { |
| |
| AllocatableValue getResult(); |
| } |
| |
| /** |
| * Marker interface for a LIR operation that moves some non-constant value to another location. |
| */ |
| public interface ValueMoveOp extends MoveOp { |
| |
| AllocatableValue getInput(); |
| } |
| |
| /** |
| * Marker interface for a LIR operation that loads a {@link #getConstant()}. |
| */ |
| public interface LoadConstantOp extends MoveOp { |
| |
| Constant getConstant(); |
| } |
| |
| /** |
| * An operation that saves registers to the stack. The set of saved registers can be |
| * {@linkplain #remove(Set) pruned} and a mapping from registers to the frame slots in which |
| * they are saved can be {@linkplain #getMap(FrameMap) retrieved}. |
| */ |
| public interface SaveRegistersOp { |
| |
| /** |
| * Determines if the {@link #remove(Set)} operation is supported for this object. |
| */ |
| boolean supportsRemove(); |
| |
| /** |
| * Prunes {@code doNotSave} from the registers saved by this operation. |
| * |
| * @param doNotSave registers that should not be saved by this operation |
| * @return the number of registers pruned |
| * @throws UnsupportedOperationException if removal is not {@linkplain #supportsRemove() |
| * supported} |
| */ |
| int remove(Set<Register> doNotSave); |
| |
| /** |
| * Gets a map from the saved registers saved by this operation to the frame slots in which |
| * they are saved. |
| * |
| * @param frameMap used to {@linkplain FrameMap#offsetForStackSlot(StackSlot) convert} a |
| * virtual slot to a frame slot index |
| */ |
| RegisterSaveLayout getMap(FrameMap frameMap); |
| |
| } |
| |
| /** |
| * A LIR operation that does nothing. If the operation records its position, it can be |
| * subsequently {@linkplain #replace(LIR, LIRInstruction) replaced}. |
| */ |
| public static final class NoOp extends LIRInstruction { |
| public static final LIRInstructionClass<NoOp> TYPE = LIRInstructionClass.create(NoOp.class); |
| |
| /** |
| * The block in which this instruction is located. |
| */ |
| final AbstractBlockBase<?> block; |
| |
| /** |
| * The block index of this instruction. |
| */ |
| final int index; |
| |
| public NoOp(AbstractBlockBase<?> block, int index) { |
| super(TYPE); |
| this.block = block; |
| this.index = index; |
| } |
| |
| public void replace(LIR lir, LIRInstruction replacement) { |
| List<LIRInstruction> instructions = lir.getLIRforBlock(block); |
| assert instructions.get(index).equals(this) : String.format("Replacing the wrong instruction: %s instead of %s", instructions.get(index), this); |
| instructions.set(index, replacement); |
| } |
| |
| public void remove(LIR lir) { |
| List<LIRInstruction> instructions = lir.getLIRforBlock(block); |
| assert instructions.get(index).equals(this) : String.format("Removing the wrong instruction: %s instead of %s", instructions.get(index), this); |
| instructions.remove(index); |
| } |
| |
| @Override |
| public void emitCode(CompilationResultBuilder crb) { |
| if (block != null) { |
| throw new GraalError(this + " should have been replaced"); |
| } |
| } |
| } |
| |
| @Opcode("BLACKHOLE") |
| public static final class BlackholeOp extends LIRInstruction { |
| public static final LIRInstructionClass<BlackholeOp> TYPE = LIRInstructionClass.create(BlackholeOp.class); |
| |
| @Use({REG, STACK, CONST}) private Value value; |
| |
| public BlackholeOp(Value value) { |
| super(TYPE); |
| this.value = value; |
| } |
| |
| @Override |
| public void emitCode(CompilationResultBuilder crb) { |
| // do nothing, just keep value alive until at least here |
| } |
| } |
| |
| public static final class BindToRegisterOp extends LIRInstruction { |
| public static final LIRInstructionClass<BindToRegisterOp> TYPE = LIRInstructionClass.create(BindToRegisterOp.class); |
| |
| @Use({REG}) private Value value; |
| |
| public BindToRegisterOp(Value value) { |
| super(TYPE); |
| this.value = value; |
| } |
| |
| @Override |
| public void emitCode(CompilationResultBuilder crb) { |
| // do nothing, just keep value alive until at least here |
| } |
| } |
| |
| @Opcode("SPILLREGISTERS") |
| public static final class SpillRegistersOp extends LIRInstruction { |
| public static final LIRInstructionClass<SpillRegistersOp> TYPE = LIRInstructionClass.create(SpillRegistersOp.class); |
| |
| public SpillRegistersOp() { |
| super(TYPE); |
| } |
| |
| @Override |
| public boolean destroysCallerSavedRegisters() { |
| return true; |
| } |
| |
| @Override |
| public void emitCode(CompilationResultBuilder crb) { |
| // do nothing, just keep value alive until at least here |
| } |
| } |
| |
| public static final class StackMove extends LIRInstruction implements ValueMoveOp { |
| public static final LIRInstructionClass<StackMove> TYPE = LIRInstructionClass.create(StackMove.class); |
| |
| @Def({STACK, HINT}) protected AllocatableValue result; |
| @Use({STACK}) protected AllocatableValue input; |
| |
| public StackMove(AllocatableValue result, AllocatableValue input) { |
| super(TYPE); |
| this.result = result; |
| this.input = input; |
| } |
| |
| @Override |
| public void emitCode(CompilationResultBuilder crb) { |
| throw new GraalError(this + " should have been removed"); |
| } |
| |
| @Override |
| public AllocatableValue getInput() { |
| return input; |
| } |
| |
| @Override |
| public AllocatableValue getResult() { |
| return result; |
| } |
| } |
| |
| } |