1 /*
2  * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 
25 package org.graalvm.compiler.lir.alloc.lsra;
26 
27 import static jdk.vm.ci.code.ValueUtil.asRegister;
28 import static jdk.vm.ci.code.ValueUtil.isRegister;
29 
30 import java.util.ArrayList;
31 import java.util.EnumSet;
32 
33 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
34 import org.graalvm.compiler.core.common.cfg.BlockMap;
35 import org.graalvm.compiler.debug.DebugContext;
36 import org.graalvm.compiler.debug.GraalError;
37 import org.graalvm.compiler.debug.Indent;
38 import org.graalvm.compiler.lir.InstructionValueConsumer;
39 import org.graalvm.compiler.lir.LIRInstruction;
40 import org.graalvm.compiler.lir.LIRInstruction.OperandFlag;
41 import org.graalvm.compiler.lir.LIRInstruction.OperandMode;
42 
43 import jdk.vm.ci.code.Register;
44 import jdk.vm.ci.meta.Value;
45 
46 /**
47  */
48 final class RegisterVerifier {
49 
50     LinearScan allocator;
51     ArrayList<AbstractBlockBase<?>> workList; // all blocks that must be processed
52     BlockMap<Interval[]> savedStates; // saved information of previous check
53 
54     // simplified access to methods of LinearScan
intervalAt(Value operand)55     Interval intervalAt(Value operand) {
56         return allocator.intervalFor(operand);
57     }
58 
59     // currently, only registers are processed
stateSize()60     int stateSize() {
61         return allocator.maxRegisterNumber() + 1;
62     }
63 
64     // accessors
stateForBlock(AbstractBlockBase<?> block)65     Interval[] stateForBlock(AbstractBlockBase<?> block) {
66         return savedStates.get(block);
67     }
68 
setStateForBlock(AbstractBlockBase<?> block, Interval[] savedState)69     void setStateForBlock(AbstractBlockBase<?> block, Interval[] savedState) {
70         savedStates.put(block, savedState);
71     }
72 
addToWorkList(AbstractBlockBase<?> block)73     void addToWorkList(AbstractBlockBase<?> block) {
74         if (!workList.contains(block)) {
75             workList.add(block);
76         }
77     }
78 
RegisterVerifier(LinearScan allocator)79     RegisterVerifier(LinearScan allocator) {
80         this.allocator = allocator;
81         workList = new ArrayList<>(16);
82         this.savedStates = new BlockMap<>(allocator.getLIR().getControlFlowGraph());
83 
84     }
85 
86     @SuppressWarnings("try")
verify(AbstractBlockBase<?> start)87     void verify(AbstractBlockBase<?> start) {
88         DebugContext debug = allocator.getDebug();
89         try (DebugContext.Scope s = debug.scope("RegisterVerifier")) {
90             // setup input registers (method arguments) for first block
91             Interval[] inputState = new Interval[stateSize()];
92             setStateForBlock(start, inputState);
93             addToWorkList(start);
94 
95             // main loop for verification
96             do {
97                 AbstractBlockBase<?> block = workList.get(0);
98                 workList.remove(0);
99 
100                 processBlock(block);
101             } while (!workList.isEmpty());
102         }
103     }
104 
105     @SuppressWarnings("try")
processBlock(AbstractBlockBase<?> block)106     private void processBlock(AbstractBlockBase<?> block) {
107         DebugContext debug = allocator.getDebug();
108         try (Indent indent = debug.logAndIndent("processBlock B%d", block.getId())) {
109             // must copy state because it is modified
110             Interval[] inputState = copy(stateForBlock(block));
111 
112             try (Indent indent2 = debug.logAndIndent("Input-State of intervals:")) {
113                 printState(inputState);
114             }
115 
116             // process all operations of the block
117             processOperations(block, inputState);
118 
119             try (Indent indent2 = debug.logAndIndent("Output-State of intervals:")) {
120                 printState(inputState);
121             }
122 
123             // iterate all successors
124             for (AbstractBlockBase<?> succ : block.getSuccessors()) {
125                 processSuccessor(succ, inputState);
126             }
127         }
128     }
129 
printState(Interval[] inputState)130     protected void printState(Interval[] inputState) {
131         DebugContext debug = allocator.getDebug();
132         for (int i = 0; i < stateSize(); i++) {
133             Register reg = allocator.getRegisters().get(i);
134             assert reg.number == i;
135             if (inputState[i] != null) {
136                 debug.log(" %6s %4d  --  %s", reg, inputState[i].operandNumber, inputState[i]);
137             } else {
138                 debug.log(" %6s   __", reg);
139             }
140         }
141     }
142 
processSuccessor(AbstractBlockBase<?> block, Interval[] inputState)143     private void processSuccessor(AbstractBlockBase<?> block, Interval[] inputState) {
144         DebugContext debug = allocator.getDebug();
145         Interval[] savedState = stateForBlock(block);
146 
147         if (savedState != null) {
148             // this block was already processed before.
149             // check if new inputState is consistent with savedState
150 
151             boolean savedStateCorrect = true;
152             for (int i = 0; i < stateSize(); i++) {
153                 if (inputState[i] != savedState[i]) {
154                     // current inputState and previous savedState assume a different
155                     // interval in this register . assume that this register is invalid
156                     if (savedState[i] != null) {
157                         // invalidate old calculation only if it assumed that
158                         // register was valid. when the register was already invalid,
159                         // then the old calculation was correct.
160                         savedStateCorrect = false;
161                         savedState[i] = null;
162 
163                         debug.log("processSuccessor B%d: invalidating slot %d", block.getId(), i);
164                     }
165                 }
166             }
167 
168             if (savedStateCorrect) {
169                 // already processed block with correct inputState
170                 debug.log("processSuccessor B%d: previous visit already correct", block.getId());
171             } else {
172                 // must re-visit this block
173                 debug.log("processSuccessor B%d: must re-visit because input state changed", block.getId());
174                 addToWorkList(block);
175             }
176 
177         } else {
178             // block was not processed before, so set initial inputState
179             debug.log("processSuccessor B%d: initial visit", block.getId());
180 
181             setStateForBlock(block, copy(inputState));
182             addToWorkList(block);
183         }
184     }
185 
copy(Interval[] inputState)186     static Interval[] copy(Interval[] inputState) {
187         return inputState.clone();
188     }
189 
statePut(DebugContext debug, Interval[] inputState, Value location, Interval interval)190     static void statePut(DebugContext debug, Interval[] inputState, Value location, Interval interval) {
191         if (location != null && isRegister(location)) {
192             Register reg = asRegister(location);
193             int regNum = reg.number;
194             if (interval != null) {
195                 debug.log("%s = %s", reg, interval.operand);
196             } else if (inputState[regNum] != null) {
197                 debug.log("%s = null", reg);
198             }
199 
200             inputState[regNum] = interval;
201         }
202     }
203 
checkState(AbstractBlockBase<?> block, LIRInstruction op, Interval[] inputState, Value operand, Value reg, Interval interval)204     static boolean checkState(AbstractBlockBase<?> block, LIRInstruction op, Interval[] inputState, Value operand, Value reg, Interval interval) {
205         if (reg != null && isRegister(reg)) {
206             if (inputState[asRegister(reg).number] != interval) {
207                 throw new GraalError(
208                                 "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",
209                                 op, block, reg, operand, interval, inputState[asRegister(reg).number]);
210             }
211         }
212         return true;
213     }
214 
processOperations(AbstractBlockBase<?> block, final Interval[] inputState)215     void processOperations(AbstractBlockBase<?> block, final Interval[] inputState) {
216         ArrayList<LIRInstruction> ops = allocator.getLIR().getLIRforBlock(block);
217         DebugContext debug = allocator.getDebug();
218         InstructionValueConsumer useConsumer = new InstructionValueConsumer() {
219 
220             @Override
221             public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
222                 // we skip spill moves inserted by the spill position optimization
223                 if (LinearScan.isVariableOrRegister(operand) && allocator.isProcessed(operand) && op.id() != LinearScan.DOMINATOR_SPILL_MOVE_ID) {
224                     Interval interval = intervalAt(operand);
225                     if (op.id() != -1) {
226                         interval = interval.getSplitChildAtOpId(op.id(), mode, allocator);
227                     }
228 
229                     assert checkState(block, op, inputState, interval.operand, interval.location(), interval.splitParent());
230                 }
231             }
232         };
233 
234         InstructionValueConsumer defConsumer = (op, operand, mode, flags) -> {
235             if (LinearScan.isVariableOrRegister(operand) && allocator.isProcessed(operand)) {
236                 Interval interval = intervalAt(operand);
237                 if (op.id() != -1) {
238                     interval = interval.getSplitChildAtOpId(op.id(), mode, allocator);
239                 }
240 
241                 statePut(debug, inputState, interval.location(), interval.splitParent());
242             }
243         };
244 
245         // visit all instructions of the block
246         for (int i = 0; i < ops.size(); i++) {
247             final LIRInstruction op = ops.get(i);
248 
249             if (debug.isLogEnabled()) {
250                 debug.log("%s", op.toStringWithIdPrefix());
251             }
252 
253             // check if input operands are correct
254             op.visitEachInput(useConsumer);
255             // invalidate all caller save registers at calls
256             if (op.destroysCallerSavedRegisters()) {
257                 for (Register r : allocator.getRegisterAllocationConfig().getRegisterConfig().getCallerSaveRegisters()) {
258                     statePut(debug, inputState, r.asValue(), null);
259                 }
260             }
261             op.visitEachAlive(useConsumer);
262             // set temp operands (some operations use temp operands also as output operands, so
263             // can't set them null)
264             op.visitEachTemp(defConsumer);
265             // set output operands
266             op.visitEachOutput(defConsumer);
267         }
268     }
269 }
270