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.lsra;
024
025import static java.lang.String.*;
026import static jdk.internal.jvmci.code.ValueUtil.*;
027
028import java.util.*;
029
030import jdk.internal.jvmci.code.*;
031import jdk.internal.jvmci.common.*;
032import com.oracle.graal.debug.*;
033import jdk.internal.jvmci.meta.*;
034
035import com.oracle.graal.lir.*;
036
037/**
038 */
039public class MoveResolver {
040
041    private final LinearScan allocator;
042
043    private int insertIdx;
044    private LIRInsertionBuffer insertionBuffer; // buffer where moves are inserted
045
046    private final List<Interval> mappingFrom;
047    private final List<Value> mappingFromOpr;
048    private final List<Interval> mappingTo;
049    private boolean multipleReadsAllowed;
050    private final int[] registerBlocked;
051
052    protected void setValueBlocked(Value location, int direction) {
053        assert direction == 1 || direction == -1 : "out of bounds";
054        if (isRegister(location)) {
055            registerBlocked[asRegister(location).number] += direction;
056        } else {
057            throw JVMCIError.shouldNotReachHere("unhandled value " + location);
058        }
059    }
060
061    protected Interval getMappingFrom(int i) {
062        return mappingFrom.get(i);
063    }
064
065    protected int mappingFromSize() {
066        return mappingFrom.size();
067    }
068
069    protected int valueBlocked(Value location) {
070        if (isRegister(location)) {
071            return registerBlocked[asRegister(location).number];
072        }
073        throw JVMCIError.shouldNotReachHere("unhandled value " + location);
074    }
075
076    void setMultipleReadsAllowed() {
077        multipleReadsAllowed = true;
078    }
079
080    protected boolean areMultipleReadsAllowed() {
081        return multipleReadsAllowed;
082    }
083
084    boolean hasMappings() {
085        return mappingFrom.size() > 0;
086    }
087
088    protected LinearScan getAllocator() {
089        return allocator;
090    }
091
092    protected MoveResolver(LinearScan allocator) {
093
094        this.allocator = allocator;
095        this.multipleReadsAllowed = false;
096        this.mappingFrom = new ArrayList<>(8);
097        this.mappingFromOpr = new ArrayList<>(8);
098        this.mappingTo = new ArrayList<>(8);
099        this.insertIdx = -1;
100        this.insertionBuffer = new LIRInsertionBuffer();
101        this.registerBlocked = new int[allocator.getRegisters().length];
102    }
103
104    protected boolean checkEmpty() {
105        assert mappingFrom.size() == 0 && mappingFromOpr.size() == 0 && mappingTo.size() == 0 : "list must be empty before and after processing";
106        for (int i = 0; i < getAllocator().getRegisters().length; i++) {
107            assert registerBlocked[i] == 0 : "register map must be empty before and after processing";
108        }
109        checkMultipleReads();
110        return true;
111    }
112
113    protected void checkMultipleReads() {
114        assert !areMultipleReadsAllowed() : "must have default value";
115    }
116
117    private boolean verifyBeforeResolve() {
118        assert mappingFrom.size() == mappingFromOpr.size() : "length must be equal";
119        assert mappingFrom.size() == mappingTo.size() : "length must be equal";
120        assert insertIdx != -1 : "insert position not set";
121
122        int i;
123        int j;
124        if (!areMultipleReadsAllowed()) {
125            for (i = 0; i < mappingFrom.size(); i++) {
126                for (j = i + 1; j < mappingFrom.size(); j++) {
127                    assert mappingFrom.get(i) == null || mappingFrom.get(i) != mappingFrom.get(j) : "cannot read from same interval twice";
128                }
129            }
130        }
131
132        for (i = 0; i < mappingTo.size(); i++) {
133            for (j = i + 1; j < mappingTo.size(); j++) {
134                assert mappingTo.get(i) != mappingTo.get(j) : "cannot write to same interval twice";
135            }
136        }
137
138        HashSet<Value> usedRegs = new HashSet<>();
139        if (!areMultipleReadsAllowed()) {
140            for (i = 0; i < mappingFrom.size(); i++) {
141                Interval interval = mappingFrom.get(i);
142                if (interval != null && !isIllegal(interval.location())) {
143                    boolean unique = usedRegs.add(interval.location());
144                    assert unique : "cannot read from same register twice";
145                }
146            }
147        }
148
149        usedRegs.clear();
150        for (i = 0; i < mappingTo.size(); i++) {
151            Interval interval = mappingTo.get(i);
152            if (isIllegal(interval.location())) {
153                // After insertion the location may become illegal, so don't check it since multiple
154                // intervals might be illegal.
155                continue;
156            }
157            boolean unique = usedRegs.add(interval.location());
158            assert unique : "cannot write to same register twice";
159        }
160
161        verifyStackSlotMapping();
162
163        return true;
164    }
165
166    protected void verifyStackSlotMapping() {
167        HashSet<Value> usedRegs = new HashSet<>();
168        for (int i = 0; i < mappingFrom.size(); i++) {
169            Interval interval = mappingFrom.get(i);
170            if (interval != null && !isRegister(interval.location())) {
171                usedRegs.add(interval.location());
172            }
173        }
174        for (int i = 0; i < mappingTo.size(); i++) {
175            Interval interval = mappingTo.get(i);
176            assert !usedRegs.contains(interval.location()) || checkIntervalLocation(mappingFrom.get(i), interval, mappingFromOpr.get(i)) : "stack slots used in mappingFrom must be disjoint to mappingTo";
177        }
178    }
179
180    private static boolean checkIntervalLocation(Interval from, Interval to, Value fromOpr) {
181        if (from == null) {
182            return fromOpr != null;
183        } else {
184            return to.location().equals(from.location());
185        }
186    }
187
188    // mark assignedReg and assignedRegHi of the interval as blocked
189    private void blockRegisters(Interval interval) {
190        Value location = interval.location();
191        if (mightBeBlocked(location)) {
192            assert areMultipleReadsAllowed() || valueBlocked(location) == 0 : "location already marked as used: " + location;
193            int direction = 1;
194            setValueBlocked(location, direction);
195            Debug.log("block %s", location);
196        }
197    }
198
199    // mark assignedReg and assignedRegHi of the interval as unblocked
200    private void unblockRegisters(Interval interval) {
201        Value location = interval.location();
202        if (mightBeBlocked(location)) {
203            assert valueBlocked(location) > 0 : "location already marked as unused: " + location;
204            setValueBlocked(location, -1);
205            Debug.log("unblock %s", location);
206        }
207    }
208
209    /**
210     * Checks if the {@linkplain Interval#location() location} of {@code to} is not blocked or is
211     * only blocked by {@code from}.
212     */
213    private boolean safeToProcessMove(Interval from, Interval to) {
214        Value fromReg = from != null ? from.location() : null;
215
216        Value location = to.location();
217        if (mightBeBlocked(location)) {
218            if ((valueBlocked(location) > 1 || (valueBlocked(location) == 1 && !isMoveToSelf(fromReg, location)))) {
219                return false;
220            }
221        }
222
223        return true;
224    }
225
226    protected boolean isMoveToSelf(Value from, Value to) {
227        assert to != null;
228        if (to.equals(from)) {
229            return true;
230        }
231        if (from != null && isRegister(from) && isRegister(to) && asRegister(from).equals(asRegister(to))) {
232            assert LIRKind.verifyMoveKinds(to.getLIRKind(), from.getLIRKind()) : String.format("Same register but Kind mismatch %s <- %s", to, from);
233            return true;
234        }
235        return false;
236    }
237
238    protected boolean mightBeBlocked(Value location) {
239        return isRegister(location);
240    }
241
242    private void createInsertionBuffer(List<LIRInstruction> list) {
243        assert !insertionBuffer.initialized() : "overwriting existing buffer";
244        insertionBuffer.init(list);
245    }
246
247    private void appendInsertionBuffer() {
248        if (insertionBuffer.initialized()) {
249            insertionBuffer.finish();
250        }
251        assert !insertionBuffer.initialized() : "must be uninitialized now";
252
253        insertIdx = -1;
254    }
255
256    private void insertMove(Interval fromInterval, Interval toInterval) {
257        assert !fromInterval.operand.equals(toInterval.operand) : "from and to interval equal: " + fromInterval;
258        assert LIRKind.verifyMoveKinds(toInterval.kind(), fromInterval.kind()) : "move between different types";
259        assert insertIdx != -1 : "must setup insert position first";
260
261        insertionBuffer.append(insertIdx, createMove(fromInterval.operand, toInterval.operand, fromInterval.location(), toInterval.location()));
262
263        if (Debug.isLogEnabled()) {
264            Debug.log("insert move from %s to %s at %d", fromInterval, toInterval, insertIdx);
265        }
266    }
267
268    /**
269     * @param fromOpr {@link Interval#operand operand} of the {@code from} interval
270     * @param toOpr {@link Interval#operand operand} of the {@code to} interval
271     * @param fromLocation {@link Interval#location() location} of the {@code to} interval
272     * @param toLocation {@link Interval#location() location} of the {@code to} interval
273     */
274    protected LIRInstruction createMove(AllocatableValue fromOpr, AllocatableValue toOpr, AllocatableValue fromLocation, AllocatableValue toLocation) {
275        return getAllocator().getSpillMoveFactory().createMove(toOpr, fromOpr);
276    }
277
278    private void insertMove(Value fromOpr, Interval toInterval) {
279        assert LIRKind.verifyMoveKinds(toInterval.kind(), fromOpr.getLIRKind()) : format("move between different types %s %s", fromOpr.getLIRKind(), toInterval.kind());
280        assert insertIdx != -1 : "must setup insert position first";
281
282        AllocatableValue toOpr = toInterval.operand;
283        LIRInstruction move = getAllocator().getSpillMoveFactory().createMove(toOpr, fromOpr);
284        insertionBuffer.append(insertIdx, move);
285
286        if (Debug.isLogEnabled()) {
287            Debug.log("insert move from value %s to %s at %d", fromOpr, toInterval, insertIdx);
288        }
289    }
290
291    private void resolveMappings() {
292        try (Indent indent = Debug.logAndIndent("resolveMapping")) {
293            assert verifyBeforeResolve();
294            if (Debug.isLogEnabled()) {
295                printMapping();
296            }
297
298            // Block all registers that are used as input operands of a move.
299            // When a register is blocked, no move to this register is emitted.
300            // This is necessary for detecting cycles in moves.
301            int i;
302            for (i = mappingFrom.size() - 1; i >= 0; i--) {
303                Interval fromInterval = mappingFrom.get(i);
304                if (fromInterval != null) {
305                    blockRegisters(fromInterval);
306                }
307            }
308
309            int spillCandidate = -1;
310            while (mappingFrom.size() > 0) {
311                boolean processedInterval = false;
312
313                for (i = mappingFrom.size() - 1; i >= 0; i--) {
314                    Interval fromInterval = mappingFrom.get(i);
315                    Interval toInterval = mappingTo.get(i);
316
317                    if (safeToProcessMove(fromInterval, toInterval)) {
318                        // this interval can be processed because target is free
319                        if (fromInterval != null) {
320                            insertMove(fromInterval, toInterval);
321                            unblockRegisters(fromInterval);
322                        } else {
323                            insertMove(mappingFromOpr.get(i), toInterval);
324                        }
325                        mappingFrom.remove(i);
326                        mappingFromOpr.remove(i);
327                        mappingTo.remove(i);
328
329                        processedInterval = true;
330                    } else if (fromInterval != null && isRegister(fromInterval.location())) {
331                        // this interval cannot be processed now because target is not free
332                        // it starts in a register, so it is a possible candidate for spilling
333                        spillCandidate = i;
334                    }
335                }
336
337                if (!processedInterval) {
338                    breakCycle(spillCandidate);
339                }
340            }
341        }
342
343        // reset to default value
344        multipleReadsAllowed = false;
345
346        // check that all intervals have been processed
347        assert checkEmpty();
348    }
349
350    protected void breakCycle(int spillCandidate) {
351        // no move could be processed because there is a cycle in the move list
352        // (e.g. r1 . r2, r2 . r1), so one interval must be spilled to memory
353        assert spillCandidate != -1 : "no interval in register for spilling found";
354
355        // create a new spill interval and assign a stack slot to it
356        Interval fromInterval = mappingFrom.get(spillCandidate);
357        // do not allocate a new spill slot for temporary interval, but
358        // use spill slot assigned to fromInterval. Otherwise moves from
359        // one stack slot to another can happen (not allowed by LIRAssembler
360        StackSlotValue spillSlot = fromInterval.spillSlot();
361        if (spillSlot == null) {
362            spillSlot = getAllocator().getFrameMapBuilder().allocateSpillSlot(fromInterval.kind());
363            fromInterval.setSpillSlot(spillSlot);
364        }
365        spillInterval(spillCandidate, fromInterval, spillSlot);
366    }
367
368    protected void spillInterval(int spillCandidate, Interval fromInterval, StackSlotValue spillSlot) {
369        assert mappingFrom.get(spillCandidate).equals(fromInterval);
370        Interval spillInterval = getAllocator().createDerivedInterval(fromInterval);
371        spillInterval.setKind(fromInterval.kind());
372
373        // add a dummy range because real position is difficult to calculate
374        // Note: this range is a special case when the integrity of the allocation is
375        // checked
376        spillInterval.addRange(1, 2);
377
378        spillInterval.assignLocation(spillSlot);
379
380        if (Debug.isLogEnabled()) {
381            Debug.log("created new Interval for spilling: %s", spillInterval);
382        }
383        blockRegisters(spillInterval);
384
385        // insert a move from register to stack and update the mapping
386        insertMove(fromInterval, spillInterval);
387        mappingFrom.set(spillCandidate, spillInterval);
388        unblockRegisters(fromInterval);
389    }
390
391    private void printMapping() {
392        try (Indent indent = Debug.logAndIndent("Mapping")) {
393            for (int i = mappingFrom.size() - 1; i >= 0; i--) {
394                Interval fromInterval = mappingFrom.get(i);
395                Interval toInterval = mappingTo.get(i);
396                Value from;
397                Value to = toInterval.location();
398                if (fromInterval == null) {
399                    from = mappingFromOpr.get(i);
400                } else {
401                    from = fromInterval.location();
402                }
403                Debug.log("move %s <- %s", from, to);
404            }
405        }
406    }
407
408    void setInsertPosition(List<LIRInstruction> insertList, int insertIdx) {
409        assert this.insertIdx == -1 : "use moveInsertPosition instead of setInsertPosition when data already set";
410
411        createInsertionBuffer(insertList);
412        this.insertIdx = insertIdx;
413    }
414
415    void moveInsertPosition(List<LIRInstruction> newInsertList, int newInsertIdx) {
416        if (insertionBuffer.lirList() != null && (insertionBuffer.lirList() != newInsertList || this.insertIdx != newInsertIdx)) {
417            // insert position changed . resolve current mappings
418            resolveMappings();
419        }
420
421        if (insertionBuffer.lirList() != newInsertList) {
422            // block changed . append insertionBuffer because it is
423            // bound to a specific block and create a new insertionBuffer
424            appendInsertionBuffer();
425            createInsertionBuffer(newInsertList);
426        }
427
428        this.insertIdx = newInsertIdx;
429    }
430
431    public void addMapping(Interval fromInterval, Interval toInterval) {
432
433        if (isIllegal(toInterval.location()) && toInterval.canMaterialize()) {
434            if (Debug.isLogEnabled()) {
435                Debug.log("no store to rematerializable interval %s needed", toInterval);
436            }
437            return;
438        }
439        if (isIllegal(fromInterval.location()) && fromInterval.canMaterialize()) {
440            // Instead of a reload, re-materialize the value
441            Value rematValue = fromInterval.getMaterializedValue();
442            addMapping(rematValue, toInterval);
443            return;
444        }
445        if (Debug.isLogEnabled()) {
446            Debug.log("add move mapping from %s to %s", fromInterval, toInterval);
447        }
448
449        assert !fromInterval.operand.equals(toInterval.operand) : "from and to interval equal: " + fromInterval;
450        assert LIRKind.verifyMoveKinds(toInterval.kind(), fromInterval.kind()) : String.format("Kind mismatch: %s vs. %s, from=%s, to=%s", fromInterval.kind(), toInterval.kind(), fromInterval,
451                        toInterval);
452        mappingFrom.add(fromInterval);
453        mappingFromOpr.add(Value.ILLEGAL);
454        mappingTo.add(toInterval);
455    }
456
457    public void addMapping(Value fromOpr, Interval toInterval) {
458        if (Debug.isLogEnabled()) {
459            Debug.log("add move mapping from %s to %s", fromOpr, toInterval);
460        }
461
462        assert isConstant(fromOpr) : "only for constants";
463
464        mappingFrom.add(null);
465        mappingFromOpr.add(fromOpr);
466        mappingTo.add(toInterval);
467    }
468
469    void resolveAndAppendMoves() {
470        if (hasMappings()) {
471            resolveMappings();
472        }
473        appendInsertionBuffer();
474    }
475}