mirror of
https://gitlab.com/camelot/kickc.git
synced 2025-08-13 15:25:40 +00:00
496 lines
21 KiB
Java
496 lines
21 KiB
Java
package dk.camelot64.kickc.fragment;
|
|
|
|
import dk.camelot64.kickc.model.*;
|
|
import dk.camelot64.kickc.model.operators.Operator;
|
|
import dk.camelot64.kickc.model.operators.OperatorUnary;
|
|
import dk.camelot64.kickc.model.operators.Operators;
|
|
import dk.camelot64.kickc.model.statements.Statement;
|
|
import dk.camelot64.kickc.model.statements.StatementAssignment;
|
|
import dk.camelot64.kickc.model.statements.StatementConditionalJump;
|
|
import dk.camelot64.kickc.model.symbols.ConstantVar;
|
|
import dk.camelot64.kickc.model.symbols.Label;
|
|
import dk.camelot64.kickc.model.symbols.Symbol;
|
|
import dk.camelot64.kickc.model.symbols.Variable;
|
|
import dk.camelot64.kickc.model.types.*;
|
|
import dk.camelot64.kickc.model.values.*;
|
|
|
|
import java.lang.InternalError;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.Map;
|
|
|
|
/**
|
|
* A fragment specification generated from a {@link Statement} used to load/synthesize an {@link AsmFragmentInstance} for creating ASM code for the statement
|
|
*/
|
|
public class AsmFragmentInstanceSpecFactory {
|
|
|
|
/**
|
|
* The symbol table.
|
|
*/
|
|
private Program program;
|
|
|
|
/**
|
|
* Binding of named values in the fragment to values (constants, variables, ...) .
|
|
*/
|
|
private Map<String, Value> bindings;
|
|
|
|
/**
|
|
* The created ASM fragment instance specification
|
|
*/
|
|
private AsmFragmentInstanceSpec asmFragmentInstanceSpec;
|
|
|
|
/** Indexing for zeropages/constants/labels */
|
|
private int nextZpIdx = 1;
|
|
private int nextConstIdx = 1;
|
|
private int nextLabelIdx = 1;
|
|
|
|
public Map<String, Value> getBindings() {
|
|
return bindings;
|
|
}
|
|
|
|
public AsmFragmentInstanceSpecFactory(
|
|
StatementConditionalJump conditionalJump,
|
|
ControlFlowBlock block,
|
|
Program program,
|
|
ControlFlowGraph graph) {
|
|
this.program = program;
|
|
this.bindings = new LinkedHashMap<>();
|
|
ScopeRef codeScope = program.getStatementInfos().getBlock(conditionalJump).getScope();
|
|
String signature = conditionalJumpSignature(conditionalJump, block, graph);
|
|
this.asmFragmentInstanceSpec = new AsmFragmentInstanceSpec(program, signature, bindings, codeScope);
|
|
}
|
|
|
|
public AsmFragmentInstanceSpecFactory(StatementAssignment assignment, Program program) {
|
|
this.program = program;
|
|
this.bindings = new LinkedHashMap<>();
|
|
ScopeRef codeScope = program.getStatementInfos().getBlock(assignment).getScope();
|
|
String signature = assignmentSignature(
|
|
assignment.getlValue(),
|
|
assignment.getrValue1(),
|
|
assignment.getOperator(),
|
|
assignment.getrValue2());
|
|
this.asmFragmentInstanceSpec = new AsmFragmentInstanceSpec(program, signature, bindings, codeScope);
|
|
}
|
|
|
|
public AsmFragmentInstanceSpecFactory(LValue lValue, RValue rValue, Program program, ScopeRef codeScopeRef) {
|
|
this.program = program;
|
|
this.bindings = new LinkedHashMap<>();
|
|
String signature = assignmentSignature(lValue, null, null, rValue);
|
|
this.asmFragmentInstanceSpec = new AsmFragmentInstanceSpec(program, signature, bindings, codeScopeRef);
|
|
}
|
|
|
|
public AsmFragmentInstanceSpecFactory(StatementAssignment assignment, StatementAssignment assignmentAlu, Program program) {
|
|
this.program = program;
|
|
this.bindings = new LinkedHashMap<>();
|
|
ScopeRef codeScope = program.getStatementInfos().getBlock(assignment).getScope();
|
|
String signature = assignmentWithAluSignature(assignment, assignmentAlu);
|
|
this.asmFragmentInstanceSpec = new AsmFragmentInstanceSpec(program, signature, bindings, codeScope);
|
|
}
|
|
|
|
/**
|
|
* Get the created ASM fragment instance specification
|
|
*
|
|
* @return The ASM fragment instance specification
|
|
*/
|
|
public AsmFragmentInstanceSpec getAsmFragmentInstanceSpec() {
|
|
return asmFragmentInstanceSpec;
|
|
}
|
|
|
|
private static String getOperatorFragmentName(Operator operator) {
|
|
return operator.getAsmOperator();
|
|
}
|
|
|
|
private String assignmentWithAluSignature(StatementAssignment assignment, StatementAssignment assignmentAlu) {
|
|
if(!(assignment.getrValue2() instanceof VariableRef)) {
|
|
throw new AsmFragmentInstance.AluNotApplicableException("Error! ALU register only allowed as rValue2. " + assignment);
|
|
}
|
|
VariableRef assignmentRValue2 = (VariableRef) assignment.getrValue2();
|
|
Variable assignmentRValue2Var = program.getSymbolInfos().getVariable(assignmentRValue2);
|
|
Registers.Register rVal2Register = assignmentRValue2Var.getAllocation();
|
|
|
|
if(!rVal2Register.getType().equals(Registers.RegisterType.REG_ALU)) {
|
|
throw new AsmFragmentInstance.AluNotApplicableException("Error! ALU register only allowed as rValue2. " + assignment);
|
|
}
|
|
StringBuilder signature = new StringBuilder();
|
|
signature.append(bind(assignment.getlValue()));
|
|
signature.append("=");
|
|
if(assignment.getrValue1() != null) {
|
|
signature.append(bind(assignment.getrValue1()));
|
|
}
|
|
if(assignment.getOperator() != null) {
|
|
signature.append(getOperatorFragmentName(assignment.getOperator()));
|
|
}
|
|
signature.append(assignmentRightSideSignature(
|
|
assignmentAlu.getrValue1(),
|
|
assignmentAlu.getOperator(),
|
|
assignmentAlu.getrValue2()));
|
|
return signature.toString();
|
|
}
|
|
|
|
private String assignmentSignature(LValue lValue, RValue rValue1, Operator operator, RValue rValue2) {
|
|
StringBuilder signature = new StringBuilder();
|
|
signature.append(bind(lValue));
|
|
signature.append("=");
|
|
signature.append(assignmentRightSideSignature(rValue1, operator, rValue2));
|
|
return signature.toString();
|
|
}
|
|
|
|
private String assignmentRightSideSignature(RValue rValue1, Operator operator, RValue rValue2) {
|
|
StringBuilder signature = new StringBuilder();
|
|
if(rValue1 != null) {
|
|
signature.append(bind(rValue1));
|
|
}
|
|
if(operator != null) {
|
|
signature.append(getOperatorFragmentName(operator));
|
|
}
|
|
if(
|
|
rValue2 instanceof ConstantInteger &&
|
|
((ConstantInteger) rValue2).getValue() == 1 &&
|
|
operator != null &&
|
|
(operator.getOperator().equals("-") || operator.getOperator().equals("+"))) {
|
|
signature.append("1");
|
|
} else if(
|
|
rValue2 instanceof ConstantInteger &&
|
|
((ConstantInteger) rValue2).getValue() == 2 &&
|
|
operator != null &&
|
|
(operator.getOperator().equals("-") || operator.getOperator().equals("+"))) {
|
|
signature.append("2");
|
|
} else if(
|
|
rValue2 instanceof ConstantInteger &&
|
|
((ConstantInteger) rValue2).getValue() <= 7 &&
|
|
operator != null &&
|
|
(operator.getOperator().equals(">>") || operator.getOperator().equals("<<"))) {
|
|
signature.append(((ConstantInteger) rValue2).getValue());
|
|
} else if(
|
|
rValue2 instanceof ConstantInteger &&
|
|
((((ConstantInteger) rValue2).getValue()) % 8 == 0) &&
|
|
operator != null &&
|
|
(operator.getOperator().equals(">>") || operator.getOperator().equals("<<"))) {
|
|
signature.append(((ConstantInteger) rValue2).getValue());
|
|
} else if(
|
|
rValue2 instanceof ConstantInteger &&
|
|
((ConstantInteger) rValue2).getValue() == 0 &&
|
|
operator != null &&
|
|
(operator.getOperator().equals("-") || operator.getOperator().equals("+"))) {
|
|
signature.append("0");
|
|
} else {
|
|
signature.append(bind(rValue2));
|
|
}
|
|
return signature.toString();
|
|
}
|
|
|
|
private String conditionalJumpSignature(
|
|
StatementConditionalJump conditionalJump,
|
|
ControlFlowBlock block,
|
|
ControlFlowGraph graph) {
|
|
StringBuilder signature = new StringBuilder();
|
|
if(conditionalJump.getrValue1() != null) {
|
|
signature.append(bind(conditionalJump.getrValue1()));
|
|
}
|
|
if(conditionalJump.getOperator() != null) {
|
|
signature.append(getOperatorFragmentName(conditionalJump.getOperator()));
|
|
}
|
|
if(conditionalJump.getrValue2() instanceof ConstantInteger && ((ConstantInteger) conditionalJump.getrValue2()).getValue() == 0) {
|
|
signature.append("0");
|
|
} else if(conditionalJump.getrValue2() instanceof ConstantBool) {
|
|
ConstantBool boolValue = (ConstantBool) conditionalJump.getrValue2();
|
|
signature.append(boolValue.toString());
|
|
} else {
|
|
signature.append(bind(conditionalJump.getrValue2()));
|
|
}
|
|
signature.append("_then_");
|
|
LabelRef destination = conditionalJump.getDestination();
|
|
ControlFlowBlock destinationBlock = graph.getBlock(destination);
|
|
String destinationLabel;
|
|
if(destinationBlock.hasPhiBlock()) {
|
|
destinationLabel =
|
|
(destinationBlock.getLabel().getLocalName() +
|
|
"_from_" +
|
|
block.getLabel().getLocalName()).replace('@', 'b').replace(':', '_');
|
|
} else {
|
|
destinationLabel = destination.getLocalName();
|
|
}
|
|
Symbol destSymbol = program.getScope().getSymbol(destination);
|
|
signature.append(bind(new Label(destinationLabel, destSymbol.getScope(), false)));
|
|
return signature.toString();
|
|
}
|
|
|
|
/**
|
|
* Add bindings of a value.
|
|
*
|
|
* @param value The value to bind.
|
|
* @return The bound name of the value. If the value has already been bound the existing bound name is returned.
|
|
*/
|
|
public String bind(Value value) {
|
|
return bind(value, null);
|
|
}
|
|
|
|
/**
|
|
* Add bindings of a value.
|
|
*
|
|
* @param value The value to bind.
|
|
* @param castType The type to bind the value as (used for casting). null if not casting, will use the actual type of the value.
|
|
* @return The bound name of the value. If the value has already been bound the existing bound name is returned.
|
|
*/
|
|
public String bind(Value value, SymbolType castType) {
|
|
|
|
if(value instanceof CastValue) {
|
|
CastValue cast = (CastValue) value;
|
|
SymbolType toType = cast.getToType();
|
|
RValue castValue = cast.getValue();
|
|
SymbolType castValueType = SymbolTypeInference.inferType(this.program.getScope(), castValue);
|
|
if(castValueType.getSizeBytes() == toType.getSizeBytes()) {
|
|
if(castType != null) {
|
|
if(castType.getSizeBytes() == toType.getSizeBytes()) {
|
|
return bind(castValue, castType);
|
|
} else {
|
|
OperatorUnary castUnary = Operators.getCastUnary(castType);
|
|
return getOperatorFragmentName(castUnary) + bind(castValue, toType);
|
|
}
|
|
} else {
|
|
return bind(castValue, toType);
|
|
}
|
|
} else {
|
|
// Size of inner value and inner cast type mismatches - require explicit conversion
|
|
if(castType != null) {
|
|
OperatorUnary castUnaryInner = Operators.getCastUnary(toType);
|
|
OperatorUnary castUnaryOuter = Operators.getCastUnary(castType);
|
|
return getOperatorFragmentName(castUnaryOuter) + getOperatorFragmentName(castUnaryInner) + bind(castValue);
|
|
} else {
|
|
OperatorUnary castUnaryInner = Operators.getCastUnary(toType);
|
|
return getOperatorFragmentName(castUnaryInner) + bind(castValue);
|
|
}
|
|
}
|
|
} else if(value instanceof ConstantCastValue) {
|
|
ConstantCastValue castVal = (ConstantCastValue) value;
|
|
ConstantValue val = castVal.getValue();
|
|
if(castType == null) {
|
|
SymbolType toType = castVal.getToType();
|
|
// If value literal not matching cast type then add expression code to transform it into the value space ( eg. value & 0xff )
|
|
|
|
if(toType instanceof SymbolTypeIntegerFixed) {
|
|
SymbolTypeIntegerFixed integerFixed = (SymbolTypeIntegerFixed) toType;
|
|
ConstantLiteral constantLiteral;
|
|
Long integerValue;
|
|
try {
|
|
constantLiteral = val.calculateLiteral(program.getScope());
|
|
if(constantLiteral instanceof ConstantInteger) {
|
|
integerValue = ((ConstantInteger) constantLiteral).getValue();
|
|
} else if(constantLiteral instanceof ConstantPointer) {
|
|
integerValue = ((ConstantPointer) constantLiteral).getValue();
|
|
} else {
|
|
throw new InternalError("Not implemented " + constantLiteral);
|
|
}
|
|
} catch(ConstantNotLiteral e) {
|
|
// Assume it is a word
|
|
integerValue = 0xffffL;
|
|
}
|
|
|
|
if(!integerFixed.contains(integerValue)) {
|
|
if(toType.getSizeBytes() == 1) {
|
|
val = new ConstantBinary(new ConstantInteger(0xffL, SymbolType.BYTE), Operators.BOOL_AND, val);
|
|
} else if(toType.getSizeBytes() == 2) {
|
|
val = new ConstantBinary(new ConstantInteger(0xffffL, SymbolType.WORD), Operators.BOOL_AND, val);
|
|
} else if(toType.getSizeBytes() == 4) {
|
|
val = new ConstantBinary(new ConstantInteger(0xffffffffL, SymbolType.DWORD), Operators.BOOL_AND, val);
|
|
} else {
|
|
throw new InternalError("Not implemented " + toType);
|
|
}
|
|
}
|
|
}
|
|
|
|
return bind(val, toType);
|
|
} else {
|
|
return bind(val, castType);
|
|
}
|
|
} else if(value instanceof PointerDereference) {
|
|
PointerDereference deref = (PointerDereference) value;
|
|
SymbolType ptrType = null;
|
|
if(castType != null) {
|
|
ptrType = new SymbolTypePointer(castType);
|
|
}
|
|
if(value instanceof PointerDereferenceSimple) {
|
|
String bindPointer = bind(deref.getPointer(), ptrType);
|
|
if(bindPointer.contains("deref")) {
|
|
// Special handling of nested derefs - add parenthesis!
|
|
return "_deref_" + "(" + bindPointer + ")";
|
|
} else {
|
|
return "_deref_" + bindPointer;
|
|
}
|
|
} else if(value instanceof PointerDereferenceIndexed) {
|
|
PointerDereferenceIndexed derefIdx = (PointerDereferenceIndexed) value;
|
|
StringBuilder bindValue = new StringBuilder();
|
|
String bindPointer = bind(derefIdx.getPointer(), ptrType);
|
|
if(bindPointer.contains("deref")) {
|
|
// Special handling of nested derefs - add parenthesis!
|
|
bindValue.append("(").append(bindPointer).append(")");
|
|
} else {
|
|
bindValue.append(bindPointer);
|
|
}
|
|
bindValue.append("_derefidx_");
|
|
String bindIndex = bind(derefIdx.getIndex());
|
|
if(bindIndex.contains("deref")) {
|
|
// Special handling of nested derefs - add parenthesis!
|
|
bindValue.append("(").append(bindIndex).append(")");
|
|
} else {
|
|
bindValue.append(bindIndex);
|
|
}
|
|
return bindValue.toString();
|
|
}
|
|
} else if(value instanceof VariableRef) {
|
|
Variable variable = program.getSymbolInfos().getVariable((VariableRef) value);
|
|
if(castType == null) {
|
|
castType = variable.getType();
|
|
}
|
|
Registers.Register register = variable.getAllocation();
|
|
String name = getTypePrefix(castType) + getRegisterName(register);
|
|
bind(name, variable);
|
|
return name;
|
|
} else if(value instanceof ConstantValue) {
|
|
if(castType == null) {
|
|
castType = SymbolTypeInference.inferType(program.getScope(), (ConstantValue) value);
|
|
}
|
|
String name = getTypePrefix(castType) + getConstName(value);
|
|
bind(name, value);
|
|
return name;
|
|
} else if(value instanceof Label) {
|
|
String name = "la" + nextLabelIdx++;
|
|
bind(name, value);
|
|
return name;
|
|
}
|
|
throw new RuntimeException("Binding of value type not supported " + value);
|
|
}
|
|
|
|
/**
|
|
* Add binding for a name/value pair if it is not already bound.
|
|
*
|
|
* @param name The name
|
|
* @param value The value
|
|
*/
|
|
private void bind(String name, Value value) {
|
|
bindings.putIfAbsent(name, value);
|
|
}
|
|
|
|
/**
|
|
* Get the symbol type part of the binding name (eg. vbu/pws/...)
|
|
*
|
|
* @param type The type
|
|
* @return The type name
|
|
*/
|
|
static String getTypePrefix(SymbolType type) {
|
|
if(SymbolType.BYTE.equals(type)) {
|
|
return "vbu";
|
|
} else if(SymbolType.SBYTE.equals(type)) {
|
|
return "vbs";
|
|
} else if(SymbolType.WORD.equals(type)) {
|
|
return "vwu";
|
|
} else if(SymbolType.SWORD.equals(type)) {
|
|
return "vws";
|
|
} else if(SymbolType.DWORD.equals(type)) {
|
|
return "vdu";
|
|
} else if(SymbolType.SDWORD.equals(type)) {
|
|
return "vds";
|
|
} else if(SymbolType.STRING.equals(type)) {
|
|
return "pbu";
|
|
} else if(SymbolType.BOOLEAN.equals(type)) {
|
|
return "vbo";
|
|
} else if(type instanceof SymbolTypeStruct) {
|
|
return "vss";
|
|
} else if(type instanceof SymbolTypePointer) {
|
|
SymbolType elementType = ((SymbolTypePointer) type).getElementType();
|
|
if(SymbolType.BYTE.equals(elementType)) {
|
|
return "pbu";
|
|
} else if(SymbolType.SBYTE.equals(elementType)) {
|
|
return "pbs";
|
|
} else if(SymbolType.WORD.equals(elementType)) {
|
|
return "pwu";
|
|
} else if(SymbolType.SWORD.equals(elementType)) {
|
|
return "pws";
|
|
} else if(SymbolType.DWORD.equals(elementType)) {
|
|
return "pdu";
|
|
} else if(SymbolType.SDWORD.equals(elementType)) {
|
|
return "pds";
|
|
} else if(SymbolType.BOOLEAN.equals(elementType)) {
|
|
return "pbo";
|
|
} else if(SymbolType.VOID.equals(elementType)) {
|
|
return "pvo";
|
|
} else if(elementType instanceof SymbolTypeProcedure) {
|
|
return "ppr";
|
|
} else if(elementType instanceof SymbolTypePointer) {
|
|
return "ppt";
|
|
} else if(elementType instanceof SymbolTypeStruct) {
|
|
return "pss";
|
|
} else {
|
|
throw new RuntimeException("Not implemented " + type);
|
|
}
|
|
} else {
|
|
throw new RuntimeException("Not implemented " + type);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the register part of the binding name (eg. aa, z1, z2, ...).
|
|
* Examines all previous bindings to reuse register index if the same register is bound multiple times.
|
|
*
|
|
* @param register The register
|
|
* @return The register part of the binding name.
|
|
*/
|
|
private String getRegisterName(Registers.Register register) {
|
|
if(
|
|
Registers.RegisterType.ZP_BOOL.equals(register.getType()) ||
|
|
Registers.RegisterType.ZP_BYTE.equals(register.getType()) ||
|
|
Registers.RegisterType.ZP_WORD.equals(register.getType()) ||
|
|
Registers.RegisterType.ZP_DWORD.equals(register.getType()) ||
|
|
Registers.RegisterType.ZP_STRUCT.equals(register.getType())
|
|
) {
|
|
// Examine if the ZP register is already bound
|
|
Registers.RegisterZp registerZp = (Registers.RegisterZp) register;
|
|
String zpNameIdx = null;
|
|
for(String boundName : bindings.keySet()) {
|
|
Value boundValue = bindings.get(boundName);
|
|
if(boundValue instanceof Variable) {
|
|
Registers.Register boundRegister = ((Variable) boundValue).getAllocation();
|
|
if(boundRegister != null && boundRegister.isZp()) {
|
|
Registers.RegisterZp boundRegisterZp = (Registers.RegisterZp) boundRegister;
|
|
if(registerZp.getZp() == boundRegisterZp.getZp()) {
|
|
// Found other register with same ZP address!
|
|
zpNameIdx = boundName.substring(boundName.length() - 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// If not create a new one
|
|
if(zpNameIdx == null) {
|
|
zpNameIdx = Integer.toString(nextZpIdx++);
|
|
}
|
|
return "z" + zpNameIdx;
|
|
} else if(Registers.RegisterType.REG_A_BYTE.equals(register.getType())) {
|
|
return "aa";
|
|
} else if(Registers.RegisterType.REG_X_BYTE.equals(register.getType())) {
|
|
return "xx";
|
|
} else if(Registers.RegisterType.REG_Y_BYTE.equals(register.getType())) {
|
|
return "yy";
|
|
} else if(Registers.RegisterType.REG_ALU.equals(register.getType())) {
|
|
throw new AsmFragmentInstance.AluNotApplicableException();
|
|
} else {
|
|
throw new RuntimeException("Not implemented " + register.getType());
|
|
}
|
|
}
|
|
|
|
private String getConstName(Value constant) {
|
|
// If the constant is already bound - reuse the index
|
|
for(String boundName : bindings.keySet()) {
|
|
Value boundValue = bindings.get(boundName);
|
|
if(boundValue instanceof ConstantValue || boundValue instanceof ConstantVar) {
|
|
if(boundValue.equals(constant)) {
|
|
return "c" + boundName.substring(boundName.length() - 1);
|
|
}
|
|
}
|
|
}
|
|
// Otherwise use a new index
|
|
return "c" + nextConstIdx++;
|
|
}
|
|
|
|
|
|
}
|