| /* |
| * Copyright (c) 2017, 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.alloc.trace; |
| |
| import java.util.Arrays; |
| import java.util.BitSet; |
| import java.util.EnumSet; |
| |
| import org.graalvm.compiler.core.common.cfg.AbstractBlockBase; |
| import org.graalvm.compiler.debug.DebugContext; |
| import org.graalvm.compiler.lir.LIR; |
| import org.graalvm.compiler.lir.LIRInstruction; |
| import org.graalvm.compiler.lir.LIRInstruction.OperandFlag; |
| import org.graalvm.compiler.lir.LIRInstruction.OperandMode; |
| import org.graalvm.compiler.lir.LIRValueUtil; |
| import org.graalvm.compiler.lir.ValueConsumer; |
| import org.graalvm.compiler.lir.Variable; |
| |
| import jdk.vm.ci.meta.Value; |
| |
| /** |
| * Stores live in/live out variables and locations for each basic block. |
| * |
| * <em>Live variable information</em> is stored as an integer array containing <em>variable |
| * indices</em>. The information is only stored once per <em>control-flow split</em> or |
| * <em>control-merge</em>. In other words the live sets at the end of the source block and the |
| * beginning of the target block of an edge are the same. |
| */ |
| public final class GlobalLivenessInfo { |
| |
| public static final class Builder { |
| private GlobalLivenessInfo info; |
| public final int[] emptySet; |
| |
| public Builder(LIR lir) { |
| this(lir.numVariables(), lir.getControlFlowGraph().getBlocks().length); |
| } |
| |
| public Builder(int numVariables, int numBlocks) { |
| info = new GlobalLivenessInfo(numVariables, numBlocks); |
| emptySet = new int[0]; |
| } |
| |
| public GlobalLivenessInfo createLivenessInfo() { |
| GlobalLivenessInfo livenessInfo = info; |
| info = null; |
| return livenessInfo; |
| } |
| |
| public void setIncoming(AbstractBlockBase<?> block, int[] varsIn) { |
| assert info.blockToVarIn[block.getId()] == null; |
| assert verifyVars(varsIn); |
| assert storesIncoming(block) || info.blockToVarOut[block.getPredecessors()[0].getId()] == varsIn; |
| info.blockToVarIn[block.getId()] = varsIn; |
| } |
| |
| public void setOutgoing(AbstractBlockBase<?> block, int[] varsOut) { |
| assert info.blockToVarOut[block.getId()] == null; |
| assert verifyVars(varsOut); |
| assert storesOutgoing(block) || info.blockToVarIn[block.getSuccessors()[0].getId()] == varsOut; |
| info.blockToVarOut[block.getId()] = varsOut; |
| } |
| |
| private static boolean verifyVars(int[] vars) { |
| for (int var : vars) { |
| assert var >= 0; |
| } |
| return true; |
| } |
| |
| @SuppressWarnings("unused") |
| public void addVariable(LIRInstruction op, Variable var) { |
| info.variables[var.index] = var; |
| } |
| |
| } |
| |
| private final Variable[] variables; |
| private final int[][] blockToVarIn; |
| private final int[][] blockToVarOut; |
| private final Value[][] blockToLocIn; |
| private final Value[][] blockToLocOut; |
| |
| private GlobalLivenessInfo(int numVariables, int numBlocks) { |
| variables = new Variable[numVariables]; |
| blockToVarIn = new int[numBlocks][]; |
| blockToVarOut = new int[numBlocks][]; |
| blockToLocIn = new Value[numBlocks][]; |
| blockToLocOut = new Value[numBlocks][]; |
| } |
| |
| public Variable getVariable(int varNum) { |
| return variables[varNum]; |
| } |
| |
| public int[] getBlockOut(AbstractBlockBase<?> block) { |
| return blockToVarOut[block.getId()]; |
| } |
| |
| public int[] getBlockIn(AbstractBlockBase<?> block) { |
| return blockToVarIn[block.getId()]; |
| } |
| |
| public void setInLocations(AbstractBlockBase<?> block, Value[] values) { |
| blockToLocIn[block.getId()] = values; |
| } |
| |
| public void setOutLocations(AbstractBlockBase<?> block, Value[] values) { |
| blockToLocOut[block.getId()] = values; |
| } |
| |
| public Value[] getInLocation(AbstractBlockBase<?> block) { |
| return blockToLocIn[block.getId()]; |
| } |
| |
| public Value[] getOutLocation(AbstractBlockBase<?> block) { |
| return blockToLocOut[block.getId()]; |
| } |
| |
| public static boolean storesIncoming(AbstractBlockBase<?> block) { |
| assert block.getPredecessorCount() >= 0; |
| return block.getPredecessorCount() != 1; |
| } |
| |
| public static boolean storesOutgoing(AbstractBlockBase<?> block) { |
| assert block.getSuccessorCount() >= 0; |
| /* |
| * The second condition handles non-critical empty blocks, introduced, e.g., by two |
| * consecutive loop-exits. |
| */ |
| return block.getSuccessorCount() != 1 || block.getSuccessors()[0].getPredecessorCount() == 1; |
| } |
| |
| /** |
| * Verifies that the local liveness information is correct, i.e., that all variables used in a |
| * block {@code b} are either defined in {@code b} or in the incoming live set. |
| */ |
| @SuppressWarnings("try") |
| public boolean verify(LIR lir) { |
| DebugContext debug = lir.getDebug(); |
| try (DebugContext.Scope s = debug.scope("Verify GlobalLivenessInfo", this)) { |
| for (AbstractBlockBase<?> block : lir.getControlFlowGraph().getBlocks()) { |
| assert verifyBlock(block, lir); |
| } |
| } catch (Throwable e) { |
| throw debug.handle(e); |
| } |
| return true; |
| } |
| |
| private boolean verifyBlock(AbstractBlockBase<?> block, LIR lir) { |
| BitSet liveSet = new BitSet(lir.numVariables()); |
| int[] liveIn = getBlockIn(block); |
| for (int varNum : liveIn) { |
| liveSet.set(varNum); |
| } |
| ValueConsumer proc = new ValueConsumer() { |
| |
| @Override |
| public void visitValue(Value value, OperandMode mode, EnumSet<OperandFlag> flags) { |
| if (LIRValueUtil.isVariable(value)) { |
| Variable var = LIRValueUtil.asVariable(value); |
| if (mode == OperandMode.DEF) { |
| liveSet.set(var.index); |
| } else { |
| assert liveSet.get(var.index) : String.format("Variable %s but not defined in block %s (liveIn: %s)", var, block, Arrays.toString(liveIn)); |
| } |
| } |
| } |
| |
| }; |
| for (LIRInstruction op : lir.getLIRforBlock(block)) { |
| op.visitEachInput(proc); |
| op.visitEachAlive(proc); |
| op.visitEachState(proc); |
| op.visitEachOutput(proc); |
| // no need for checking temp |
| } |
| return true; |
| } |
| } |