001/*
002 * Copyright (c) 2009, 2014, 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.trace;
024
025import static jdk.internal.jvmci.code.ValueUtil.*;
026
027import java.util.*;
028
029import jdk.internal.jvmci.code.*;
030import jdk.internal.jvmci.common.*;
031import jdk.internal.jvmci.meta.*;
032
033import com.oracle.graal.debug.*;
034import com.oracle.graal.lir.*;
035import com.oracle.graal.lir.alloc.lsra.*;
036import com.oracle.graal.lir.framemap.*;
037import com.oracle.graal.lir.gen.*;
038import com.oracle.graal.lir.gen.LIRGeneratorTool.SpillMoveFactory;
039
040/**
041 */
042class TraceGlobalMoveResolver {
043
044    private int insertIdx;
045    private LIRInsertionBuffer insertionBuffer; // buffer where moves are inserted
046
047    private final List<Value> mappingFrom;
048    private final List<AllocatableValue> mappingTo;
049    private final int[] registerBlocked;
050    private static final int STACK_SLOT_IN_CALLER_FRAME_IDX = -1;
051    private int[] stackBlocked;
052    private final int firstVirtualStackIndex;
053    private final SpillMoveFactory spillMoveFactory;
054    private final FrameMapBuilder frameMapBuilder;
055
056    private void setValueBlocked(Value location, int direction) {
057        assert direction == 1 || direction == -1 : "out of bounds";
058        if (isStackSlotValue(location)) {
059            int stackIdx = getStackArrayIndex(asStackSlotValue(location));
060            if (stackIdx == STACK_SLOT_IN_CALLER_FRAME_IDX) {
061                // incoming stack arguments can be ignored
062                return;
063            }
064            if (stackIdx >= stackBlocked.length) {
065                stackBlocked = Arrays.copyOf(stackBlocked, stackIdx + 1);
066            }
067            stackBlocked[stackIdx] += direction;
068        } else {
069            assert direction == 1 || direction == -1 : "out of bounds";
070            if (isRegister(location)) {
071                registerBlocked[asRegister(location).number] += direction;
072            } else {
073                throw JVMCIError.shouldNotReachHere("unhandled value " + location);
074            }
075        }
076    }
077
078    private int valueBlocked(Value location) {
079        if (isStackSlotValue(location)) {
080            int stackIdx = getStackArrayIndex(asStackSlotValue(location));
081            if (stackIdx == STACK_SLOT_IN_CALLER_FRAME_IDX) {
082                // incoming stack arguments are always blocked (aka they can not be written)
083                return 1;
084            }
085            if (stackIdx >= stackBlocked.length) {
086                return 0;
087            }
088            return stackBlocked[stackIdx];
089        }
090        if (isRegister(location)) {
091            return registerBlocked[asRegister(location).number];
092        }
093        throw JVMCIError.shouldNotReachHere("unhandled value " + location);
094    }
095
096    private static boolean areMultipleReadsAllowed() {
097        return true;
098    }
099
100    private boolean hasMappings() {
101        return mappingFrom.size() > 0;
102    }
103
104    private SpillMoveFactory getSpillMoveFactory() {
105        return spillMoveFactory;
106    }
107
108    private Register[] getRegisters() {
109        return frameMapBuilder.getRegisterConfig().getAllocatableRegisters();
110    }
111
112    public TraceGlobalMoveResolver(LIRGenerationResult res, SpillMoveFactory spillMoveFactory, Architecture arch) {
113
114        this.mappingFrom = new ArrayList<>(8);
115        this.mappingTo = new ArrayList<>(8);
116        this.insertIdx = -1;
117        this.insertionBuffer = new LIRInsertionBuffer();
118
119        this.frameMapBuilder = res.getFrameMapBuilder();
120        this.spillMoveFactory = spillMoveFactory;
121        this.registerBlocked = new int[arch.getRegisters().length];
122
123        FrameMapBuilderTool frameMapBuilderTool = (FrameMapBuilderTool) frameMapBuilder;
124        this.stackBlocked = new int[frameMapBuilderTool.getNumberOfStackSlots()];
125
126        FrameMap frameMap = frameMapBuilderTool.getFrameMap();
127        this.firstVirtualStackIndex = !frameMap.frameNeedsAllocating() ? 0 : frameMap.currentFrameSize() + 1;
128    }
129
130    private boolean checkEmpty() {
131        for (int i = 0; i < stackBlocked.length; i++) {
132            assert stackBlocked[i] == 0 : "stack map must be empty before and after processing";
133        }
134        assert mappingFrom.size() == 0 && mappingTo.size() == 0 : "list must be empty before and after processing";
135        for (int i = 0; i < getRegisters().length; i++) {
136            assert registerBlocked[i] == 0 : "register map must be empty before and after processing";
137        }
138        return true;
139    }
140
141    private boolean verifyBeforeResolve() {
142        assert mappingFrom.size() == mappingTo.size() : "length must be equal";
143        assert insertIdx != -1 : "insert position not set";
144
145        int i;
146        int j;
147        if (!areMultipleReadsAllowed()) {
148            for (i = 0; i < mappingFrom.size(); i++) {
149                for (j = i + 1; j < mappingFrom.size(); j++) {
150                    assert mappingFrom.get(i) == null || mappingFrom.get(i) != mappingFrom.get(j) : "cannot read from same interval twice";
151                }
152            }
153        }
154
155        for (i = 0; i < mappingTo.size(); i++) {
156            for (j = i + 1; j < mappingTo.size(); j++) {
157                assert mappingTo.get(i) != mappingTo.get(j) : "cannot write to same interval twice";
158            }
159        }
160
161        for (i = 0; i < mappingTo.size(); i++) {
162            Value to = mappingTo.get(i);
163            assert !isStackSlotValue(to) || getStackArrayIndex(asStackSlotValue(to)) != STACK_SLOT_IN_CALLER_FRAME_IDX : "Cannot move to in argument: " + to;
164        }
165
166        HashSet<Value> usedRegs = new HashSet<>();
167        if (!areMultipleReadsAllowed()) {
168            for (i = 0; i < mappingFrom.size(); i++) {
169                Value from = mappingFrom.get(i);
170                if (from != null && !isIllegal(from)) {
171                    boolean unique = usedRegs.add(from);
172                    assert unique : "cannot read from same register twice";
173                }
174            }
175        }
176
177        usedRegs.clear();
178        for (i = 0; i < mappingTo.size(); i++) {
179            Value to = mappingTo.get(i);
180            if (isIllegal(to)) {
181                // After insertion the location may become illegal, so don't check it since multiple
182                // intervals might be illegal.
183                continue;
184            }
185            boolean unique = usedRegs.add(to);
186            assert unique : "cannot write to same register twice";
187        }
188
189        return true;
190    }
191
192    // mark assignedReg and assignedRegHi of the interval as blocked
193    private void block(Value location) {
194        if (mightBeBlocked(location)) {
195            assert areMultipleReadsAllowed() || valueBlocked(location) == 0 : "location already marked as used: " + location;
196            setValueBlocked(location, 1);
197            Debug.log("block %s", location);
198        }
199    }
200
201    // mark assignedReg and assignedRegHi of the interval as unblocked
202    private void unblock(Value location) {
203        if (mightBeBlocked(location)) {
204            assert valueBlocked(location) > 0 : "location already marked as unused: " + location;
205            setValueBlocked(location, -1);
206            Debug.log("unblock %s", location);
207        }
208    }
209
210    /**
211     * Checks if the {@linkplain Interval#location() location} of {@code to} is not blocked or is
212     * only blocked by {@code from}.
213     */
214    private boolean safeToProcessMove(Value fromLocation, Value toLocation) {
215        if (mightBeBlocked(toLocation)) {
216            if ((valueBlocked(toLocation) > 1 || (valueBlocked(toLocation) == 1 && !isMoveToSelf(fromLocation, toLocation)))) {
217                return false;
218            }
219        }
220
221        return true;
222    }
223
224    public static boolean isMoveToSelf(Value from, Value to) {
225        assert to != null;
226        if (to.equals(from)) {
227            return true;
228        }
229        if (from != null && isRegister(from) && isRegister(to) && asRegister(from).equals(asRegister(to))) {
230            assert LIRKind.verifyMoveKinds(to.getLIRKind(), from.getLIRKind()) : String.format("Same register but Kind mismatch %s <- %s", to, from);
231            return true;
232        }
233        return false;
234    }
235
236    private static boolean mightBeBlocked(Value location) {
237        return isRegister(location) || isStackSlotValue(location);
238    }
239
240    private void createInsertionBuffer(List<LIRInstruction> list) {
241        assert !insertionBuffer.initialized() : "overwriting existing buffer";
242        insertionBuffer.init(list);
243    }
244
245    private void appendInsertionBuffer() {
246        if (insertionBuffer.initialized()) {
247            insertionBuffer.finish();
248        }
249        assert !insertionBuffer.initialized() : "must be uninitialized now";
250
251        insertIdx = -1;
252    }
253
254    private void insertMove(Value fromOperand, AllocatableValue toOperand) {
255        assert !fromOperand.equals(toOperand) : "from and to are equal: " + fromOperand + " vs. " + toOperand;
256        assert LIRKind.verifyMoveKinds(fromOperand.getLIRKind(), fromOperand.getLIRKind()) : "move between different types";
257        assert insertIdx != -1 : "must setup insert position first";
258
259        insertionBuffer.append(insertIdx, createMove(fromOperand, toOperand));
260
261        if (Debug.isLogEnabled()) {
262            Debug.log("insert move from %s to %s at %d", fromOperand, toOperand, insertIdx);
263        }
264    }
265
266    /**
267     * @param fromOpr {@link Interval#operand operand} of the {@code from} interval
268     * @param toOpr {@link Interval#operand operand} of the {@code to} interval
269     */
270    private LIRInstruction createMove(Value fromOpr, AllocatableValue toOpr) {
271        if (isStackSlotValue(toOpr) && isStackSlotValue(fromOpr)) {
272            return getSpillMoveFactory().createStackMove(toOpr, fromOpr);
273        }
274        return getSpillMoveFactory().createMove(toOpr, fromOpr);
275    }
276
277    private void resolveMappings() {
278        try (Indent indent = Debug.logAndIndent("resolveMapping")) {
279            assert verifyBeforeResolve();
280            if (Debug.isLogEnabled()) {
281                printMapping();
282            }
283
284            // Block all registers that are used as input operands of a move.
285            // When a register is blocked, no move to this register is emitted.
286            // This is necessary for detecting cycles in moves.
287            for (int i = mappingFrom.size() - 1; i >= 0; i--) {
288                Value from = mappingFrom.get(i);
289                block(from);
290            }
291
292            int spillCandidate = -1;
293            while (mappingFrom.size() > 0) {
294                boolean processedInterval = false;
295
296                for (int i = mappingFrom.size() - 1; i >= 0; i--) {
297                    Value fromInterval = mappingFrom.get(i);
298                    AllocatableValue toInterval = mappingTo.get(i);
299
300                    Value fromLocation = fromInterval;
301                    AllocatableValue toLocation = toInterval;
302                    if (safeToProcessMove(fromLocation, toLocation)) {
303                        // this interval can be processed because target is free
304                        insertMove(fromLocation, toLocation);
305                        unblock(fromLocation);
306                        mappingFrom.remove(i);
307                        mappingTo.remove(i);
308
309                        processedInterval = true;
310                    } else if (fromInterval != null && isRegister(fromLocation)) {
311                        // this interval cannot be processed now because target is not free
312                        // it starts in a register, so it is a possible candidate for spilling
313                        spillCandidate = i;
314                    }
315                }
316
317                if (!processedInterval) {
318                    breakCycle(spillCandidate);
319                }
320            }
321        }
322
323        // check that all intervals have been processed
324        assert checkEmpty();
325    }
326
327    private void breakCycle(int spillCandidate) {
328        // no move could be processed because there is a cycle in the move list
329        // (e.g. r1 . r2, r2 . r1), so one interval must be spilled to memory
330        assert spillCandidate != -1 : "no interval in register for spilling found";
331
332        // create a new spill interval and assign a stack slot to it
333        Value from = mappingFrom.get(spillCandidate);
334        try (Indent indent = Debug.logAndIndent("BreakCycle: %s", from)) {
335            StackSlotValue spillSlot = frameMapBuilder.allocateSpillSlot(from.getLIRKind());
336            if (Debug.isLogEnabled()) {
337                Debug.log("created new slot for spilling: %s", spillSlot);
338            }
339            // insert a move from register to stack and update the mapping
340            insertMove(from, spillSlot);
341            block(spillSlot);
342            mappingFrom.set(spillCandidate, spillSlot);
343            unblock(from);
344        }
345    }
346
347    private void printMapping() {
348        try (Indent indent = Debug.logAndIndent("Mapping")) {
349            for (int i = mappingFrom.size() - 1; i >= 0; i--) {
350                Debug.log("move %s <- %s", mappingTo.get(i), mappingFrom.get(i));
351            }
352        }
353    }
354
355    public void setInsertPosition(List<LIRInstruction> insertList, int insertIdx) {
356        assert this.insertIdx == -1 : "use moveInsertPosition instead of setInsertPosition when data already set";
357
358        createInsertionBuffer(insertList);
359        this.insertIdx = insertIdx;
360    }
361
362    public void addMapping(Value from, AllocatableValue to) {
363        if (Debug.isLogEnabled()) {
364            Debug.log("add move mapping from %s to %s", from, to);
365        }
366
367        assert !from.equals(to) : "from and to interval equal: " + from;
368        assert LIRKind.verifyMoveKinds(to.getLIRKind(), from.getLIRKind()) : String.format("Kind mismatch: %s vs. %s, from=%s, to=%s", from.getLIRKind(), to.getLIRKind(), from, to);
369        mappingFrom.add(from);
370        mappingTo.add(to);
371    }
372
373    public void resolveAndAppendMoves() {
374        if (hasMappings()) {
375            resolveMappings();
376        }
377        appendInsertionBuffer();
378    }
379
380    private int getStackArrayIndex(StackSlotValue stackSlotValue) {
381        if (isStackSlot(stackSlotValue)) {
382            return getStackArrayIndex(asStackSlot(stackSlotValue));
383        }
384        if (isVirtualStackSlot(stackSlotValue)) {
385            return getStackArrayIndex(asVirtualStackSlot(stackSlotValue));
386        }
387        throw JVMCIError.shouldNotReachHere("Unhandled StackSlotValue: " + stackSlotValue);
388    }
389
390    private int getStackArrayIndex(StackSlot stackSlot) {
391        int stackIdx;
392        if (stackSlot.isInCallerFrame()) {
393            // incoming stack arguments can be ignored
394            stackIdx = STACK_SLOT_IN_CALLER_FRAME_IDX;
395        } else {
396            assert stackSlot.getRawAddFrameSize() : "Unexpected stack slot: " + stackSlot;
397            int offset = -stackSlot.getRawOffset();
398            assert 0 <= offset && offset < firstVirtualStackIndex : String.format("Wrong stack slot offset: %d (first virtual stack slot index: %d", offset, firstVirtualStackIndex);
399            stackIdx = offset;
400        }
401        return stackIdx;
402    }
403
404    private int getStackArrayIndex(VirtualStackSlot virtualStackSlot) {
405        return firstVirtualStackIndex + virtualStackSlot.getId();
406    }
407
408}