/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.vm.compiler.ir;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.jnode.vm.objects.BootableArrayList;
/**
* @author Madhu Siddalingaiah
*/
public class LinearScanAllocator<T> {
private LiveRange<T>[] liveRanges;
private List<LiveRange<T>> active;
private RegisterPool<T> registerPool;
private EndPointComparator<T> endPointComparator;
private List<Variable<T>> spilledVariableList;
private Variable<T>[] spilledVariables;
public LinearScanAllocator(LiveRange<T>[] liveRanges) {
this.liveRanges = liveRanges;
final CodeGenerator<T> cg = CodeGenerator.getInstance();
this.registerPool = cg.getRegisterPool();
this.active = new BootableArrayList<LiveRange<T>>();
this.endPointComparator = new EndPointComparator<T>();
this.spilledVariableList = new BootableArrayList<Variable<T>>();
}
public void allocate() {
Arrays.sort(liveRanges);
int n = liveRanges.length;
for (int i = 0; i < n; i += 1) {
LiveRange<T> lr = liveRanges[i];
Variable<T> var = lr.getVariable();
if (!(var instanceof MethodArgument)) {
// don't allocate method arguments to registers
expireOldRange(lr);
T reg = registerPool.request(var.getType());
if (reg == null) {
spillRange(lr);
} else {
lr.setLocation(new RegisterLocation<T>(reg));
active.add(lr);
Collections.sort(active, endPointComparator);
}
}
}
// This sort is probably not necessary...
Collections.sort(spilledVariableList, new StorageSizeComparator<T>());
n = spilledVariableList.size();
spilledVariables = new Variable[n];
for (int i = 0; i < n; i += 1) {
spilledVariables[i] = spilledVariableList.get(i);
}
}
public Variable<T>[] getSpilledVariables() {
return spilledVariables;
}
/**
* @param lr
*/
private void expireOldRange(LiveRange<T> lr) {
for (LiveRange<T> l : new ArrayList<LiveRange>(active)) {
if (l.getLastUseAddress() >= lr.getAssignAddress()) {
return;
}
active.remove(l);
RegisterLocation<T> regLoc = (RegisterLocation<T>) l.getLocation();
registerPool.release(regLoc.getRegister());
}
}
/**
* @param lr
*/
private void spillRange(LiveRange<T> lr) {
if (active.isEmpty() || lr.getVariable().getType() == Operand.LONG ||
lr.getVariable().getType() == Operand.DOUBLE) {
lr.setLocation(new StackLocation<T>());
this.spilledVariableList.add(lr.getVariable());
return;
}
LiveRange<T> spill = active.get(active.size() - 1);
if (spill.getLastUseAddress() > lr.getLastUseAddress()) {
lr.setLocation(spill.getLocation());
spill.setLocation(new StackLocation<T>());
this.spilledVariableList.add(spill.getVariable());
active.remove(spill);
active.add(lr);
Collections.sort(active);
} else {
lr.setLocation(new StackLocation<T>());
this.spilledVariableList.add(lr.getVariable());
}
}
}
class EndPointComparator<T> implements Comparator<LiveRange<T>> {
/**
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(LiveRange<T> lr1, LiveRange<T> lr2) {
return lr1.getLastUseAddress() - lr2.getLastUseAddress();
}
}
class StorageSizeComparator<T> implements Comparator<Variable<T>> {
/**
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(Variable<T> lr1, Variable<T> lr2) {
int size1 = 0;
int size2 = 0;
// These are defined in the order on the stack
switch (lr1.getType()) {
case Operand.BYTE:
size1 = 1;
break;
case Operand.SHORT:
size1 = 2;
break;
case Operand.CHAR:
size1 = 3;
break;
case Operand.INT:
size1 = 4;
break;
case Operand.FLOAT:
size1 = 5;
break;
// this could be 32 or 64 bits, in between FLOAT and LONG is best
case Operand.REFERENCE:
size1 = 6;
break;
case Operand.LONG:
size1 = 7;
break;
case Operand.DOUBLE:
size1 = 8;
break;
}
switch (lr2.getType()) {
case Operand.BYTE:
size2 = 1;
break;
case Operand.SHORT:
size2 = 2;
break;
case Operand.CHAR:
size2 = 3;
break;
case Operand.INT:
size2 = 4;
break;
case Operand.FLOAT:
size2 = 5;
break;
case Operand.REFERENCE:
size2 = 6;
break;
case Operand.LONG:
size2 = 7;
break;
case Operand.DOUBLE:
size2 = 8;
break;
}
return size1 - size2;
}
}