/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.compilers.opt.regalloc; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import org.jikesrvm.compilers.opt.ir.Instruction; import org.jikesrvm.compilers.opt.ir.Register; /** * This class holds information on scratch register usage, needed to * adjust GC Maps. */ public final class ScratchMap { private static final boolean DEBUG = false; /** * For each register, the set of intervals describing the register. */ private final HashMap<Register, ArrayList<Interval>> map = new HashMap<Register, ArrayList<Interval>>(); /** * For each register, a pending (incomplete) interval under * construction. */ private final HashMap<Register, Interval> pending = new HashMap<Register, Interval>(); /** * For each GC Point s, a set of symbolic registers that are cached in * dirty scratch registers before s. */ private final HashMap<Instruction, HashSet<Register>> dirtyMap = new HashMap<Instruction, HashSet<Register>>(); private final RegisterAllocatorState regAllocState; public ScratchMap(RegisterAllocatorState regAllocState) { this.regAllocState = regAllocState; } /** * Begin a new interval of scratch-ness for a symbolic register. * * @param r the symbolic register being moved into scratch * @param scratch the physical register being used as a scratch * @param begin the instruction before which the physical register is * vacated. */ void beginSymbolicInterval(Register r, Register scratch, Instruction begin) { if (DEBUG) { System.out.println("beginSymbolicInterval " + r + " " + scratch + " " + regAllocState.getDFN(begin)); } SymbolicInterval i = new SymbolicInterval(r, scratch); i.begin = begin; ArrayList<Interval> v = findOrCreateIntervalSet(r); v.add(i); pending.put(r, i); } /** * End an interval of scratch-ness for a symbolic register. * * @param r the symbolic register being moved into scratch * @param end the instruction before which the scratch interval ends */ public void endSymbolicInterval(Register r, Instruction end) { if (DEBUG) { System.out.println("endSymbolicInterval " + r + " " + regAllocState.getDFN(end)); } SymbolicInterval i = (SymbolicInterval) pending.get(r); i.end = end; pending.remove(r); } /** * Begin a new interval of scratch-ness for a physical register. * * @param r the physical register being used as a scratch * @param begin the instruction before which the physical register is * vacated. */ void beginScratchInterval(Register r, Instruction begin) { if (DEBUG) { System.out.println("beginScratchInterval " + r + " " + regAllocState.getDFN(begin)); } PhysicalInterval p = new PhysicalInterval(r); p.begin = begin; ArrayList<Interval> v = findOrCreateIntervalSet(r); v.add(p); pending.put(r, p); } /** * End an interval of scratch-ness for a physical register. * * @param r the physical register being used as a scratch * @param end the instruction before which the physical register is * vacated. */ public void endScratchInterval(Register r, Instruction end) { if (DEBUG) { System.out.println("endScratchInterval " + r + " " + regAllocState.getDFN(end)); } PhysicalInterval p = (PhysicalInterval) pending.get(r); p.end = end; pending.remove(r); } /** * Find or create the set of intervals corresponding to a register r. * * @param r the register to check * @return a possibly empty list of intervals */ private ArrayList<Interval> findOrCreateIntervalSet(Register r) { ArrayList<Interval> v = map.get(r); if (v == null) { v = new ArrayList<Interval>(); map.put(r, v); } return v; } /** * Is the given physical register being used as a scratch register * in the given instruction? * @param r a physical register * @param n the instruction's number * @return {@code true} if the register is used as a scratch register * in the instruction, {@code false} otherwise */ boolean isScratch(Register r, int n) { ArrayList<Interval> v = map.get(r); if (v == null) return false; for (final Interval interval : v) { if (interval.contains(n)) return true; } return false; } /** * Gets the scratch register if a matching one exists. * * @param r a symbolic register * @param n the instruction number * @return if a symbolic register resides in a scratch register at an * instruction with the given number, then return the scratch register. Else, * return {@code null}. */ Register getScratch(Register r, int n) { ArrayList<Interval> v = map.get(r); if (v == null) return null; for (Interval i : v) { if (i.contains(n)) return i.scratch; } return null; } public boolean isEmpty() { return map.isEmpty(); } /** * Records that the real value of a symbolic register is cached in * a dirty scratch register at a given instruction that is a GC point. * * @param s an instruction that is a GC point. Note: it is the caller's * responsibility to check this * @param symb the symbolic register */ public void markDirty(Instruction s, Register symb) { HashSet<Register> set = dirtyMap.get(s); if (set == null) { set = new HashSet<Register>(3); dirtyMap.put(s, set); } set.add(symb); } /** * At GC point s, is the value of register r cached in a dirty scratch * register? * * @param s an instruction that is a GC point * @param r register to check * @return {@code true} if the register is in a scratch register and * the scratch register is dirty, {@code false} otherwise */ public boolean isDirty(Instruction s, Register r) { HashSet<Register> set = dirtyMap.get(s); if (set == null) { return false; } else { return set.contains(r); } } @Override public String toString() { StringBuilder result = new StringBuilder(); for (ArrayList<Interval> v : map.values()) { for (Interval i : v) { result.append(i); result.append("\n"); } } return result.toString(); } /** * Super class of physical and symbolic intervals */ private abstract class Interval { /** * The instruction before which the scratch range begins. */ protected Instruction begin; /** * The instruction before which the scratch range ends. */ protected Instruction end; /** * The physical scratch register or register evicted. */ protected final Register scratch; protected Interval(Register scratch) { this.scratch = scratch; } /** * Does this interval contain the instruction numbered n? * * @param n instruction number * @return {@code true} if and only if the instruction with the * given number is contained n this interval */ protected final boolean contains(int n) { return (regAllocState.getDFN(begin) <= n && regAllocState.getDFN(end) > n); } } /** * An object that represents an interval where a symbolic register * resides in a scratch register.<p> * * Note that this interval must not span a basic block. */ private final class SymbolicInterval extends Interval { /** * The symbolic register */ private final Register symbolic; SymbolicInterval(Register symbolic, Register scratch) { super(scratch); this.symbolic = symbolic; } /** * Return a string representation, assuming the 'scratch' field of * Instruction identifies an instruction. * * @return a string representation of this interval */ @Override public String toString() { return "SI: " + symbolic + " " + scratch + " [" + regAllocState.getDFN(begin) + "," + regAllocState.getDFN(end) + "]"; } } /** * An object that represents an interval where a physical register's * contents are evicted so that the physical register can be used as a * scratch.<p> * * Note that this interval must not span a basic block. */ private final class PhysicalInterval extends Interval { PhysicalInterval(Register scratch) { super(scratch); } /** * Return a string representation, assuming the 'scratch' field of * Instruction identifies an instruction. * * @return a string representation of this interval */ @Override public String toString() { return "PI: " + scratch + " [" + regAllocState.getDFN(begin) + "," + regAllocState.getDFN(end) + "]"; } } }