kickc/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java

1342 lines
74 KiB
Java

package dk.camelot64.kickc.passes;
import dk.camelot64.cpufamily6502.CpuAddressingMode;
import dk.camelot64.cpufamily6502.CpuClobber;
import dk.camelot64.kickc.asm.*;
import dk.camelot64.kickc.fragment.*;
import dk.camelot64.kickc.fragment.synthesis.AsmFragmentTemplateSynthesizer;
import dk.camelot64.kickc.model.InternalError;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.operators.Operators;
import dk.camelot64.kickc.model.statements.*;
import dk.camelot64.kickc.model.symbols.*;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.types.SymbolTypeIntegerFixed;
import dk.camelot64.kickc.model.types.SymbolTypePointer;
import dk.camelot64.kickc.model.types.SymbolTypeStruct;
import dk.camelot64.kickc.model.values.*;
import dk.camelot64.kickc.passes.calcs.PassNCalcVariableReferenceInfos;
import dk.camelot64.kickc.passes.utils.SizeOfConstants;
import java.io.File;
import java.util.*;
/**
* Code Generation of 6502 Assembler from ICL/SSA Control Flow Graph
*/
public class Pass4CodeGeneration {
/**
* Should the generated ASM contain verbose alive info for the statements (costs a bit more to generate).
*/
boolean verboseAliveInfo;
/**
* Missing fragments produce a warning instead of an error.
*/
boolean warnFragmentMissing;
/**
* The program being generated.
*/
private final Program program;
/**
* Keeps track of the phi transitions into blocks during code generation.
* Used to ensure that duplicate transitions are only code generated once.
* Maps to-blocks to the transition information for the block
*/
private final Map<PhiTransitions.PhiTransition, Boolean> transitionsGenerated = new LinkedHashMap<>();
/**
* Determines if a phi-transition has already been code-generated
*
* @param transition The transition to examine
* @return true if it has already been generated
*/
private boolean transitionIsGenerated(PhiTransitions.PhiTransition transition) {
return Boolean.TRUE.equals(transitionsGenerated.get(transition));
}
/**
* Mark a Phi transition as generated
*
* @param transition The transition
*/
private void transitionSetGenerated(PhiTransitions.PhiTransition transition) {
transitionsGenerated.put(transition, Boolean.TRUE);
}
public Pass4CodeGeneration(Program program, boolean verboseAliveInfo, boolean warnFragmentMissing) {
this.program = program;
this.verboseAliveInfo = verboseAliveInfo;
this.warnFragmentMissing = warnFragmentMissing;
}
Graph getGraph() {
return program.getGraph();
}
ProgramScope getScope() {
return program.getScope();
}
public boolean generateProcedure(Procedure procedure) {
if(!procedure.isDeclaredExtern()) {
/* In search for further source code library or object file filtering.
String primaryCFile = program.getOutputFileManager().getPrimaryCFile().getFileName().toString();
if(procedure.getDefinitionSource() != null) {
if(procedure.getDefinitionSource().getFileName().equals(primaryCFile) || procedure.isAsmExportLibrary()) {
return true;
}
} else {
return true;
}
*/
return true;
}
return false;
}
public void generate() {
AsmProgram asm = new AsmProgram(program.getTargetCpu());
String asmLibraryName = program.getAsmLibraryName();
ScopeRef currentScope = ScopeRef.ROOT;
asm.startChunk(currentScope, null, null);
// 820/25 - #import .asm libraries only once!
asm.addImportOnce();
// Add file level comments
asm.startChunk(currentScope, null, "File Comments");
generateComments(asm, program.getMainFileComments());
// #820/24 - Create a namespace if the output of the compile is an .asm library instead of a program.
if (program.getAsmLibraryName() != null) {
asm.startChunk(currentScope, null, "Library");
asm.addNamespaceBegin(program);
}
asm.startChunk(currentScope, null, "Upstart");
final TargetPlatform targetPlatform = program.getTargetPlatform();
// Add Target CPU - if it is not the default
final TargetCpu targetCpu = program.getTargetCpu();
if (!targetCpu.equals(TargetCpu.DEFAULT)) asm.addLine(new AsmSetCpu(targetCpu));
File linkScriptFile = targetPlatform.getLinkScriptFile();
String linkScriptBody = "";
if((asmLibraryName == null) || ((asmLibraryName != null) && (!targetPlatform.isDefaultLinkScriptFile()))) {
linkScriptBody += targetPlatform.getLinkScriptBody();
String outputFileName = program.getOutputFileManager().getBinaryOutputFile().getFileName().toString();
linkScriptBody = linkScriptBody.replace("%O", outputFileName);
linkScriptBody = linkScriptBody.replace("%_O", outputFileName.toLowerCase());
linkScriptBody = linkScriptBody.replace("%^O", outputFileName.toUpperCase());
String entryName = program.getStartProcedure().getFullName();
linkScriptBody = linkScriptBody.replace("%E", entryName);
Number startAddress = program.getTargetPlatform().getStartAddress();
if (startAddress != null)
linkScriptBody = linkScriptBody.replace("%P", AsmFormat.getAsmNumber(startAddress));
} else {
// #820/31 - Generate Code and Data segment stub for .asm libraries,
// to have a Code and Data segment defined when exporting, but not when importing.
// This standard import will only get executed when there isn't a link script.
linkScriptBody += "\n" +
" .segmentdef Code\n" +
" .segmentdef Data\n";
}
if(asmLibraryName != null) {
asm.addIgnoredImportInlinedKickAsm(asmLibraryName, linkScriptBody, 0L, 0L, CpuClobber.CLOBBER_ALL);
} else {
asm.addInlinedKickAsm(linkScriptBody, 0L, 0L, CpuClobber.CLOBBER_ALL);
}
// Generate global ZP labels
asm.startChunk(currentScope, null, "Global Constants & labels");
addConstantsAndLabels(asm, currentScope, null);
Procedure oldProcedure = null;
for (Graph.Block block : getGraph().getAllBlocks()) {
/** #820 - Reworked the code generation, avoiding code to be generated
* for procedures which are external. These must be included through linkage.
* Also, code that is not part of the main library, is not generated.
*/
Procedure procedure = program.getProcedure(block);
if (!block.getScope().equals(currentScope)) {
if (oldProcedure != null && generateProcedure(oldProcedure)) {
// The current block is in a different scope. End the old scope.
generateScopeEnding(asm, currentScope);
}
currentScope = block.getScope();
oldProcedure = procedure;
if(generateProcedure(procedure)) {
if (program.isProcedureEntry(block)) {
currentCodeSegmentName = procedure.getSegmentCode();
}
// Set segment and procedure label.
setCurrentSegment(currentCodeSegmentName, asm);
asm.startChunk(currentScope, null, block.getLabel().getFullName());
// Add any procedure comments
if (program.isProcedureEntry(block)) {
generateComments(asm, procedure.getComments());
// Generate parameter information
generateSignatureComments(asm, procedure);
}
// Start the new scope
asm.addScopeBegin(AsmFormat.asmFix(block.getLabel().getFullName()));
// Add all ZP labels for the scope
addConstantsAndLabels(asm, currentScope, null);
}
}
if (generateProcedure(procedure)) {
generateComments(asm, block.getComments());
// Generate entry points (if needed)
genBlockEntryPoints(asm, block);
if (program.isProcedureEntry(block)) {
// Generate interrupt entry if needed
if (procedure != null && procedure.getInterruptType() != null) {
generateInterruptEntry(asm, procedure);
}
} else {
// Generate label for block inside procedure
asm.startChunk(currentScope, null, block.getLabel().getFullName());
asm.addLabel(AsmFormat.asmFix(block.getLabel().getLocalName()));
}
// Generate statements
genStatements(asm, block);
// Generate exit
Graph.Block defaultSuccessor = getGraph().getDefaultSuccessor(block);
if (defaultSuccessor != null) {
if (defaultSuccessor.hasPhiBlock()) {
PhiTransitions.PhiTransition transition = getTransitions(defaultSuccessor).getTransition(block);
if (!transitionIsGenerated(transition)) {
genBlockPhiTransition(asm, block, defaultSuccessor, defaultSuccessor.getScope());
String label = AsmFormat.asmFix(defaultSuccessor.getLabel().getLocalName());
asm.addInstruction("JMP", CpuAddressingMode.ABS, label, false);
} else {
String label = AsmFormat.asmFix(defaultSuccessor.getLabel().getLocalName() + "_from_" + block.getLabel().getLocalName());
asm.addInstruction("JMP", CpuAddressingMode.ABS, label, false);
}
} else {
String label = AsmFormat.asmFix(defaultSuccessor.getLabel().getLocalName());
asm.addInstruction("JMP", CpuAddressingMode.ABS, label, false);
}
}
}
}
// 820/24 - Scope ending of the normal assembler and/or asm library export generation.
if (oldProcedure != null && generateProcedure(oldProcedure)) {
// The current block is in a different scope. End the old scope.
generateScopeEnding(asm, currentScope);
}
if (program.getAsmLibraryName() == null) {
asm.startChunk(ScopeRef.ROOT, null, "File Data Internal or Ignore");
addData(asm, ScopeRef.ROOT, null);
addAbsoluteAddressData(asm, ScopeRef.ROOT, null);
} else {
// #820/34 - Export the global data, but when the compilation is an .asm export library.
asm.startChunk(ScopeRef.ROOT, null, "Exported Global Data");
// Add the global data elements for the ROOT scope of the .asm library.
addData(asm, ScopeRef.ROOT, program.getAsmLibraryName());
addAbsoluteAddressData(asm, ScopeRef.ROOT, program.getAsmLibraryName());
// #820/24 - Close the namespace of the asm library.
asm.addNamespaceEnd(program);
}
// #820/25 - .asm library #import stub code.
generateImportAsmLibraries(asm, program);
program.setAsm(asm);
}
// Name of the current data segment
String currentCodeSegmentName = Scope.SEGMENT_CODE_DEFAULT;
// Name of the current code segment
final String currentDataSegmentName = Scope.SEGMENT_DATA_DEFAULT;
// Name of the current active segment
String currentSegmentName = "";
/**
* Set the current ASM segment - if needed
*
* @param segmentName The segment name we want
* @param asm The ASM program (where a .segment line is added if needed)
*/
private void setCurrentSegment(String segmentName, AsmProgram asm) {
if (!currentSegmentName.equals(segmentName)) {
asm.addLine(new AsmSegment(segmentName));
currentSegmentName = segmentName;
}
}
/**
* Counter used to separate indirect calls
*/
private int indirectCallCount = 1;
/**
* Generate the end of a scope
*
* @param asm The assembler program being generated
* @param currentScope The current scope, which is ending here
*/
private void generateScopeEnding(AsmProgram asm, ScopeRef currentScope) {
if (!ScopeRef.ROOT.equals(currentScope)) {
if (asm.hasStash()) asm.startChunk(currentScope, null, "Outside Flow");
// Generate all stashed ASM lines
asm.addStash();
addData(asm, currentScope, program.getAsmLibraryName());
asm.addScopeEnd();
}
}
/**
* Generate a comment that describes the procedure signature and parameter transfer.
*
* @param asm The assembler program being generated
* @param procedure The procedure
*/
private void generateSignatureComments(AsmProgram asm, Procedure procedure) {
StringBuilder signature = new StringBuilder();
signature.append(" ");
generateSignatureVar(procedure.getLocalVar("return"), procedure, signature);
signature.append(procedure.getReturnType().toCDecl());
signature.append(" ").append(procedure.getLocalName()).append("(");
int i = 0;
for (Variable parameter : procedure.getParameters()) {
if (i++ > 0) signature.append(", ");
Variable param = generateSignatureVar(parameter, procedure, signature);
signature.append(param.getType().toCDecl(parameter.getLocalName()));
}
signature.append(")");
// Always add the signature comments...
asm.addComment(signature.toString(), false);
if(!procedure.getBank().isCommon()) {
asm.addComment(" " + procedure.getBank(), false);
}
}
/**
* Generate part of a comment that describes a returnvalue/parameter
*
* @param param The variable to describe
* @param scope The scope (procedure)
* @param signature The signature to append to
* @return The version of the variable chosen
*/
Variable generateSignatureVar(Variable param, Scope scope, StringBuilder signature) {
if (param == null) return param;
if (param.isKindPhiMaster()) {
List<Variable> versions = new ArrayList<>(scope.getVersions(param));
if (versions.size() > 0) if (param.getLocalName().equals("return")) {
// Choose the last version for return values
param = versions.get(versions.size() - 1);
} else {
// Choose the first version for parameters
param = versions.get(0);
}
else
// Parameter optimized away to a constant or unused
return param;
}
Registers.Register allocation = param.getAllocation();
if (allocation instanceof Registers.RegisterZpMem) {
Registers.RegisterZpMem registerZp = (Registers.RegisterZpMem) allocation;
signature.append("__zp(").append(AsmFormat.getAsmNumber(registerZp.getZp())).append(") ");
} else if (allocation instanceof Registers.RegisterMainMem) {
Registers.RegisterMainMem registerMainMem = (Registers.RegisterMainMem) allocation;
signature.append("__mem(").append(registerMainMem.getAddress() == null ? "" : AsmFormat.getAsmNumber(registerMainMem.getAddress())).append(") ");
} else if (allocation instanceof Registers.RegisterAByte) {
signature.append("__register(A) ");
} else if (allocation instanceof Registers.RegisterXByte) {
signature.append("__register(X) ");
} else if (allocation instanceof Registers.RegisterYByte) {
signature.append("__register(Y) ");
} else if (allocation instanceof Registers.RegisterZByte) {
signature.append("__register(Z) ");
}
return param;
}
/**
* Add comments to the assembler program
*
* @param asm The assembler program
* @param comments The comments to add
*/
private void generateComments(AsmProgram asm, List<Comment> comments) {
for (Comment comment : comments) {
asm.addComment(comment.getComment(), comment.isBlock());
}
}
/**
* 820/25 - Generate the #import stub kickasm pre-processor code,
* to #import any .asm library used in the main program.
* Inside the .asm library code, the #define will be #undef-ined to ensure
* the .asm library is #import-ed only once.
*
* @param asm The assembler source code being generated.
* @param program The main program.
*/
private void generateImportAsmLibraries(AsmProgram asm, Program program) {
for (AsmImportLibrary asmImportLibrary : program.getAsmImportLibraries().values()) {
asm.addAsmImportLibrary(asmImportLibrary);
}
}
private boolean hasExportAsmLibrary(Variable constantVar, String exportAsmLibrary) {
String varExportAsmLibrary = constantVar.getExportAsmLibrary();
if(exportAsmLibrary != null) {
if(varExportAsmLibrary != null) {
return varExportAsmLibrary.equals(exportAsmLibrary);
} else {
return true; // Bugfix
}
} else {
boolean isImportAsmLibraryGlobal = constantVar.isAsmImportLibraryGlobal();
boolean isExportAsmLibraryParameter = constantVar.isAsmExportLibraryParameter();
boolean isExportAsmLibraryReturn = constantVar.isAsmExportLibraryReturn();
return ( varExportAsmLibrary == null || isExportAsmLibraryParameter || isExportAsmLibraryReturn )
&& !isImportAsmLibraryGlobal;
}
}
private boolean hasData(Variable constantVar) {
ConstantValue constantValue = constantVar.getInitValue();
if (constantValue instanceof ConstantArray) return true;
else if (constantValue instanceof ConstantStructValue) return true;
else if (constantValue instanceof ConstantString) return true;
else return false;
}
/**
* Determines whether to use a .label instead of .const for a constant.
* This can be necessary because KickAssembler does not allow constant references between scopes.
* If a constant in one scope is referenced from another scope a .label is generated in stead - to allow the cross-scope reference.
*
* @param scopeRef The current scope
* @param constantVar The constant to examine
* @return true if a .label should be used in the generated ASM
*/
private boolean useLabelForConst(ScopeRef scopeRef, Variable constantVar) {
boolean useLabel = false;
Collection<Integer> constRefStatements = program.getVariableReferenceInfos().getConstRefStatements(constantVar.getConstantRef());
if (constRefStatements != null) {
for (Integer constRefStmtIdx : constRefStatements) {
Statement statement = program.getStatementInfos().getStatement(constRefStmtIdx);
ScopeRef refScope = program.getStatementInfos().getBlock(constRefStmtIdx).getScope();
if (statement instanceof StatementPhiBlock) {
// Const reference in PHI block - examine if the only predecessor is current scope
boolean found = false;
for (StatementPhiBlock.PhiVariable phiVariable : ((StatementPhiBlock) statement).getPhiVariables()) {
for (StatementPhiBlock.PhiRValue phiRValue : phiVariable.getValues()) {
RValue phiRRValue = phiRValue.getrValue();
Collection<ConstantRef> phiRValueConstRefs = PassNCalcVariableReferenceInfos.getReferencedConsts(phiRRValue);
for (ConstantRef phiRValueConstRef : phiRValueConstRefs) {
if (phiRValueConstRef.equals(constantVar.getRef())) {
found = true;
// Found the constant
LabelRef pred = phiRValue.getPredecessor();
Graph.Block predBlock = program.getGraph().getBlock(pred);
ScopeRef predScope = predBlock.getScope();
if (!predScope.equals(scopeRef)) {
// Scopes in PHI RValue differs from const scope - generate label
useLabel = true;
}
}
}
}
}
if (!found) {
// PHI-reference is complex - generate label
program.getLog().append("Warning: Complex PHI-value using constant. Using .label as fallback. " + statement);
useLabel = true;
}
} else if (!refScope.equals(scopeRef)) {
// Used in a non-PHI statement in another scope - generate label
useLabel = true;
}
}
}
Collection<SymbolVariableRef> symbolRefConsts = program.getVariableReferenceInfos().getConstRefSymbols(constantVar.getConstantRef());
if (symbolRefConsts != null) {
for (SymbolVariableRef symbolRefConst : symbolRefConsts) {
Variable symbolRefVar = (Variable) program.getScope().getSymbol(symbolRefConst);
if (!symbolRefVar.getScope().getRef().equals(scopeRef)) {
// Used in constant in another scope - generate label
useLabel = true;
break;
}
}
}
return useLabel;
}
/**
* Add global exported struct size constant declarations for structure variables that are exported and thus global.
* Added for all structure variables in a library at the end code of the ROOT scope.
*
* This is a dirty copy but I feel that the code to handle and create the size structure type symbols
* was also a bit "dirty" and this whole size thing is candidate for a some serious code rework overall ...
*
* @param asm The ASM program
* @param scopeRef The scope
*/
private void addGlobalStructSizeConstant(AsmProgram asm, ScopeRef scopeRef, Variable var) {
Scope scope = program.getScope().getScope(scopeRef);
Set<String> added = new LinkedHashSet<>();
Collection<Variable> scopeConstants = scope.getAllConstants(false);
String asmExportLibraryName = program.getAsmLibraryName();
if(var.isStruct()) {
// String typeVar = "SIZEOF_" + var.getType().getConstantFriendlyName();
// // First add all constants without data that can become constants in KickAsm
// for (Variable constantVar : scopeConstants) {
// if (!hasData(constantVar)) {
// String asmName = constantVar.getAsmName() == null ? constantVar.getLocalName() : constantVar.getAsmName();
// if (asmName != null && !added.contains(asmName)) {
// if (constantVar.getLocalName().equals(typeVar)) {
// // Use constant otherwise
// added.add(asmName);
// // Find the constant value calculation
// String asmConstant = AsmFormat.getAsmConstant(program, constantVar.getInitValue(), 99, scopeRef);
// addConstant(asmName, constantVar, asmConstant, asm);
// }
// }
// }
// }
}
}
/**
* Add constant declarations for constants without data and labels for memory variables without data.
* Added before the the code of the scope.
*
* @param asm The ASM program
* @param scopeRef The scope
*/
private void addConstantsAndLabels(AsmProgram asm, ScopeRef scopeRef, String exportAsmLibrary) {
Scope scope = program.getScope().getScope(scopeRef);
Set<String> added = new LinkedHashSet<>();
Collection<Variable> scopeConstants = scope.getAllConstants(false);
// First add all constants without data that can become constants in KickAsm
for (Variable constantVar : scopeConstants) {
if (!hasData(constantVar) && hasExportAsmLibrary(constantVar, exportAsmLibrary)) {
String asmName = constantVar.getAsmName() == null ? constantVar.getLocalName() : constantVar.getAsmName();
if (asmName != null && !added.contains(asmName)) {
if (isIntTypeInScope(constantVar)) {
// Use label for integers/bools referenced in other scope - to allow cross-scope referencing
if (!useLabelForConst(scopeRef, constantVar)) {
// Use constant for constant integers not referenced outside scope
added.add(asmName);
program.getAsmLibraryNamespaceSymbols().put(asmName, constantVar);
// Find the constant value calculation
String asmConstant = AsmFormat.getAsmConstant(program, constantVar.getInitValue(), 99, scopeRef);
addConstant(asmName, constantVar, asmConstant, asm);
}
} else if (!(constantVar.getType() instanceof SymbolTypePointer)) {
// Use constant otherwise
added.add(asmName);
program.getAsmLibraryNamespaceSymbols().put(asmName, constantVar);
// Find the constant value calculation
String asmConstant = AsmFormat.getAsmConstant(program, constantVar.getInitValue(), 99, scopeRef);
// constantVar.setAsmExportLibrary(exportAsmLibrary);
addConstantLabelDecl(asmName, constantVar, asmConstant, asm);
// addConstant(asmName, constantVar, asmConstant, asm);
}
}
}
}
// Add constants without data that must be labels in KickAsm
for (Variable constantVar : scopeConstants) {
if (!hasData(constantVar) && hasExportAsmLibrary(constantVar, exportAsmLibrary)) {
String asmName = constantVar.getAsmName() == null ? constantVar.getLocalName() : constantVar.getAsmName();
if (asmName != null && !added.contains(asmName)) {
if (constantVar.getType() instanceof SymbolTypePointer) {
// Must use a label for pointers
added.add(asmName);
program.getAsmLibraryNamespaceSymbols().put(asmName, constantVar);
String asmConstant = AsmFormat.getAsmConstant(program, constantVar.getInitValue(), 99, scopeRef);
addConstantLabelDecl(asmName, constantVar, asmConstant, asm);
} else if (isIntTypeInScope(constantVar)) {
// Use label for integers referenced in other scope - to allow cross-scope referencing
if (useLabelForConst(scopeRef, constantVar)) {
// Use label for integers referenced in other scope - to allow cross-scope referencing
added.add(asmName);
program.getAsmLibraryNamespaceSymbols().put(asmName, constantVar);
// Add any comments
String asmConstant = AsmFormat.getAsmConstant(program, constantVar.getInitValue(), 99, scopeRef);
addConstantLabelDecl(asmName, constantVar, asmConstant, asm);
}
}
}
}
}
// Add labels for memory variables without data
Collection<Variable> scopeVars = scope.getAllVariables(false);
for (Variable scopeVar : scopeVars) {
Registers.Register register = scopeVar.getAllocation();
if (register != null) {
if (Registers.RegisterType.ZP_MEM.equals(register.getType())) {
Registers.RegisterZpMem registerZp = (Registers.RegisterZpMem) register;
String asmName = scopeVar.getAsmName();
if (asmName != null && !added.contains(asmName)) {
added.add(asmName);
program.getAsmLibraryNamespaceSymbols().put(asmName, scopeVar);
addConstantLabelDecl(asmName, scopeVar, AsmFormat.getAsmNumber(registerZp.getZp()), asm);
}
} else if (Registers.RegisterType.MAIN_MEM.equals(register.getType()) && ((Registers.RegisterMainMem) register).getAddress() != null) {
String asmName = scopeVar.getAsmName();
if (asmName != null && !added.contains(asmName)) {
added.add(asmName);
program.getAsmLibraryNamespaceSymbols().put(asmName, scopeVar);
// Add the label declaration
Long address = ((Registers.RegisterMainMem) register).getAddress();
addConstantLabelDecl(asmName, scopeVar, AsmFormat.getAsmNumber(address), asm);
}
}
}
}
}
private boolean isIntTypeInScope(Variable constantVar) {
return (SymbolType.isInteger(constantVar.getType()) || SymbolType.BOOLEAN.equals(constantVar.getType())) && constantVar.getRef().getScopeDepth() > 0;
}
private void addConstant(String asmName, Variable constantVar, String asmConstant, AsmProgram asm) {
// Add any comments
generateComments(asm, constantVar.getComments());
// Ensure encoding is good
AsmEncodingHelper.ensureEncoding(asm, constantVar.getInitValue());
asm.addConstant(AsmFormat.asmFix(asmName), asmConstant);
}
private void addConstantLabelDecl(String asmName, Variable variable, String asmConstant, AsmProgram asm) {
// Add any comments
generateComments(asm, variable.getComments());
// Ensure encoding is good
AsmEncodingHelper.ensureEncoding(asm, variable.getInitValue());
// Find the constant value calculation
asm.addLabelDecl(AsmFormat.asmFix2(asmName, variable.getScope().getLocalName()), asmConstant);
}
/**
* Add all constants with data that must be placed at an absolute address
* Added at the end of the file
*
* @param asm The ASM program
* @param scopeRef The scope
*/
private void addAbsoluteAddressData(AsmProgram asm, ScopeRef scopeRef, String exportAsmLibrary) {
Scope scope = program.getScope().getScope(scopeRef);
Collection<Variable> scopeConstants = scope.getAllConstants(false);
Set<String> added = new LinkedHashSet<>();
// Add all constants arrays incl. strings with data
for (Variable constantVar : scopeConstants) {
if (hasData(constantVar) && hasExportAsmLibrary(constantVar, exportAsmLibrary)) {
// Skip if already added
String asmName = constantVar.getAsmName() == null ? constantVar.getLocalName() : constantVar.getAsmName();
if (added.contains(asmName)) {
continue;
}
// Skip if address is not absolute
if (constantVar.getMemoryAddress() == null) continue;
// Set segment
setCurrentSegment(constantVar.getDataSegment(), asm);
// Set absolute address
asm.addLine(new AsmSetPc(asmName, AsmFormat.getAsmConstant(program, constantVar.getMemoryAddress(), 99, scopeRef)));
// Add any comments
generateComments(asm, constantVar.getComments());
// Add any alignment
Integer declaredAlignment = constantVar.getMemoryAlignment();
if (declaredAlignment != null) {
String alignment = AsmFormat.getAsmNumber(declaredAlignment);
asm.addDataAlignment(alignment);
}
ConstantValue constantValue = constantVar.getInitValue();
if (constantValue instanceof ConstantArray || constantValue instanceof ConstantString || constantValue instanceof ConstantStructValue) {
AsmDataChunk asmDataChunk = new AsmDataChunk();
addChunkData(asmDataChunk, constantValue, constantVar.getType(), constantVar.getArraySpec(), scopeRef);
asmDataChunk.addToAsm(AsmFormat.asmFix(asmName), asm);
} else {
throw new InternalError("Constant Variable not handled " + constantVar.toString(program));
}
added.add(asmName);
}
}
}
/**
* Add constants with data and memory variables with data for a scope.
* Added after the the code of the scope.
*
* @param asm The ASM program
* @param scopeRef The scope
*/
private void addData(AsmProgram asm, ScopeRef scopeRef, String exportAsmLibrary) {
Scope scope = program.getScope().getScope(scopeRef);
Collection<Variable> scopeConstants = scope.getAllConstants(false);
Set<String> added = new LinkedHashSet<>();
// Add all constants arrays incl. strings with data
for (Variable constantVar : scopeConstants) {
if (hasData(constantVar) && hasExportAsmLibrary(constantVar, exportAsmLibrary)) {
// Skip if already added
String asmName = constantVar.getAsmName() == null ? constantVar.getLocalName() : constantVar.getAsmName();
if (added.contains(asmName)) {
continue;
}
// Skip if address is absolute
if (constantVar.getMemoryAddress() != null) continue;
// Set segment
// We use a dummy segment if an .asm library for data to be ignored during import.
String dataSegment = constantVar.getDataSegment();
// if(exportAsmLibrary == null && scopeRef == ScopeRef.ROOT) {
// dataSegment = "Default";
// }
setCurrentSegment(dataSegment, asm);
// Add any comments
generateComments(asm, constantVar.getComments());
// Add any alignment
Integer declaredAlignment = constantVar.getMemoryAlignment();
if (declaredAlignment != null) {
String alignment = AsmFormat.getAsmNumber(declaredAlignment);
asm.addDataAlignment(alignment);
}
ConstantValue constantValue = constantVar.getInitValue();
if (constantValue instanceof ConstantArray || constantValue instanceof ConstantString || constantValue instanceof ConstantStructValue) {
AsmDataChunk asmDataChunk = new AsmDataChunk();
addChunkData(asmDataChunk, constantValue, constantVar.getType(), constantVar.getArraySpec(), scopeRef);
asmDataChunk.addToAsm(AsmFormat.asmFix2(asmName, constantVar.getScope().getLocalName()), asm);
} else {
throw new InternalError("Constant Variable not handled " + constantVar.toString(program));
}
added.add(asmName);
}
}
// Add all memory variables
Collection<Variable> scopeVariables = scope.getAllVariables(false);
for (Variable variable : scopeVariables) {
if(hasExportAsmLibrary(variable, exportAsmLibrary)) {
Registers.Register allocation = variable.getAllocation();
if (variable.getAllocation() instanceof Registers.RegisterMainMem) {
Registers.RegisterMainMem registerMainMem = (Registers.RegisterMainMem) allocation;
// Skip PHI masters
if (variable.isKindPhiMaster()) continue;
// Skip if already added
if (added.contains(variable.getAsmName())) continue;
if (variable.isKindLoadStore() || variable.isKindPhiVersion() || variable.isKindIntermediate()) {
final Variable mainVar = program.getScope().getVariable(registerMainMem.getVariableRef());
if (registerMainMem.getAddress() == null) {
// Generate into the data segment
// Set segment
setCurrentSegment(variable.getDataSegment(), asm);
// Add any comments
generateComments(asm, variable.getComments());
final String mainAsmName = AsmFormat.getAsmConstant(program, new ConstantSymbolPointer(mainVar.getRef()), 99, scopeRef);
final String asmSymbolName = AsmFormat.getAsmSymbolName(program, variable, scopeRef);
if (!mainAsmName.equals(asmSymbolName)) {
asm.addLabelDecl(asmSymbolName, mainAsmName);
} else {
// Add any alignment
Integer declaredAlignment = variable.getMemoryAlignment();
if (declaredAlignment != null) {
String alignment = AsmFormat.getAsmNumber(declaredAlignment);
asm.addDataAlignment(alignment);
}
if (variable.getInitValue() != null) {
// Variable has a constant init Value
if(exportAsmLibrary != null)
addGlobalStructSizeConstant(asm, scopeRef, variable);
ConstantValue constantValue = variable.getInitValue();
AsmDataChunk asmDataChunk = new AsmDataChunk();
addChunkData(asmDataChunk, constantValue, variable.getType(), variable.getArraySpec(), scopeRef);
asmDataChunk.addToAsm(AsmFormat.asmFix2(variable.getAsmName(), variable.getScope().getLocalName()), asm);
} else {
// Zero-fill variable
AsmDataChunk asmDataChunk = new AsmDataChunk();
ConstantValue zeroValue = Initializers.createZeroValue(new Initializers.ValueTypeSpec(variable.getType()), null);
addChunkData(asmDataChunk, zeroValue, variable.getType(), variable.getArraySpec(), scopeRef);
asmDataChunk.addToAsm(AsmFormat.asmFix2(variable.getAsmName(), variable.getScope().getLocalName()), asm);
}
}
added.add(variable.getAsmName());
}
} else {
throw new InternalError("Not handled variable storage " + variable.toString());
}
}
}
}
}
/**
* Get the declared size of an array type as an integer
*
* @param declaredSize The declared size
* @return The integer size. Null if it can't be determined
*/
private Integer getArrayDeclaredSize(ConstantValue declaredSize) {
if (declaredSize != null) {
ConstantLiteral declaredSizeVal = declaredSize.calculateLiteral(getScope());
if (!(declaredSizeVal instanceof ConstantInteger)) {
throw new CompileError("Error! Array declared size is not integer " + declaredSize.toString());
}
return ((ConstantInteger) declaredSizeVal).getInteger().intValue();
}
return null;
}
/**
* Fill the data of a constant value into a data chunk
*
* @param dataChunk The data chunk
* @param value The constant value
* @param valueType The declared type of the value
* @param valueArraySpec The array properties of the value
* @param scopeRef The scope containing the data chunk
*/
private void addChunkData(AsmDataChunk dataChunk, ConstantValue value, SymbolType valueType, ArraySpec valueArraySpec, ScopeRef scopeRef) {
if (valueType instanceof SymbolTypeStruct) {
if (value instanceof ConstantStructValue) {
// Add each struct member recursively
ConstantStructValue structValue = (ConstantStructValue) value;
int size = 0;
for (SymbolVariableRef memberRef : structValue.getMembers()) {
ConstantValue memberValue = structValue.getValue(memberRef);
Variable memberVariable = getScope().getVar(memberRef);
addChunkData(dataChunk, memberValue, memberVariable.getType(), memberVariable.getArraySpec(), scopeRef);
size += SymbolTypeStruct.getMemberSizeBytes(memberVariable.getType(), memberVariable.getArraySize(), getScope());
}
// Add padding if this is a union and the first member does not use all bytes
final int declaredSize = structValue.getStructType().getSizeBytes();
if (size < declaredSize) {
long paddingSize = declaredSize - size;
// TODO: Use SIZEOF constant
ConstantValue paddingSizeVal = new ConstantInteger(paddingSize);
String paddingBytesAsm = AsmFormat.getAsmConstant(program, paddingSizeVal, 99, scopeRef);
ConstantValue zeroValue = new ConstantInteger(0l, SymbolType.BYTE);
dataChunk.addDataZeroFilled(AsmDataNumeric.Type.BYTE, paddingBytesAsm, (int) paddingSize, AsmEncodingHelper.getEncoding(zeroValue));
}
} else if (value instanceof StructZero) {
final SymbolTypeStruct typeStruct = ((StructZero) value).getTypeStruct();
final ConstantRef structSize = SizeOfConstants.getSizeOfConstantVar(getScope(), typeStruct);
String totalSizeBytesAsm = AsmFormat.getAsmConstant(program, structSize, 99, scopeRef);
int totalSizeBytes = typeStruct.getSizeBytes();
dataChunk.addDataZeroFilled(AsmDataNumeric.Type.BYTE, totalSizeBytesAsm, totalSizeBytes, null);
}
} else if (valueType instanceof SymbolTypePointer && valueArraySpec != null) {
SymbolTypePointer constTypeArray = (SymbolTypePointer) valueType;
SymbolType elementType = constTypeArray.getElementType();
SymbolType dataType = value.getType(program.getScope());
int dataNumElements = 0;
if (value instanceof ConstantArrayFilled) {
ConstantArrayFilled constantArrayFilled = (ConstantArrayFilled) value;
ConstantValue arraySize = constantArrayFilled.getSize();
ConstantLiteral arraySizeConst = arraySize.calculateLiteral(getScope());
if (!(arraySizeConst instanceof ConstantInteger)) {
throw new Pass2SsaAssertion.AssertionFailed("Error! Array size is not constant integer " + arraySize.toString(program));
}
dataNumElements = ((ConstantInteger) arraySizeConst).getInteger().intValue();
int elementSizeBytes = elementType.getSizeBytes();
String totalSizeBytesAsm;
if (elementSizeBytes > 1) {
// TODO: Use a SIZEOF constant for the element size ASM
totalSizeBytesAsm = AsmFormat.getAsmConstant(program, new ConstantBinary(new ConstantInteger((long) elementSizeBytes, SymbolType.NUMBER), Operators.MULTIPLY, arraySize), 99, scopeRef);
} else {
totalSizeBytesAsm = AsmFormat.getAsmConstant(program, arraySize, 99, scopeRef);
}
if (elementType instanceof SymbolTypeIntegerFixed || elementType instanceof SymbolTypePointer) {
// Use an ASM type in the fill that matches the element type
dataChunk.addDataZeroFilled(getNumericType(elementType), totalSizeBytesAsm, dataNumElements, null);
} else {
// Complex fill type - calculate byte size and use that
int totalSizeBytes = elementSizeBytes * dataNumElements;
dataChunk.addDataZeroFilled(AsmDataNumeric.Type.BYTE, totalSizeBytesAsm, totalSizeBytes, null);
}
} else if (value instanceof ConstantArrayKickAsm) {
ConstantArrayKickAsm kickAsm = (ConstantArrayKickAsm) value;
// default - larger then 256
int bytes = 1023;
Integer declaredSize = getArrayDeclaredSize(valueArraySpec.getArraySize());
if (declaredSize != null) {
bytes = declaredSize * elementType.getSizeBytes();
}
dataChunk.addDataKickAsm(bytes, kickAsm.getKickAsmCode(), AsmEncodingHelper.getEncoding(value));
dataNumElements = bytes;
} else if (value instanceof ConstantString) {
ConstantString stringValue = (ConstantString) value;
String asmConstant = AsmFormat.getAsmConstant(program, stringValue, 99, scopeRef);
dataChunk.addDataString(asmConstant, AsmEncodingHelper.getEncoding(stringValue));
if (stringValue.isZeroTerminated()) {
dataChunk.addDataNumeric(AsmDataNumeric.Type.BYTE, "0", null);
}
dataNumElements = stringValue.getStringLength();
} else {
// Assume we have a ConstantArrayList
ConstantArrayList constantArrayList = (ConstantArrayList) value;
// Output each element to the chunk
for (ConstantValue element : constantArrayList.getElements()) {
addChunkData(dataChunk, element, elementType, null, scopeRef);
}
dataNumElements = constantArrayList.getElements().size();
}
// Pad output to match declared size (if larger than the data list)
if (!(value instanceof ConstantArrayKickAsm)) {
Integer declaredSize = getArrayDeclaredSize(valueArraySpec.getArraySize());
if (declaredSize != null && declaredSize > dataNumElements) {
long paddingSize = declaredSize - dataNumElements;
ConstantValue paddingSizeVal = new ConstantInteger(paddingSize);
int elementSizeBytes = elementType.getSizeBytes();
String paddingBytesAsm;
if (elementSizeBytes > 1) {
// TODO: Use a SIZEOF constant for the element size ASM - combine this with ConstantArrayFilled above
paddingBytesAsm = AsmFormat.getAsmConstant(program, new ConstantBinary(new ConstantInteger((long) elementSizeBytes, SymbolType.NUMBER), Operators.MULTIPLY, paddingSizeVal), 99, scopeRef);
} else {
paddingBytesAsm = AsmFormat.getAsmConstant(program, paddingSizeVal, 99, scopeRef);
}
ConstantValue zeroValue = Initializers.createZeroValue(new Initializers.ValueTypeSpec(elementType), null);
if (zeroValue instanceof ConstantInteger | zeroValue instanceof ConstantPointer) {
dataChunk.addDataZeroFilled(getNumericType(elementType), paddingBytesAsm, (int) paddingSize, AsmEncodingHelper.getEncoding(zeroValue));
} else {
for (int i = 0; i < paddingSize; i++) {
addChunkData(dataChunk, zeroValue, elementType, null, scopeRef);
}
}
}
}
} else if (value instanceof ConstantString) {
ConstantString stringValue = (ConstantString) value;
// Ensure encoding is good
String asmConstant = AsmFormat.getAsmConstant(program, stringValue, 99, scopeRef);
dataChunk.addDataString(asmConstant, AsmEncodingHelper.getEncoding(stringValue));
if (stringValue.isZeroTerminated()) {
dataChunk.addDataNumeric(AsmDataNumeric.Type.BYTE, "0", null);
}
} else if (SymbolType.BYTE.equals(valueType) || SymbolType.SBYTE.equals(valueType)) {
dataChunk.addDataNumeric(AsmDataNumeric.Type.BYTE, AsmFormat.getAsmConstant(program, value, 99, scopeRef), AsmEncodingHelper.getEncoding(value));
} else if (SymbolType.WORD.equals(valueType) || SymbolType.SWORD.equals(valueType)) {
dataChunk.addDataNumeric(AsmDataNumeric.Type.WORD, AsmFormat.getAsmConstant(program, value, 99, scopeRef), AsmEncodingHelper.getEncoding(value));
} else if (SymbolType.DWORD.equals(valueType) || SymbolType.SDWORD.equals(valueType)) {
dataChunk.addDataNumeric(AsmDataNumeric.Type.DWORD, AsmFormat.getAsmConstant(program, value, 99, scopeRef), AsmEncodingHelper.getEncoding(value));
} else if (valueType instanceof SymbolTypePointer) {
dataChunk.addDataNumeric(AsmDataNumeric.Type.WORD, AsmFormat.getAsmConstant(program, value, 99, scopeRef), AsmEncodingHelper.getEncoding(value));
} else if (SymbolType.BOOLEAN.equals(valueType)) {
dataChunk.addDataNumeric(AsmDataNumeric.Type.BYTE, AsmFormat.getAsmConstant(program, value, 99, scopeRef), AsmEncodingHelper.getEncoding(value));
} else {
throw new InternalError("Unhandled array element type " + valueType.toString() + " value " + value.toString(program));
}
}
/**
* Get the numeric data type to use when outputting a value type to ASM
*
* @param valueType The value type
* @return The numeric data type
*/
private static AsmDataNumeric.Type getNumericType(SymbolType valueType) {
if (SymbolType.BYTE.equals(valueType) || SymbolType.SBYTE.equals(valueType)) {
return AsmDataNumeric.Type.BYTE;
} else if (SymbolType.WORD.equals(valueType) || SymbolType.SWORD.equals(valueType)) {
return AsmDataNumeric.Type.WORD;
} else if (SymbolType.DWORD.equals(valueType) || SymbolType.SDWORD.equals(valueType)) {
return AsmDataNumeric.Type.DWORD;
} else if (valueType instanceof SymbolTypePointer) {
return AsmDataNumeric.Type.WORD;
} else {
throw new InternalError("Unhandled type " + valueType.toString());
}
}
private void genStatements(AsmProgram asm, Graph.Block block) {
Iterator<Statement> statementsIt = block.getStatements().iterator();
while (statementsIt.hasNext()) {
Statement statement = statementsIt.next();
if (!(statement instanceof StatementPhiBlock)) {
try {
generateStatementAsm(asm, block, statement, true);
} catch (AsmFragmentTemplateSynthesizer.UnknownFragmentException e) {
StatementSource statementSource = statement.getSource();
if (warnFragmentMissing) {
String stmtFormat = "";
if (statementSource != null) stmtFormat = statementSource.format();
program.getLog().append("Warning! Unknown fragment for statement " + statement.toString(program, false) + "\nMissing ASM fragment " + e.getFragmentSignature() + "\n" + stmtFormat);
asm.addLine(new AsmInlineKickAsm(".assert \"Missing ASM fragment " + e.getFragmentSignature() + "\", 0, 1", 0L, 0L, CpuClobber.CLOBBER_NONE));
} else {
throw new CompileError("Unknown fragment for statement " + statement.toString(program, false) + "\nMissing ASM fragment " + e.getFragmentSignature(), statementSource);
}
} catch (CompileError e) {
if (e.getSource() == null) {
throw new CompileError(e.getMessage(), statement);
}
}
}
}
}
/**
* Generate ASM code for a single statement
*
* @param asm The ASM program to generate into
* @param block The block containing the statement
* @param statement The statement to generate ASM code for
*/
void generateStatementAsm(AsmProgram asm, Graph.Block block, Statement statement, boolean genCallPhiEntry) {
asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo));
generateComments(asm, statement.getComments());
if (!(statement instanceof StatementPhiBlock)) {
if (statement instanceof StatementAssignment) {
StatementAssignment assignment = (StatementAssignment) statement;
LValue lValue = assignment.getlValue();
if (assignment.getOperator() == null && assignment.getrValue1() == null && isRegisterCopy(lValue, assignment.getrValue2())) {
//asm.addComment(lValue.toString(program) + " = " + assignment.getrValue2().toString(program) + " // register copy " + getRegister(lValue));
} else {
// sven - catch this error decently, it sometimes throws an exception!
try {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.assignment(assignment, program), program);
} catch( AsmFragmentTemplateSynthesizer.UnknownFragmentException e) {
throw new AsmFragmentTemplateSynthesizer.UnknownFragmentException(e.getMessage());
} catch( RuntimeException e) {
throw new CompileError("Problem with source, runtime Exception: " + e.getMessage(), statement);
}
}
} else if (statement instanceof StatementConditionalJump) {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.conditionalJump((StatementConditionalJump) statement, block, program), program);
} else if (statement instanceof StatementCall) {
StatementCall call = (StatementCall) statement;
Procedure toProcedure = getScope().getProcedure(call.getProcedure());
Procedure fromProcedure = program.getProcedure(block); // We obtain from where the procedure is called, to validate the bank equality.
if (toProcedure.isDeclaredIntrinsic()) {
if (Pass1ByteXIntrinsicRewrite.INTRINSIC_MAKELONG4.equals(toProcedure.getFullName())) {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.makelong4(call, program), program);
} else {
throw new CompileError("Intrinsic procedure not supported " + toProcedure.toString(program));
}
} else if (Procedure.CallingConvention.PHI_CALL.equals(toProcedure.getCallingConvention())) {
// Generate PHI transition
if (genCallPhiEntry) {
Graph.Block callSuccessor = getGraph().getCallSuccessor(block);
if (callSuccessor != null && callSuccessor.hasPhiBlock()) {
PhiTransitions.PhiTransition transition = getTransitions(callSuccessor).getTransition(block);
if (transitionIsGenerated(transition)) {
throw new InternalError("Error! JSR transition already generated. Must modify PhiTransitions code to ensure this does not happen.");
}
genBlockPhiTransition(asm, block, callSuccessor, block.getScope());
}
}
final Bank.CallingDistance callingDistance = Bank.CallingDistance.forCall(fromProcedure.getBank(), toProcedure.getBank());
if(Bank.CallingDistance.NEAR.equals(callingDistance)) {
asm.addInstruction("jsr", CpuAddressingMode.ABS, toProcedure.getCallFullName(), false);
} else {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.callBanked(toProcedure, callingDistance, program), program);
}
} else if (Procedure.CallingConvention.STACK_CALL.equals(toProcedure.getCallingConvention())) {
final Bank.CallingDistance callingDistance = Bank.CallingDistance.forCall(fromProcedure.getBank(), toProcedure.getBank());
if(Bank.CallingDistance.NEAR.equals(callingDistance)) {
if(toProcedure.getAsmLibrary() == null) {
asm.addInstruction("jsr", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false);
} else {
// Call the library routine and clobber all registers.
// Possibly an option to be given that specific routines only clobber specific registers.
asm.addInstruction("jsr", CpuAddressingMode.ABS, toProcedure.getCallFullName(), false);
asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL);
}
} else {
throw new CompileError("Stack Call procedure not supported in banked mode " + toProcedure.toString(program));
}
}
} else if (statement instanceof StatementCallExecute) {
StatementCallExecute call = (StatementCallExecute) statement;
ProcedureRef procedureRef = call.getProcedure();
if(procedureRef != null) {
ProgramScope scope = getScope();
Procedure toProcedure = scope.getProcedure(procedureRef);
Procedure fromProcedure = program.getProcedure(block); // We obtain from where the procedure is called, to validate the bank equality.
RValue procedureRVal = call.getProcedureRVal();
// Same as PHI
final Bank.CallingDistance callingDistance = Bank.CallingDistance.forCall(fromProcedure.getBank(), toProcedure.getBank());
if (Bank.CallingDistance.FAR.equals(callingDistance)) {
// if (toProcedure.isDeclaredBanked() && fromProcedure.getBank() != toProcedure.getBank()) {
throw new CompileError("Stack Call procedure not supported in banked mode " + toProcedure.toString(program));
} else {
if (toProcedure.getAsmLibrary() == null) {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.call(call, indirectCallCount++, program), program);
asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL);
} else {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.callBanked(toProcedure, callingDistance, program), program);
// Call the library routine and clobber all registers.
// Possibly an option to be given that specific routines only clobber specific registers.
//asm.addInstruction("jsr", CpuAddressingMode.ABS, toProcedure.getLibrary() + "." + call.getProcedure().getFullName(), false);
asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL);
}
}
// RValue procedureRVal = call.getProcedureRVal();
if (!(procedureRVal instanceof ProcedureRef)) {
asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL);
}
} else {
RValue procedureRVal = call.getProcedureRVal();
// Generate ASM for a call
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.call(call, indirectCallCount++, program), program);
if (!(procedureRVal instanceof ProcedureRef)) {
asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL);
}
}
} else if (statement instanceof StatementExprSideEffect) {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.exprSideEffect((StatementExprSideEffect) statement, program), program);
} else if (statement instanceof StatementReturn) {
Procedure procedure = null;
ScopeRef scope = block.getScope();
if (!scope.equals(ScopeRef.ROOT)) {
procedure = getScope().getProcedure((ProcedureRef) scope);
}
if (procedure == null || procedure.getInterruptType() == null) {
asm.addInstruction("rts", CpuAddressingMode.NON, null, false);
} else {
generateInterruptExit(asm, procedure);
}
} else if (statement instanceof StatementAsm) {
StatementAsm statementAsm = (StatementAsm) statement;
HashMap<String, Value> bindings = new HashMap<>();
AsmFragmentInstance asmFragmentInstance = new AsmFragmentInstance(program, "inline", block.getScope(), new AsmFragmentTemplate(statementAsm.getAsmLines(), program.getTargetCpu()), bindings);
asmFragmentInstance.generate(asm);
AsmChunk currentChunk = asm.getCurrentChunk();
if (statementAsm.getDeclaredClobber() != null) {
currentChunk.setClobberOverwrite(statementAsm.getDeclaredClobber());
} else {
for (AsmLine asmLine : currentChunk.getLines()) {
if (asmLine instanceof AsmInstruction) {
AsmInstruction asmInstruction = (AsmInstruction) asmLine;
if (asmInstruction.getCpuOpcode().getMnemonic().equals("jsr")) {
currentChunk.setClobberOverwrite(CpuClobber.CLOBBER_ALL);
}
}
}
}
/* author: sven.van.de.velde@telenet.be - 2023-03-21
The following logic ensures that inlined asm{} blocks placed in inline functions()
are replacing the used C constants or variables with the inlined version
of these C constants or variables.
During pass1, in the functions inlineStatement() and execute() in the Pass1ProcedureInline.c,
the asm fragment gets scanned for referenced constants or variables and the
referenced constant names get updated with the modified inlined referenced constant names.
These modified reference names are then used here in the asm generation, to scan the asm fragment,
of which the source is the bare, parsed antlr source, and really directly in the cut/pasted source
search for any constant or variable in operand1 and replace with the inlined reference
constant or variable name using the Operand1 structure.
This is the only pragmatic way I saw possible.
However, this section gets called during coalescing many, many times and
slows the compiler. However, such solution requires a complete redesign and cannot
just be put in scope to add this fix. So I hope that this change is acceptable.
*/
for (AsmLine asmLine : currentChunk.getLines()) {
if (asmLine instanceof AsmInstruction) {
AsmInstruction asmInstruction = (AsmInstruction) asmLine;
Map<String, SymbolRef> referenced = statementAsm.getReferenced();
for(String reference : referenced.keySet()) {
String operand = asmInstruction.getOperand1();
if(operand != null) {
String replace = referenced.get(reference).getLocalName();
if(operand.startsWith(replace)) {
} else {
if(operand.contains(reference)) {
operand = operand.replaceAll(reference, replace);
asmInstruction.setOperand1(operand);
}
}
}
}
}
}
} else if (statement instanceof StatementKickAsm) {
StatementKickAsm statementKasm = (StatementKickAsm) statement;
addKickAsm(asm, statementKasm);
AsmChunk currentChunk = asm.getCurrentChunk();
if (statementKasm.getDeclaredClobber() != null) {
currentChunk.setClobberOverwrite(statementKasm.getDeclaredClobber());
}
} else if (statement instanceof StatementCallPointer) {
throw new InternalError("Statement not supported " + statement);
}
}
}
/**
* Generate exit-code for entering an interrupt procedure based on the interrupt type
*
* @param asm The assembler to generate code into
* @param procedure The interrupt procedure
*/
private void generateInterruptEntry(AsmProgram asm, Procedure procedure) {
final String interruptType = procedure.getInterruptType().toLowerCase();
AsmFragmentInstanceSpec entryFragment;
String entryName;
if (interruptType.contains("clobber")) {
entryFragment = AsmFragmentInstanceSpecBuilder.interruptEntry(interruptType.replace("clobber", "all"), program);
entryName = entryFragment.getSignature().replace("all", "clobber");
} else {
entryFragment = AsmFragmentInstanceSpecBuilder.interruptEntry(interruptType, program);
entryName = entryFragment.getSignature();
}
try {
asm.startChunk(procedure.getRef(), null, "interrupt(" + entryName + ")");
AsmFragmentCodeGenerator.generateAsm(asm, entryFragment, program);
} catch (AsmFragmentTemplateSynthesizer.UnknownFragmentException e) {
throw new CompileError("Interrupt type not supported " + procedure.getInterruptType() + " int " + procedure + "\n" + e.getMessage());
}
}
/**
* Generate exit-code for ending an interrupt procedure based on the interrupt type
*
* @param asm The assembler to generate code into
* @param procedure The procedure
*/
private void generateInterruptExit(AsmProgram asm, Procedure procedure) {
final String interruptType = procedure.getInterruptType().toLowerCase();
AsmFragmentInstanceSpec entryFragment;
String entryName;
if (interruptType.contains("clobber")) {
entryFragment = AsmFragmentInstanceSpecBuilder.interruptExit(interruptType.replace("clobber", "all"), program);
entryName = entryFragment.getSignature().replace("all", "clobber");
} else {
entryFragment = AsmFragmentInstanceSpecBuilder.interruptExit(interruptType, program);
entryName = entryFragment.getSignature();
}
asm.startChunk(procedure.getRef(), null, "interrupt(" + entryName + ")");
try {
AsmFragmentCodeGenerator.generateAsm(asm, entryFragment, program);
} catch (AsmFragmentTemplateSynthesizer.UnknownFragmentException e) {
throw new CompileError("Interrupt type not supported " + procedure.getInterruptType() + " int " + procedure + "\n" + e.getMessage());
}
}
private void addKickAsm(AsmProgram asm, StatementKickAsm statementKasm) {
Long asmBytes = null;
if (statementKasm.getBytes() != null) {
ConstantValue kasmBytes = (ConstantValue) statementKasm.getBytes();
ConstantLiteral kasmBytesLiteral = kasmBytes.calculateLiteral(getScope());
asmBytes = ((ConstantInteger) kasmBytesLiteral).getInteger();
}
Long asmCycles = null;
if (statementKasm.getCycles() != null) {
ConstantValue kasmCycles = (ConstantValue) statementKasm.getCycles();
ConstantLiteral kasmCyclesLiteral = kasmCycles.calculateLiteral(getScope());
asmCycles = ((ConstantInteger) kasmCyclesLiteral).getInteger();
}
asm.addInlinedKickAsm(statementKasm.getKickAsmCode(), asmBytes, asmCycles, statementKasm.getDeclaredClobber());
}
/**
* Generate all block entry points (phi transitions) which have not already been generated.
*
* @param asm The ASM program to generate into
* @param toBlock The block to generate remaining entry points for.
*/
private void genBlockEntryPoints(AsmProgram asm, Graph.Block toBlock) {
PhiTransitions transitions = getTransitions(toBlock);
for (var fromBlock : transitions.getFromBlocks()) {
PhiTransitions.PhiTransition transition = transitions.getTransition(fromBlock);
if (!transitionIsGenerated(transition) && toBlock.getLabel().equals(fromBlock.getConditionalSuccessor())) {
genBlockPhiTransition(asm, fromBlock, toBlock, toBlock.getScope());
asm.addInstruction("JMP", CpuAddressingMode.ABS, AsmFormat.asmFix(toBlock.getLabel().getLocalName()), false);
}
}
}
/**
* Generate a phi block transition. The transition performs all necessary assignment operations when moving from one block to another.
* The transition can be inserted either at the start of the to-block (used for conditional jumps)
* or at the end of the from-block ( used at default block transitions and before JMP/JSR)
*
* @param asm The ASP program to generate the transition into.
* @param fromBlock The from-block
* @param toBlock The to-block
* @param scope The scope where the ASM code is being inserted. Used to ensure that labels inserted in the code reference the right variables.
* If the transition code is inserted in the to-block, this is the scope of the to-block.
* If the transition code is inserted in the from-block this is the scope of the from-block.
*/
private void genBlockPhiTransition(AsmProgram asm, Graph.Block fromBlock, Graph.Block toBlock, ScopeRef scope) {
PhiTransitions transitions = getTransitions(toBlock);
PhiTransitions.PhiTransition transition = transitions.getTransition(fromBlock);
if (!transitionIsGenerated(transition)) {
Statement toFirstStatement = toBlock.getStatements().get(0);
String chunkSrc = "[" + toFirstStatement.getIndex() + "] phi from ";
for (var fBlock : transition.getFromBlocks()) {
chunkSrc += fBlock.getLabel().getFullName() + " ";
}
chunkSrc += "to " + toBlock.getLabel().getFullName();
asm.startChunk(scope, toFirstStatement.getIndex(), chunkSrc);
asm.getCurrentChunk().setSubStatementId(transition.getTransitionId());
for (var fBlock : transition.getFromBlocks()) {
asm.addLabel(AsmFormat.asmFix(toBlock.getLabel().getLocalName() + "_from_" + fBlock.getLabel().getLocalName()));
}
List<PhiTransitions.PhiTransition.PhiAssignment> assignments = transition.getAssignments();
for (PhiTransitions.PhiTransition.PhiAssignment assignment : assignments) {
LValue lValue = assignment.getVariable();
RValue rValue = assignment.getrValue();
Statement statement = assignment.getPhiBlock();
// Generate an ASM move fragment
asm.startChunk(scope, statement.getIndex(), "[" + statement.getIndex() + "] phi " + lValue.toString(program) + " = " + rValue.toString(program));
asm.getCurrentChunk().setSubStatementId(transition.getTransitionId());
asm.getCurrentChunk().setSubStatementIdx(assignment.getAssignmentIdx());
if (isRegisterCopy(lValue, rValue)) {
asm.getCurrentChunk().setFragment("register_copy");
} else {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.assignment(lValue, rValue, program, scope), program);
}
}
transitionSetGenerated(transition);
} else {
program.getLog().append("Already generated transition from " + fromBlock.getLabel() + " to " + toBlock.getLabel() + " - not generating it again!");
}
}
/**
* Get phi transitions for a specific to-block.
*
* @param toBlock The block
* @return The transitions into the block
*/
private PhiTransitions getTransitions(Graph.Block toBlock) {
return program.getPhiTransitions().get(toBlock.getLabel());
}
private Registers.Register getRegister(RValue rValue) {
if (rValue instanceof VariableRef) {
VariableRef rValueRef = (VariableRef) rValue;
return program.getSymbolInfos().getVariable(rValueRef).getAllocation();
} else if (rValue instanceof CastValue) {
return getRegister(((CastValue) rValue).getValue());
} else {
return null;
}
}
private boolean isRegisterCopy(LValue lValue, RValue rValue) {
return getRegister(lValue) != null && getRegister(rValue) != null && getRegister(lValue).equals(getRegister(rValue));
}
}