/* * Copyright (c) 2009, 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.LIRValueUtil.asVariable; import static org.graalvm.compiler.lir.LIRValueUtil.isJavaConstant; import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue; import static org.graalvm.compiler.lir.LIRValueUtil.isVariable; import static jdk.vm.ci.code.ValueUtil.asRegister; import static jdk.vm.ci.code.ValueUtil.isIllegal; import static jdk.vm.ci.code.ValueUtil.isRegister; 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.GraalError; import org.graalvm.compiler.debug.TTY; import org.graalvm.compiler.lir.LIRInstruction.OperandFlag; import org.graalvm.compiler.lir.LIRInstruction.OperandMode; import org.graalvm.compiler.lir.framemap.FrameMap; import org.graalvm.compiler.lir.ssa.SSAUtil; import jdk.vm.ci.code.Register; import jdk.vm.ci.meta.Value; public final class LIRVerifier { private final LIR lir; private final FrameMap frameMap; private final boolean beforeRegisterAllocation; private final BitSet[] blockLiveOut; private final Object[] variableDefinitions; private BitSet liveOutFor(AbstractBlockBase<?> block) { return blockLiveOut[block.getId()]; } private void setLiveOutFor(AbstractBlockBase<?> block, BitSet liveOut) { blockLiveOut[block.getId()] = liveOut; } private int maxRegisterNum() { return frameMap.getTarget().arch.getRegisters().size(); } private boolean isAllocatableRegister(Value value) { return isRegister(value) && frameMap.getRegisterConfig().getAttributesMap()[asRegister(value).number].isAllocatable(); } public static boolean verify(final LIRInstruction op) { op.visitEachInput(LIRVerifier::allowed); op.visitEachAlive(LIRVerifier::allowed); op.visitEachState(LIRVerifier::allowed); op.visitEachTemp(LIRVerifier::allowed); op.visitEachOutput(LIRVerifier::allowed); op.verify(); return true; } public static boolean verify(boolean beforeRegisterAllocation, LIR lir, FrameMap frameMap) { LIRVerifier verifier = new LIRVerifier(beforeRegisterAllocation, lir, frameMap); verifier.verify(); return true; } private LIRVerifier(boolean beforeRegisterAllocation, LIR lir, FrameMap frameMap) { this.beforeRegisterAllocation = beforeRegisterAllocation; this.lir = lir; this.frameMap = frameMap; this.blockLiveOut = new BitSet[lir.linearScanOrder().length]; this.variableDefinitions = new Object[lir.numVariables()]; } private BitSet curVariablesLive; private Value[] curRegistersLive; private AbstractBlockBase<?> curBlock; private Object curInstruction; private BitSet curRegistersDefined; private void verify() { ValueConsumer useConsumer = new ValueConsumer() { @Override public void visitValue(Value value, OperandMode mode, EnumSet<OperandFlag> flags) { use(value, mode, flags); } }; ValueConsumer defConsumer = new ValueConsumer() { @Override public void visitValue(Value value, OperandMode mode, EnumSet<OperandFlag> flags) { def(value, mode, flags); } }; int maxRegisterNum = maxRegisterNum(); curRegistersDefined = new BitSet(); for (AbstractBlockBase<?> block : lir.linearScanOrder()) { curBlock = block; curVariablesLive = new BitSet(); curRegistersLive = new Value[maxRegisterNum]; if (block.getDominator() != null) { curVariablesLive.or(liveOutFor(block.getDominator())); } assert lir.getLIRforBlock(block).get(0) instanceof StandardOp.LabelOp : "block must start with label"; if (block.getSuccessorCount() > 0) { LIRInstruction last = lir.getLIRforBlock(block).get(lir.getLIRforBlock(block).size() - 1); assert last instanceof StandardOp.JumpOp : "block with successor must end with unconditional jump"; } if (block.getPredecessorCount() > 1) { SSAUtil.verifyPhi(lir, block); } for (LIRInstruction op : lir.getLIRforBlock(block)) { curInstruction = op; op.visitEachInput(useConsumer); if (op.destroysCallerSavedRegisters()) { for (Register register : frameMap.getRegisterConfig().getCallerSaveRegisters()) { curRegistersLive[register.number] = null; } } curRegistersDefined.clear(); op.visitEachAlive(useConsumer); op.visitEachState(useConsumer); op.visitEachTemp(defConsumer); op.visitEachOutput(defConsumer); curInstruction = null; } setLiveOutFor(block, curVariablesLive); } } private void use(Value value, OperandMode mode, EnumSet<OperandFlag> flags) { allowed(curInstruction, value, mode, flags); if (isVariable(value)) { assert beforeRegisterAllocation; int variableIdx = asVariable(value).index; if (!curVariablesLive.get(variableIdx)) { TTY.println("block %s instruction %s", curBlock, curInstruction); TTY.println("live variables: %s", curVariablesLive); if (variableDefinitions[variableIdx] != null) { TTY.println("definition of %s: %s", value, variableDefinitions[variableIdx]); } TTY.println("ERROR: Use of variable %s that is not defined in dominator", value); throw GraalError.shouldNotReachHere(); } } else if (isAllocatableRegister(value)) { int regNum = asRegister(value).number; if (mode == OperandMode.ALIVE) { curRegistersDefined.set(regNum); } if (beforeRegisterAllocation && !curRegistersLive[regNum].equals(value)) { TTY.println("block %s instruction %s", curBlock, curInstruction); TTY.println("live registers: %s", Arrays.toString(curRegistersLive)); TTY.println("ERROR: Use of fixed register %s that is not defined in this block", value); throw GraalError.shouldNotReachHere(); } } } private void def(Value value, OperandMode mode, EnumSet<OperandFlag> flags) { allowed(curInstruction, value, mode, flags); if (isVariable(value)) { assert beforeRegisterAllocation; int variableIdx = asVariable(value).index; if (variableDefinitions[variableIdx] != null) { TTY.println("block %s instruction %s", curBlock, curInstruction); TTY.println("live variables: %s", curVariablesLive); TTY.println("definition of %s: %s", value, variableDefinitions[variableIdx]); TTY.println("ERROR: Variable %s defined multiple times", value); throw GraalError.shouldNotReachHere(); } assert curInstruction != null; variableDefinitions[variableIdx] = curInstruction; assert !curVariablesLive.get(variableIdx); if (mode == OperandMode.DEF) { curVariablesLive.set(variableIdx); } } else if (isAllocatableRegister(value)) { int regNum = asRegister(value).number; if (curRegistersDefined.get(regNum)) { TTY.println("block %s instruction %s", curBlock, curInstruction); TTY.println("ERROR: Same register defined twice in the same instruction: %s", value); throw GraalError.shouldNotReachHere(); } curRegistersDefined.set(regNum); if (beforeRegisterAllocation) { if (mode == OperandMode.DEF) { curRegistersLive[regNum] = value; } else { curRegistersLive[regNum] = null; } } } } // @formatter:off private static void allowed(Object op, Value value, OperandMode mode, EnumSet<OperandFlag> flags) { if ((isVariable(value) && flags.contains(OperandFlag.REG)) || (isRegister(value) && flags.contains(OperandFlag.REG)) || (isStackSlotValue(value) && flags.contains(OperandFlag.STACK)) || (isJavaConstant(value) && flags.contains(OperandFlag.CONST) && mode != OperandMode.DEF) || (isIllegal(value) && flags.contains(OperandFlag.ILLEGAL))) { return; } throw new GraalError("Invalid LIR%n Instruction: %s%n Mode: %s%n Flags: %s%n Unexpected value: %s %s", op, mode, flags, value.getClass().getSimpleName(), value); } // @formatter:on }