--- /dev/null 2017-01-22 10:16:57.869617664 -0800 +++ new/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/RegisterVerifier.java 2017-02-15 17:05:37.808010666 -0800 @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2009, 2012, 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.lsra; + +import static org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanPhase.isVariableOrRegister; +import static jdk.vm.ci.code.ValueUtil.asRegister; +import static jdk.vm.ci.code.ValueUtil.isRegister; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; + +import org.graalvm.compiler.core.common.cfg.AbstractBlockBase; +import org.graalvm.compiler.core.common.util.ArrayMap; +import org.graalvm.compiler.debug.Debug; +import org.graalvm.compiler.debug.Debug.Scope; +import org.graalvm.compiler.debug.GraalError; +import org.graalvm.compiler.debug.Indent; +import org.graalvm.compiler.lir.InstructionValueConsumer; +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.alloc.trace.lsra.TraceLinearScanPhase.TraceLinearScan; + +import jdk.vm.ci.code.Register; +import jdk.vm.ci.meta.Value; + +/** + */ +final class RegisterVerifier { + + TraceLinearScan allocator; + List> workList; // all blocks that must be processed + ArrayMap savedStates; // saved information of previous check + + // simplified access to methods of LinearScan + TraceInterval intervalAt(Value operand) { + return allocator.intervalFor(operand); + } + + // currently, only registers are processed + int stateSize() { + return allocator.numRegisters(); + } + + // accessors + TraceInterval[] stateForBlock(AbstractBlockBase block) { + return savedStates.get(block.getId()); + } + + void setStateForBlock(AbstractBlockBase block, TraceInterval[] savedState) { + savedStates.put(block.getId(), savedState); + } + + void addToWorkList(AbstractBlockBase block) { + if (!workList.contains(block)) { + workList.add(block); + } + } + + RegisterVerifier(TraceLinearScan allocator) { + this.allocator = allocator; + workList = new ArrayList<>(16); + this.savedStates = new ArrayMap<>(); + + } + + @SuppressWarnings("try") + void verify(AbstractBlockBase start) { + try (Scope s = Debug.scope("RegisterVerifier")) { + // setup input registers (method arguments) for first block + TraceInterval[] inputState = new TraceInterval[stateSize()]; + setStateForBlock(start, inputState); + addToWorkList(start); + + // main loop for verification + do { + AbstractBlockBase block = workList.get(0); + workList.remove(0); + + processBlock(block); + } while (!workList.isEmpty()); + } + } + + @SuppressWarnings("try") + private void processBlock(AbstractBlockBase block) { + try (Indent indent = Debug.logAndIndent("processBlock B%d", block.getId())) { + // must copy state because it is modified + TraceInterval[] inputState = copy(stateForBlock(block)); + + try (Indent indent2 = Debug.logAndIndent("Input-State of intervals:")) { + printState(inputState); + } + + // process all operations of the block + processOperations(block, inputState); + + try (Indent indent2 = Debug.logAndIndent("Output-State of intervals:")) { + printState(inputState); + } + + // iterate all successors + for (AbstractBlockBase succ : block.getSuccessors()) { + processSuccessor(succ, inputState); + } + } + } + + protected void printState(TraceInterval[] inputState) { + for (int i = 0; i < stateSize(); i++) { + Register reg = allocator.getRegisters().get(i); + assert reg.number == i; + if (inputState[i] != null) { + Debug.log(" %6s %4d -- %s", reg, inputState[i].operandNumber, inputState[i]); + } else { + Debug.log(" %6s __", reg); + } + } + } + + private void processSuccessor(AbstractBlockBase block, TraceInterval[] inputState) { + TraceInterval[] savedState = stateForBlock(block); + + if (savedState != null) { + // this block was already processed before. + // check if new inputState is consistent with savedState + + boolean savedStateCorrect = true; + for (int i = 0; i < stateSize(); i++) { + if (inputState[i] != savedState[i]) { + // current inputState and previous savedState assume a different + // interval in this register . assume that this register is invalid + if (savedState[i] != null) { + // invalidate old calculation only if it assumed that + // register was valid. when the register was already invalid, + // then the old calculation was correct. + savedStateCorrect = false; + savedState[i] = null; + + Debug.log("processSuccessor B%d: invalidating slot %d", block.getId(), i); + } + } + } + + if (savedStateCorrect) { + // already processed block with correct inputState + Debug.log("processSuccessor B%d: previous visit already correct", block.getId()); + } else { + // must re-visit this block + Debug.log("processSuccessor B%d: must re-visit because input state changed", block.getId()); + addToWorkList(block); + } + + } else { + // block was not processed before, so set initial inputState + Debug.log("processSuccessor B%d: initial visit", block.getId()); + + setStateForBlock(block, copy(inputState)); + addToWorkList(block); + } + } + + static TraceInterval[] copy(TraceInterval[] inputState) { + return inputState.clone(); + } + + static void statePut(TraceInterval[] inputState, Value location, TraceInterval interval) { + if (location != null && isRegister(location)) { + Register reg = asRegister(location); + int regNum = reg.number; + if (interval != null) { + Debug.log("%s = %s", reg, interval.operand); + } else if (inputState[regNum] != null) { + Debug.log("%s = null", reg); + } + + inputState[regNum] = interval; + } + } + + static boolean checkState(AbstractBlockBase block, LIRInstruction op, TraceInterval[] inputState, Value operand, Value reg, TraceInterval interval) { + if (reg != null && isRegister(reg)) { + if (inputState[asRegister(reg).number] != interval) { + throw new GraalError( + "Error in register allocation: operation (%s) in block %s expected register %s (operand %s) to contain the value of interval %s but data-flow says it contains interval %s", + op, block, reg, operand, interval, inputState[asRegister(reg).number]); + } + } + return true; + } + + void processOperations(AbstractBlockBase block, final TraceInterval[] inputState) { + List ops = allocator.getLIR().getLIRforBlock(block); + InstructionValueConsumer useConsumer = new InstructionValueConsumer() { + + @Override + public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet flags) { + // we skip spill moves inserted by the spill position optimization + if (isVariableOrRegister(operand) && allocator.isProcessed(operand) && op.id() != TraceLinearScanPhase.DOMINATOR_SPILL_MOVE_ID) { + TraceInterval interval = intervalAt(operand); + if (op.id() != -1) { + interval = interval.getSplitChildAtOpId(op.id(), mode); + } + + assert checkState(block, op, inputState, interval.operand, interval.location(), interval.splitParent()); + } + } + }; + + InstructionValueConsumer defConsumer = (op, operand, mode, flags) -> { + if (isVariableOrRegister(operand) && allocator.isProcessed(operand)) { + TraceInterval interval = intervalAt(operand); + if (op.id() != -1) { + interval = interval.getSplitChildAtOpId(op.id(), mode); + } + + statePut(inputState, interval.location(), interval.splitParent()); + } + }; + + // visit all instructions of the block + for (int i = 0; i < ops.size(); i++) { + final LIRInstruction op = ops.get(i); + + if (Debug.isLogEnabled()) { + Debug.log("%s", op.toStringWithIdPrefix()); + } + + // check if input operands are correct + op.visitEachInput(useConsumer); + // invalidate all caller save registers at calls + if (op.destroysCallerSavedRegisters()) { + for (Register r : allocator.getRegisterAllocationConfig().getRegisterConfig().getCallerSaveRegisters()) { + statePut(inputState, r.asValue(), null); + } + } + op.visitEachAlive(useConsumer); + // set temp operands (some operations use temp operands also as output operands, so + // can't set them null) + op.visitEachTemp(defConsumer); + // set output operands + op.visitEachOutput(defConsumer); + } + } +}