1/*
2 * Copyright (c) 2015, 2015, 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 */
23package org.graalvm.compiler.lir.alloc.trace.lsra;
24
25import static jdk.vm.ci.code.ValueUtil.asRegister;
26import static jdk.vm.ci.code.ValueUtil.isRegister;
27
28import org.graalvm.compiler.lir.LIRInstruction;
29
30import jdk.vm.ci.meta.AllocatableValue;
31import jdk.vm.ci.meta.Value;
32
33/**
34 * Represents a fixed interval.
35 */
36final class FixedInterval extends IntervalHint {
37
38    /**
39     * The fixed operand of this interval.
40     */
41    public final AllocatableValue operand;
42
43    /**
44     * The head of the list of ranges describing this interval. This list is sorted by
45     * {@linkplain LIRInstruction#id instruction ids}.
46     */
47    private FixedRange first;
48
49    /**
50     * Iterator used to traverse the ranges of an interval.
51     */
52    private FixedRange current;
53
54    /**
55     * Link to next interval in a sorted list of intervals that ends with {@link #EndMarker}.
56     */
57    FixedInterval next;
58
59    private int cachedTo; // cached value: to of last range (-1: not cached)
60
61    public FixedRange first() {
62        return first;
63    }
64
65    @Override
66    public int from() {
67        return first.from;
68    }
69
70    public int to() {
71        if (cachedTo == -1) {
72            cachedTo = calcTo();
73        }
74        assert cachedTo == calcTo() : "invalid cached value";
75        return cachedTo;
76    }
77
78    // test intersection
79    boolean intersects(TraceInterval i) {
80        return first.intersects(i);
81    }
82
83    int intersectsAt(TraceInterval i) {
84        return first.intersectsAt(i);
85    }
86
87    // range iteration
88    void rewindRange() {
89        current = first;
90    }
91
92    void nextRange() {
93        assert this != EndMarker : "not allowed on sentinel";
94        current = current.next;
95    }
96
97    int currentFrom() {
98        return current.from;
99    }
100
101    int currentTo() {
102        return current.to;
103    }
104
105    boolean currentAtEnd() {
106        return current == FixedRange.EndMarker;
107    }
108
109    boolean currentIntersects(TraceInterval it) {
110        return current.intersects(it);
111    }
112
113    int currentIntersectsAt(TraceInterval it) {
114        return current.intersectsAt(it);
115    }
116
117    // range creation
118    public void setFrom(int from) {
119        assert !isEmpty();
120        first().from = from;
121    }
122
123    private boolean isEmpty() {
124        return first() == FixedRange.EndMarker;
125    }
126
127    public void addRange(int from, int to) {
128        if (isEmpty()) {
129            first = new FixedRange(from, to, first());
130            return;
131        }
132        if (to <= to() && from >= from()) {
133            return;
134        }
135        if (from() == to) {
136            first().from = from;
137        } else {
138            first = new FixedRange(from, to, first());
139        }
140    }
141
142    @Override
143    public AllocatableValue location() {
144        return operand;
145    }
146
147    /**
148     * Sentinel interval to denote the end of an interval list.
149     */
150    static final FixedInterval EndMarker = new FixedInterval(Value.ILLEGAL);
151
152    FixedInterval(AllocatableValue operand) {
153        assert operand != null;
154        this.operand = operand;
155        this.first = FixedRange.EndMarker;
156        this.current = FixedRange.EndMarker;
157        this.next = FixedInterval.EndMarker;
158        this.cachedTo = -1;
159    }
160
161    int calcTo() {
162        assert first != FixedRange.EndMarker : "interval has no range";
163
164        FixedRange r = first;
165        while (r.next != FixedRange.EndMarker) {
166            r = r.next;
167        }
168        return r.to;
169    }
170
171    // returns true if the opId is inside the interval
172    boolean covers(int opId, LIRInstruction.OperandMode mode) {
173        FixedRange cur = first;
174
175        while (cur != FixedRange.EndMarker && cur.to < opId) {
176            cur = cur.next;
177        }
178        if (cur != FixedRange.EndMarker) {
179            assert cur.to != cur.next.from : "ranges not separated";
180
181            if (mode == LIRInstruction.OperandMode.DEF) {
182                return cur.from <= opId && opId < cur.to;
183            } else {
184                return cur.from <= opId && opId <= cur.to;
185            }
186        }
187        return false;
188    }
189
190    // returns true if the interval has any hole between holeFrom and holeTo
191    // (even if the hole has only the length 1)
192    boolean hasHoleBetween(int holeFrom, int holeTo) {
193        assert holeFrom < holeTo : "check";
194        assert from() <= holeFrom && holeTo <= to() : "index out of interval";
195
196        FixedRange cur = first;
197        while (cur != FixedRange.EndMarker) {
198            assert cur.to < cur.next.from : "no space between ranges";
199
200            // hole-range starts before this range . hole
201            if (holeFrom < cur.from) {
202                return true;
203
204                // hole-range completely inside this range . no hole
205            } else {
206                if (holeTo <= cur.to) {
207                    return false;
208
209                    // overlapping of hole-range with this range . hole
210                } else {
211                    if (holeFrom <= cur.to) {
212                        return true;
213                    }
214                }
215            }
216
217            cur = cur.next;
218        }
219
220        return false;
221    }
222
223    @Override
224    public String toString() {
225        if (this == EndMarker) {
226            return "EndMarker [?,?]";
227        }
228        String from = "?";
229        String to = "?";
230        if (first != null && first != FixedRange.EndMarker) {
231            from = String.valueOf(from());
232            // to() may cache a computed value, modifying the current object, which is a bad idea
233            // for a printing function. Compute it directly instead.
234            to = String.valueOf(calcTo());
235        }
236        String locationString = "@" + this.operand;
237        return asRegister(operand).number + ":" + operand + (isRegister(operand) ? "" : locationString) + "[" + from + "," + to + "]";
238    }
239
240    /**
241     * Gets a single line string for logging the details of this interval to a log stream.
242     */
243    @Override
244    public String logString() {
245        StringBuilder buf = new StringBuilder(100);
246        buf.append("fix ").append(asRegister(operand).number).append(':').append(operand).append(' ');
247
248        buf.append(" ranges{");
249
250        // print ranges
251        FixedRange cur = first;
252        while (cur != FixedRange.EndMarker) {
253            if (cur != first) {
254                buf.append(", ");
255            }
256            buf.append(cur);
257            cur = cur.next;
258            assert cur != null : "range list not closed with range sentinel";
259        }
260        buf.append("}");
261        return buf.toString();
262    }
263
264}
265