1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-06-03 07:29:37 +00:00
kickc/src/main/java/dk/camelot64/kickc/passes/Pass4RegistersFinalize.java
2023-04-06 08:44:31 +02:00

277 lines
12 KiB
Java

package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.symbols.*;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.types.SymbolTypePointer;
import dk.camelot64.kickc.model.types.SymbolTypeStruct;
import dk.camelot64.kickc.model.values.VariableRef;
import java.util.*;
/**
* Move register allocation from equivalence classes to RegisterAllocation.
* Also reallocate and rename zero page registers in the equivalence classes.
*/
public class Pass4RegistersFinalize extends Pass2Base {
/**
* The current zero page used to create new registers when needed.
*/
private int currentZp = 2;
/** All reserved zeropage addresses not available for the compiler. */
private List<Integer> reservedZp;
public Pass4RegistersFinalize(Program program) {
super(program);
this.reservedZp = new ArrayList<>();
// Add all ZP's declared as reserved
this.reservedZp.addAll(program.getReservedZps());
// Add all ZP's declared as reserved in a procedure
for(Procedure procedure : getSymbols().getAllProcedures(true)) {
List<Integer> procedureReservedZps = procedure.getReservedZps();
if(procedureReservedZps!=null) {
this.reservedZp.addAll(procedureReservedZps);
}
}
// Add all ZP's declared hardcoded register for a live variable
for(Variable variable : getSymbols().getAllVariables(true)) {
if(variable.getRegister() instanceof Registers.RegisterZpMem) {
int zp = ((Registers.RegisterZpMem) variable.getRegister()).getZp();
int sizeBytes = variable.getType().getSizeBytes();
for(int i=0;i<sizeBytes; i++) {
if(!reservedZp.contains(zp+i))
this.reservedZp.add(zp+i);
}
}
}
}
public void allocate(boolean reallocateZp, boolean overflowToMainMem) {
LiveRangeEquivalenceClassSet liveRangeEquivalenceClassSet = getProgram().getLiveRangeEquivalenceClassSet();
for(LiveRangeEquivalenceClass equivalenceClass : liveRangeEquivalenceClassSet.getEquivalenceClasses()) {
for(VariableRef variableRef : equivalenceClass.getVariables()) {
Variable variable = getProgram().getScope().getVariable(variableRef);
Registers.Register declaredRegister = variable.getRegister(); //TODO: Handle register/memory/storage strategy differently!
Registers.Register register = declaredRegister;
if(declaredRegister !=null) {
if(declaredRegister instanceof Registers.RegisterZpMem) {
int zp = ((Registers.RegisterZpMem) declaredRegister).getZp();
int bytes = variable.getType().getSizeBytes();
register = new Registers.RegisterZpMem(zp, bytes, true);
} else if(declaredRegister instanceof Registers.RegisterMainMem) {
Long address = ((Registers.RegisterMainMem) declaredRegister).getAddress();
VariableRef varRef = ((Registers.RegisterMainMem) declaredRegister).getVariableRef();
int bytes = variable.getType().getSizeBytes();
register = new Registers.RegisterMainMem(varRef, bytes, address, true);
} else if(equivalenceClass.getRegister()!=null && !declaredRegister.equals(equivalenceClass.getRegister())) {
throw new CompileError("Equivalence class has variables with different declared registers \n" +
" - equivalence class: " + equivalenceClass.toString(true) + "\n" +
" - one register: " + equivalenceClass.getRegister().toString() + "\n" +
" - other register: " + declaredRegister.toString()
);
}
equivalenceClass.setRegister(register);
}
}
}
if(reallocateZp) {
reallocateMemRegisters(liveRangeEquivalenceClassSet, overflowToMainMem);
}
liveRangeEquivalenceClassSet.storeRegisterAllocation();
if(reallocateZp) {
shortenAsmNames();
}
}
/**
* Shorten ASM names for variables and constants
*/
private void shortenAsmNames() {
Collection<Scope> allScopes = getProgram().getScope().getAllScopes(true);
allScopes.add(getProgram().getScope());
for(Scope scope : allScopes) {
// Create initial short names
for(Variable variable : scope.getAllVariables(false)) {
if(variable.getAllocation() != null && variable.getAllocation().isMem()) {
variable.setAsmName(variable.getLocalName());
} else {
variable.setAsmName(null);
}
}
for(Variable constantVar : scope.getAllConstants(false)) {
constantVar.setAsmName(constantVar.getLocalName());
}
// Maps short name to the allocated register.
Map<String, Registers.Register> shortNames = new LinkedHashMap<>();
// Shorten variable and constant names
for(Variable variable : scope.getAllVariables(false)) {
Registers.Register allocation = variable.getAllocation();
if(allocation != null && allocation.isMem()) {
shortenAsmName(shortNames, variable, allocation);
}
}
for(Variable constantVar : scope.getAllConstants(false)) {
Registers.Register allocation = new Registers.RegisterConstant(constantVar.getInitValue());
shortenAsmName(shortNames, constantVar, allocation);
}
}
}
/**
* Shorten the ASM name for a single variable.
*
* @param shortNames Map of allocated short names. Maps short name to the allocated register.
* @param variable The variable to shorten the name for
* @param allocation The register allocation for the variable
*/
private void shortenAsmName(Map<String, Registers.Register> shortNames, Variable variable, Registers.Register allocation) {
String asmName = variable.getAsmName();
String prefix = asmName;
if(asmName.contains("#")) {
prefix = asmName.substring(0, asmName.indexOf("#"));
}
int suffix = 0;
boolean found = false;
while(!found) {
String shortName = prefix + ((suffix==0)?"":("_"+suffix));
if(shortNames.get(shortName) == null || shortNames.get(shortName).equals(allocation)) {
// Short name is usable!
variable.setAsmName(shortName);
shortNames.put(shortName, allocation);
found=true;
}
suffix++;
}
}
/**
* Reallocate all memory registers. Minimizes zeropage usage.
*
* @param equivalenceClassSet The live range equivalence classes
* @param overflowToMainMem If true zeropage variables overflow into main memory if they do not fit on zeropage.
*/
private void reallocateMemRegisters(LiveRangeEquivalenceClassSet equivalenceClassSet, boolean overflowToMainMem) {
List<LiveRangeEquivalenceClass> equivalenceClasses = new ArrayList<>(equivalenceClassSet.getEquivalenceClasses());
final VariableRegisterWeights registerWeights = getProgram().getVariableRegisterWeights();
Collections.sort(equivalenceClasses, (o1, o2) -> Double.compare(registerWeights.getTotalWeight(o2), registerWeights.getTotalWeight(o1)));
for(LiveRangeEquivalenceClass equivalenceClass : equivalenceClasses) {
Registers.Register register = equivalenceClass.getRegister();
boolean reallocate = true;
if(register!=null) {
if(register.isHardware()) {
// Do not allocate hardware registers
reallocate = false;
} else if(register.isAddressHardcoded()) {
// Do not allocate registers with hardcoded address
reallocate = false;
}
}
if(reallocate) {
String before = register == null ? null : register.toString();
VariableRef variableRef = equivalenceClass.getVariables().get(0);
Variable variable = getProgram().getSymbolInfos().getVariable(variableRef);
if(variable.isMemoryAreaMain()) {
register = new Registers.RegisterMainMem(variableRef, variable.getType().getSizeBytes(), null, false);
} else {
register = allocateNewRegisterZp(variable);
int zp = ((Registers.RegisterZpMem) register).getZp();
int sizeBytes = variable.getType().getSizeBytes();
if(overflowToMainMem) {
if (zp + sizeBytes > 0x100) {
// Zero-page exhausted - move to main memory instead (TODO: prioritize!)
register = new Registers.RegisterMainMem(variableRef, variable.getType().getSizeBytes(), null, false);
getLog().append("Zero-page exhausted. Moving allocation to main memory " + variable.toString());
}
}
}
equivalenceClass.setRegister(register);
if(before == null || !before.equals(register.toString())) {
getLog().append("Allocated " + (before == null ? "" : ("(was " + before + ") ")) + equivalenceClass.toString());
}
}
}
}
/**
* Allocate bytes on zeropage.
* Avoids reserved zero page addresses.
* @param size The number of bytes to allocate
* @return The address of the first byte.
*/
private int allocateZp(int size) {
// Find a ZP sequence of size without any reserved ZP
boolean reserved;
do {
reserved = false;
int candidateZp = currentZp;
for(int i=0;i<size;i++) {
if(reservedZp.contains(Integer.valueOf(candidateZp+i))) {
reserved = true;
currentZp++;
break;
}
}
} while(reserved);
// No reserved ZP
int allocated = currentZp;
currentZp += size;
return allocated;
}
/**
* Create a new register for a specific variable type.
*
* @param variable The variable to create a register for.
* The register type created uses one or more zero page locations based on the variable type
* @return The new zeropage register
*/
private Registers.Register allocateNewRegisterZp(Variable variable) {
SymbolType varType = variable.getType();
if(SymbolType.BYTE.equals(varType)) {
return new Registers.RegisterZpMem(allocateZp(1), 1);
} else if(SymbolType.SBYTE.equals(varType)) {
return new Registers.RegisterZpMem(allocateZp(1),1);
} else if(SymbolType.WORD.equals(varType)) {
Registers.RegisterZpMem registerZpWord =
new Registers.RegisterZpMem(allocateZp(2), 2);
return registerZpWord;
} else if(SymbolType.SWORD.equals(varType)) {
Registers.RegisterZpMem registerZpWord =
new Registers.RegisterZpMem(allocateZp(2), 2);
return registerZpWord;
} else if(SymbolType.DWORD.equals(varType)) {
Registers.RegisterZpMem registerZpDWord =
new Registers.RegisterZpMem(allocateZp(4), 4);
return registerZpDWord;
} else if(SymbolType.SDWORD.equals(varType)) {
Registers.RegisterZpMem registerZpDWord =
new Registers.RegisterZpMem(allocateZp(4), 4);
return registerZpDWord;
} else if(varType.equals(SymbolType.BOOLEAN)) {
return new Registers.RegisterZpMem(allocateZp(1), 1);
} else if(varType.equals(SymbolType.VOID)) {
// No need to setRegister register for VOID value
return null;
} else if(varType instanceof SymbolTypePointer) {
Registers.RegisterZpMem registerZpWord =
new Registers.RegisterZpMem(allocateZp(2), 2);
return registerZpWord;
} else if(varType instanceof SymbolTypeStruct) {
Registers.RegisterZpMem registerZpStruct =
new Registers.RegisterZpMem(allocateZp(varType.getSizeBytes()), varType.getSizeBytes());
return registerZpStruct;
} else {
throw new RuntimeException("Unhandled variable type " + varType);
}
}
}