001/*
002 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 *  This file is licensed to You under the Eclipse Public License (EPL);
005 *  You may not use this file except in compliance with the License. You
006 *  may obtain a copy of the License at
007 *
008 *      http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 *  See the COPYRIGHT.txt file distributed with this work for information
011 *  regarding copyright ownership.
012 */
013package org.jikesrvm.compilers.opt.regalloc;
014
015import java.util.ArrayList;
016import java.util.HashMap;
017import java.util.HashSet;
018import org.jikesrvm.compilers.opt.ir.Instruction;
019import org.jikesrvm.compilers.opt.ir.Register;
020
021/**
022 * This class holds information on scratch register usage, needed to
023 * adjust GC Maps.
024 */
025public final class ScratchMap {
026
027  private static final boolean DEBUG = false;
028
029  /**
030   * For each register, the set of intervals describing the register.
031   */
032  private final HashMap<Register, ArrayList<Interval>> map = new HashMap<Register, ArrayList<Interval>>();
033
034  /**
035   * For each register, a pending (incomplete) interval under
036   * construction.
037   */
038  private final HashMap<Register, Interval> pending = new HashMap<Register, Interval>();
039
040  /**
041   * For each GC Point s, a set of symbolic registers that are cached in
042   * dirty scratch registers before s.
043   */
044  private final HashMap<Instruction, HashSet<Register>> dirtyMap =
045      new HashMap<Instruction, HashSet<Register>>();
046
047  private final RegisterAllocatorState regAllocState;
048
049  public ScratchMap(RegisterAllocatorState regAllocState) {
050    this.regAllocState = regAllocState;
051  }
052
053  /**
054   * Begin a new interval of scratch-ness for a symbolic register.
055   *
056   * @param r the symbolic register being moved into scratch
057   * @param scratch the physical register being used as a scratch
058   * @param begin the instruction before which the physical register is
059   * vacated.
060   */
061  void beginSymbolicInterval(Register r, Register scratch, Instruction begin) {
062    if (DEBUG) {
063      System.out.println("beginSymbolicInterval " + r + " " + scratch + " " +
064          regAllocState.getDFN(begin));
065    }
066
067    SymbolicInterval i = new SymbolicInterval(r, scratch);
068    i.begin = begin;
069    ArrayList<Interval> v = findOrCreateIntervalSet(r);
070    v.add(i);
071    pending.put(r, i);
072  }
073
074  /**
075   * End an interval of scratch-ness for a symbolic register.
076   *
077   * @param r the symbolic register being moved into scratch
078   * @param end the instruction before which the scratch interval ends
079   */
080  public void endSymbolicInterval(Register r, Instruction end) {
081    if (DEBUG) {
082      System.out.println("endSymbolicInterval " + r + " " +
083          regAllocState.getDFN(end));
084    }
085
086    SymbolicInterval i = (SymbolicInterval) pending.get(r);
087    i.end = end;
088    pending.remove(r);
089  }
090
091  /**
092   * Begin a new interval of scratch-ness for a physical register.
093   *
094   * @param r the physical register being used as a scratch
095   * @param begin the instruction before which the physical register is
096   * vacated.
097   */
098  void beginScratchInterval(Register r, Instruction begin) {
099    if (DEBUG) {
100      System.out.println("beginScratchInterval " + r + " " +
101          regAllocState.getDFN(begin));
102    }
103    PhysicalInterval p = new PhysicalInterval(r);
104    p.begin = begin;
105    ArrayList<Interval> v = findOrCreateIntervalSet(r);
106    v.add(p);
107    pending.put(r, p);
108  }
109
110  /**
111   * End an interval of scratch-ness for a physical register.
112   *
113   * @param r the physical register being used as a scratch
114   * @param end the instruction before which the physical register is
115   * vacated.
116   */
117  public void endScratchInterval(Register r, Instruction end) {
118    if (DEBUG) {
119      System.out.println("endScratchInterval " + r + " " +
120          regAllocState.getDFN(end));
121    }
122    PhysicalInterval p = (PhysicalInterval) pending.get(r);
123    p.end = end;
124    pending.remove(r);
125  }
126
127  /**
128   * Find or create the set of intervals corresponding to a register r.
129   *
130   * @param r the register to check
131   * @return a possibly empty list of intervals
132   */
133  private ArrayList<Interval> findOrCreateIntervalSet(Register r) {
134    ArrayList<Interval> v = map.get(r);
135    if (v == null) {
136      v = new ArrayList<Interval>();
137      map.put(r, v);
138    }
139    return v;
140  }
141
142  /**
143   * Is the given physical register being used as a scratch register
144   * in the given instruction?
145   * @param r a physical register
146   * @param n the instruction's number
147   * @return {@code true} if the register is used as a scratch register
148   *  in the instruction, {@code false} otherwise
149   */
150  boolean isScratch(Register r, int n) {
151    ArrayList<Interval> v = map.get(r);
152    if (v == null) return false;
153    for (final Interval interval : v) {
154      if (interval.contains(n)) return true;
155    }
156    return false;
157  }
158
159  /**
160   * Gets the scratch register if a matching one exists.
161   *
162   * @param r a symbolic register
163   * @param n the instruction number
164   * @return if a symbolic register resides in a scratch register at an
165   * instruction with the given number, then return the scratch register. Else,
166   * return {@code null}.
167   */
168  Register getScratch(Register r, int n) {
169    ArrayList<Interval> v = map.get(r);
170    if (v == null) return null;
171    for (Interval i : v) {
172      if (i.contains(n)) return i.scratch;
173    }
174    return null;
175  }
176
177  public boolean isEmpty() {
178    return map.isEmpty();
179  }
180
181  /**
182   * Records that the real value of a symbolic register is cached in
183   * a dirty scratch register at a given instruction that is a GC point.
184   *
185   * @param s an instruction that is a GC point. Note: it is the caller's
186   *    responsibility to check this
187   * @param symb the symbolic register
188   */
189  public void markDirty(Instruction s, Register symb) {
190    HashSet<Register> set = dirtyMap.get(s);
191    if (set == null) {
192      set = new HashSet<Register>(3);
193      dirtyMap.put(s, set);
194    }
195    set.add(symb);
196  }
197
198  /**
199   * At GC point s, is the value of register r cached in a dirty scratch
200   * register?
201   *
202   * @param s an instruction that is a GC point
203   * @param r register to check
204   * @return {@code true} if the register is in a scratch register and
205   *  the scratch register is dirty, {@code false} otherwise
206   */
207  public boolean isDirty(Instruction s, Register r) {
208    HashSet<Register> set = dirtyMap.get(s);
209    if (set == null) {
210      return false;
211    } else {
212      return set.contains(r);
213    }
214  }
215
216  @Override
217  public String toString() {
218    StringBuilder result = new StringBuilder();
219    for (ArrayList<Interval> v : map.values()) {
220      for (Interval i : v) {
221        result.append(i);
222        result.append("\n");
223      }
224    }
225    return result.toString();
226  }
227
228  /**
229   * Super class of physical and symbolic intervals
230   */
231  private abstract class Interval {
232    /**
233     * The instruction before which the scratch range begins.
234     */
235    protected Instruction begin;
236    /**
237     * The instruction before which the scratch range ends.
238     */
239    protected Instruction end;
240    /**
241     * The physical scratch register or register evicted.
242     */
243    protected final Register scratch;
244
245    protected Interval(Register scratch) {
246      this.scratch = scratch;
247    }
248
249    /**
250     * Does this interval contain the instruction numbered n?
251     *
252     * @param n instruction number
253     * @return {@code true} if and only if the instruction with the
254     *   given number is contained n this interval
255     */
256    protected final boolean contains(int n) {
257      return (regAllocState.getDFN(begin) <= n &&
258          regAllocState.getDFN(end) > n);
259    }
260  }
261
262  /**
263   * An object that represents an interval where a symbolic register
264   * resides in a scratch register.<p>
265   *
266   * Note that this interval must not span a basic block.
267   */
268  private final class SymbolicInterval extends Interval {
269    /**
270     * The symbolic register
271     */
272    private final Register symbolic;
273
274    SymbolicInterval(Register symbolic, Register scratch) {
275      super(scratch);
276      this.symbolic = symbolic;
277    }
278
279    /**
280     * Return a string representation, assuming the 'scratch' field of
281     * Instruction identifies an instruction.
282     *
283     * @return a string representation of this interval
284     */
285    @Override
286    public String toString() {
287      return "SI: " + symbolic + " " + scratch + " [" +
288          regAllocState.getDFN(begin) + "," + regAllocState.getDFN(end) + "]";
289    }
290  }
291
292  /**
293   * An object that represents an interval where a physical register's
294   * contents are evicted so that the physical register can be used as a
295   * scratch.<p>
296   *
297   * Note that this interval must not span a basic block.
298   */
299  private final class PhysicalInterval extends Interval {
300    PhysicalInterval(Register scratch) {
301      super(scratch);
302    }
303
304    /**
305     * Return a string representation, assuming the 'scratch' field of
306     * Instruction identifies an instruction.
307     *
308     * @return a string representation of this interval
309     */
310    @Override
311    public String toString() {
312      return "PI: " + scratch + " [" + regAllocState.getDFN(begin) +
313          "," + regAllocState.getDFN(end) + "]";
314    }
315  }
316}