001/*
002 * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.lir.alloc.lsra;
024
025import static jdk.internal.jvmci.code.ValueUtil.*;
026
027import java.util.*;
028
029import jdk.internal.jvmci.code.*;
030import jdk.internal.jvmci.common.*;
031import com.oracle.graal.debug.*;
032import com.oracle.graal.debug.Debug.Scope;
033import jdk.internal.jvmci.meta.*;
034
035import com.oracle.graal.compiler.common.cfg.*;
036import com.oracle.graal.compiler.common.util.*;
037import com.oracle.graal.lir.*;
038import com.oracle.graal.lir.LIRInstruction.OperandFlag;
039import com.oracle.graal.lir.LIRInstruction.OperandMode;
040
041/**
042 */
043final class RegisterVerifier {
044
045    LinearScan allocator;
046    List<AbstractBlockBase<?>> workList; // all blocks that must be processed
047    ArrayMap<Interval[]> savedStates; // saved information of previous check
048
049    // simplified access to methods of LinearScan
050    Interval intervalAt(Value operand) {
051        return allocator.intervalFor(operand);
052    }
053
054    // currently, only registers are processed
055    int stateSize() {
056        return allocator.maxRegisterNumber() + 1;
057    }
058
059    // accessors
060    Interval[] stateForBlock(AbstractBlockBase<?> block) {
061        return savedStates.get(block.getId());
062    }
063
064    void setStateForBlock(AbstractBlockBase<?> block, Interval[] savedState) {
065        savedStates.put(block.getId(), savedState);
066    }
067
068    void addToWorkList(AbstractBlockBase<?> block) {
069        if (!workList.contains(block)) {
070            workList.add(block);
071        }
072    }
073
074    RegisterVerifier(LinearScan allocator) {
075        this.allocator = allocator;
076        workList = new ArrayList<>(16);
077        this.savedStates = new ArrayMap<>();
078
079    }
080
081    void verify(AbstractBlockBase<?> start) {
082        try (Scope s = Debug.scope("RegisterVerifier")) {
083            // setup input registers (method arguments) for first block
084            Interval[] inputState = new Interval[stateSize()];
085            setStateForBlock(start, inputState);
086            addToWorkList(start);
087
088            // main loop for verification
089            do {
090                AbstractBlockBase<?> block = workList.get(0);
091                workList.remove(0);
092
093                processBlock(block);
094            } while (!workList.isEmpty());
095        }
096    }
097
098    private void processBlock(AbstractBlockBase<?> block) {
099        try (Indent indent = Debug.logAndIndent("processBlock B%d", block.getId())) {
100            // must copy state because it is modified
101            Interval[] inputState = copy(stateForBlock(block));
102
103            try (Indent indent2 = Debug.logAndIndent("Input-State of intervals:")) {
104                printState(inputState);
105            }
106
107            // process all operations of the block
108            processOperations(block, inputState);
109
110            try (Indent indent2 = Debug.logAndIndent("Output-State of intervals:")) {
111                printState(inputState);
112            }
113
114            // iterate all successors
115            for (AbstractBlockBase<?> succ : block.getSuccessors()) {
116                processSuccessor(succ, inputState);
117            }
118        }
119    }
120
121    protected void printState(Interval[] inputState) {
122        for (int i = 0; i < stateSize(); i++) {
123            Register reg = allocator.getRegisters()[i];
124            assert reg.number == i;
125            if (inputState[i] != null) {
126                Debug.log(" %6s %4d  --  %s", reg, inputState[i].operandNumber, inputState[i]);
127            } else {
128                Debug.log(" %6s   __", reg);
129            }
130        }
131    }
132
133    private void processSuccessor(AbstractBlockBase<?> block, Interval[] inputState) {
134        Interval[] savedState = stateForBlock(block);
135
136        if (savedState != null) {
137            // this block was already processed before.
138            // check if new inputState is consistent with savedState
139
140            boolean savedStateCorrect = true;
141            for (int i = 0; i < stateSize(); i++) {
142                if (inputState[i] != savedState[i]) {
143                    // current inputState and previous savedState assume a different
144                    // interval in this register . assume that this register is invalid
145                    if (savedState[i] != null) {
146                        // invalidate old calculation only if it assumed that
147                        // register was valid. when the register was already invalid,
148                        // then the old calculation was correct.
149                        savedStateCorrect = false;
150                        savedState[i] = null;
151
152                        Debug.log("processSuccessor B%d: invalidating slot %d", block.getId(), i);
153                    }
154                }
155            }
156
157            if (savedStateCorrect) {
158                // already processed block with correct inputState
159                Debug.log("processSuccessor B%d: previous visit already correct", block.getId());
160            } else {
161                // must re-visit this block
162                Debug.log("processSuccessor B%d: must re-visit because input state changed", block.getId());
163                addToWorkList(block);
164            }
165
166        } else {
167            // block was not processed before, so set initial inputState
168            Debug.log("processSuccessor B%d: initial visit", block.getId());
169
170            setStateForBlock(block, copy(inputState));
171            addToWorkList(block);
172        }
173    }
174
175    static Interval[] copy(Interval[] inputState) {
176        return inputState.clone();
177    }
178
179    static void statePut(Interval[] inputState, Value location, Interval interval) {
180        if (location != null && isRegister(location)) {
181            Register reg = asRegister(location);
182            int regNum = reg.number;
183            if (interval != null) {
184                Debug.log("%s = %s", reg, interval.operand);
185            } else if (inputState[regNum] != null) {
186                Debug.log("%s = null", reg);
187            }
188
189            inputState[regNum] = interval;
190        }
191    }
192
193    static boolean checkState(AbstractBlockBase<?> block, LIRInstruction op, Interval[] inputState, Value operand, Value reg, Interval interval) {
194        if (reg != null && isRegister(reg)) {
195            if (inputState[asRegister(reg).number] != interval) {
196                throw new JVMCIError(
197                                "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",
198                                op, block, reg, operand, interval, inputState[asRegister(reg).number]);
199            }
200        }
201        return true;
202    }
203
204    void processOperations(AbstractBlockBase<?> block, final Interval[] inputState) {
205        List<LIRInstruction> ops = allocator.getLIR().getLIRforBlock(block);
206        InstructionValueConsumer useConsumer = new InstructionValueConsumer() {
207
208            @Override
209            public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
210                // we skip spill moves inserted by the spill position optimization
211                if (LinearScan.isVariableOrRegister(operand) && allocator.isProcessed(operand) && op.id() != LinearScan.DOMINATOR_SPILL_MOVE_ID) {
212                    Interval interval = intervalAt(operand);
213                    if (op.id() != -1) {
214                        interval = interval.getSplitChildAtOpId(op.id(), mode, allocator);
215                    }
216
217                    assert checkState(block, op, inputState, interval.operand, interval.location(), interval.splitParent());
218                }
219            }
220        };
221
222        InstructionValueConsumer defConsumer = (op, operand, mode, flags) -> {
223            if (LinearScan.isVariableOrRegister(operand) && allocator.isProcessed(operand)) {
224                Interval interval = intervalAt(operand);
225                if (op.id() != -1) {
226                    interval = interval.getSplitChildAtOpId(op.id(), mode, allocator);
227                }
228
229                statePut(inputState, interval.location(), interval.splitParent());
230            }
231        };
232
233        // visit all instructions of the block
234        for (int i = 0; i < ops.size(); i++) {
235            final LIRInstruction op = ops.get(i);
236
237            if (Debug.isLogEnabled()) {
238                Debug.log("%s", op.toStringWithIdPrefix());
239            }
240
241            // check if input operands are correct
242            op.visitEachInput(useConsumer);
243            // invalidate all caller save registers at calls
244            if (op.destroysCallerSavedRegisters()) {
245                for (Register r : allocator.getRegisterAllocationConfig().getRegisterConfig().getCallerSaveRegisters()) {
246                    statePut(inputState, r.asValue(), null);
247                }
248            }
249            op.visitEachAlive(useConsumer);
250            // set temp operands (some operations use temp operands also as output operands, so
251            // can't set them null)
252            op.visitEachTemp(defConsumer);
253            // set output operands
254            op.visitEachOutput(defConsumer);
255        }
256    }
257}