mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-05-30 10:41:36 +00:00
1a53d6a913
- Generalized the AsmLibrary procedures to symbols, to also allow exporting variables. - Removed the initialization part of .asm library header generation for global exported variables. - The directive __asm_export can now also be used to indicate global variables exporting within C libraries (for potential .asm library generation). - Converted the usage of typedef to struct for __conio global variable in cx16_conio.c - Updated example code. - Fixed a bug where for exported structs and imported structs, the variables were defined as volatile to non-volatile. - VariableBuilder constructor now also received the program variable to refer to program level configurations defined. - Remove to declare string constants as .asm library exported global variables. - Removed the optimization PassNAsmLibraryGlobalVarsExport. It is not needed. Each global variable export must be explicitly declared using asm_export or __asm_export. - Improved naming of variables and procedures to retrieve and manage import and export libraries within the program.
920 lines
34 KiB
Java
920 lines
34 KiB
Java
package dk.camelot64.kickc.model;
|
|
|
|
import dk.camelot64.kickc.CompileLog;
|
|
import dk.camelot64.kickc.OutputFileManager;
|
|
import dk.camelot64.kickc.SourceLoader;
|
|
import dk.camelot64.kickc.asm.AsmExportLibrary;
|
|
import dk.camelot64.kickc.asm.AsmIdentifier;
|
|
import dk.camelot64.kickc.asm.AsmImportLibrary;
|
|
import dk.camelot64.kickc.asm.AsmProgram;
|
|
import dk.camelot64.kickc.fragment.synthesis.AsmFragmentTemplateMasterSynthesizer;
|
|
import dk.camelot64.kickc.model.statements.StatementPhiBlock;
|
|
import dk.camelot64.kickc.model.symbols.*;
|
|
import dk.camelot64.kickc.model.values.LabelRef;
|
|
import dk.camelot64.kickc.model.values.ProcedureRef;
|
|
import dk.camelot64.kickc.model.values.ScopeRef;
|
|
import dk.camelot64.kickc.passes.calcs.*;
|
|
import dk.camelot64.kickc.passes.utils.ProcedureUtils;
|
|
|
|
import java.io.File;
|
|
import java.nio.file.Path;
|
|
import java.nio.file.Paths;
|
|
import java.util.*;
|
|
|
|
/** A KickC Intermediate Compiler Language (ICL) Program */
|
|
public class Program {
|
|
|
|
/** Contains the current path of the program location */
|
|
private Path currentPath;
|
|
|
|
/** The log containing information about the compilation process. */
|
|
private CompileLog log;
|
|
|
|
/** The manager responsible for creating output files. PASS 0-5 (STATIC) */
|
|
private OutputFileManager outputFileManager;
|
|
|
|
/** Paths used for including files. PASS 0 (STATIC) */
|
|
private List<String> includePaths;
|
|
/** Paths used for library files. PASS 0 (STATIC) */
|
|
private List<String> libraryPaths;
|
|
/** Paths used for target files. PASS 0 (STATIC) */
|
|
private List<String> targetPlatformPaths;
|
|
|
|
/** Paths used for .asm library files. */
|
|
private List<String> asmLibraryPaths;
|
|
|
|
/** All loaded H/C-files. PASS 0 (STATIC) */
|
|
private List<String> loadedFiles;
|
|
|
|
/** The target platform that the program is being build for. PASS 0-5 (STATIC) */
|
|
private TargetPlatform targetPlatform;
|
|
|
|
/** Base folder for finding ASM fragment files. (STATIC) */
|
|
private Path asmFragmentBaseFolder;
|
|
/** The ASM fragment synthesizer responsible for loading/synthesizing ASM fragments. Depends on the target CPU. (STATIC) */
|
|
private AsmFragmentTemplateMasterSynthesizer asmFragmentMasterSynthesizer;
|
|
|
|
/** Missing fragments produce a warning instead of an error (STATIC) */
|
|
private boolean warnFragmentMissing = false;
|
|
/** Array syntax used on types (eg. char[8] x; ) produce a warning instead of an error (STATIC) */
|
|
private boolean warnArrayType = false;
|
|
/** Enable live ranges per call path optimization, which limits memory usage and code, but takes a lot of compile time. */
|
|
private boolean enableLiveRangeCallPath = true;
|
|
|
|
/** Reserved ZP addresses that the compiler cannot use. PASS 0-5 (STATIC) */
|
|
private List<Integer> reservedZps;
|
|
/** Resource files that should be copied to the output folder to be compiled with the generated ASM. PASS 0-5 (STATIC) */
|
|
private final List<Path> asmResourceFiles;
|
|
/** Comments for the (main) file. PASS 0-4 (STATIC) */
|
|
private List<Comment> mainFileComments;
|
|
|
|
/** The main scope. PASS 0-5 (DYNAMIC) */
|
|
private ProgramScope scope;
|
|
/** The procedure that starts the execution of the program. (Default: _start() which calls _init() and main() ) */
|
|
private ProcedureRef startProcedure;
|
|
|
|
/** Struct values unwound to individual variables. PASS 1 (STATIC) */
|
|
private StructVariableMemberUnwinding structVariableMemberUnwinding;
|
|
|
|
/** Cached information about calls. PASS 1-4 (CACHED ON-DEMAND) */
|
|
private CallGraph callGraph;
|
|
|
|
/** The procedures being compiled. PASS 1-5 (DYNAMIC)*/
|
|
private List<ProcedureCompilation> procedureCompilations;
|
|
|
|
/** Variables modified inside procedures. PASS 1 (STATIC) */
|
|
private ProcedureModifiedVars procedureModifiedVars;
|
|
|
|
/** Registers potentially usable as allocation for each live range equivalence class. PASS 4 (DYNAMIC) */
|
|
private RegisterPotentials registerPotentials;
|
|
/** Live range equivalence classes containing variables that do not have overlapping live ranges. PASS 3-5 (DYNAMIC) */
|
|
private LiveRangeEquivalenceClassSet liveRangeEquivalenceClassSet;
|
|
/** The 6502 ASM program. PASS 4-5 (DYNAMIC) */
|
|
private AsmProgram asm;
|
|
|
|
/** Cached information about the variables referenced by blocks/statements. PASS 1-4 (CACHED ON-DEMAND) */
|
|
private VariableReferenceInfos variableReferenceInfos;
|
|
/** Cached information about the closure of all block successors. */
|
|
private ControlFlowBlockSuccessorClosure blockSuccessorClosure;
|
|
/** Information about dominators of all blocks. PASS 2U-4 (CACHED ON-DEMAND) */
|
|
private DominatorsGraph dominators;
|
|
/** Cached information about symbols. Contains a symbol table cache for fast access. PASS 3-4 (CACHED ON-DEMAND) */
|
|
private SymbolInfos symbolInfos;
|
|
/** Cached phi transitions into each block. PASS 4 (CACHED ON-DEMAND) */
|
|
private Map<LabelRef, PhiTransitions> phiTransitions;
|
|
/** The live ranges of all variables. PASS 3-4 (CACHED ON-DEMAND) */
|
|
private LiveRangeVariables liveRangeVariables;
|
|
/** The effective live ranges of all variables. PASS 3-4 (CACHED ON-DEMAND) */
|
|
private LiveRangeVariablesEffective liveRangeVariablesEffective;
|
|
/** Separation of live range equivalence classes into scopes - used for register uplift. PASS 4 (CACHED ON-DEMAND) */
|
|
private RegisterUpliftProgram registerUpliftProgram;
|
|
/** Cached information about which block is each statement a part of. PASS 2U-5 (CACHED ON-DEMAND) */
|
|
private StatementInfos statementInfos;
|
|
/** Information about loops. PASS 2U-5 (CACHED ON-DEMAND) */
|
|
private NaturalLoopSet loopSet;
|
|
/** The register weight of all variables describing how much the variable would theoretically gain from being in a register. PASS 3-5 (CACHED ON-DEMAND) */
|
|
private VariableRegisterWeights variableRegisterWeights;
|
|
|
|
/** Defines that the program results in an .asm library of routines or capabilities
|
|
* to be included in other programs for asm file linkage.
|
|
*/
|
|
private AsmIdentifier asmLibraryName;
|
|
|
|
/** #820/34 - Declare global variables that aren't yet declared.
|
|
* Specifies whether during ASM generation, a library .namespace is active.
|
|
*/
|
|
private boolean asmLibraryNamespace;
|
|
|
|
/** #820/34 - Declare global variables that aren't yet declared.
|
|
* Keeps all the symbols that have been added into the library
|
|
*/
|
|
private HashMap<String, Symbol> asmLibraryNamespaceSymbols;
|
|
|
|
/**
|
|
* Defines the asm source to be imported with the defined procedures.
|
|
*/
|
|
private Map<String, AsmImportLibrary> asmImportLibraries;
|
|
|
|
/**
|
|
* Defines the asm source to be exported with the defined procedures.
|
|
*/
|
|
private final Map<String, AsmExportLibrary> asmExportLibraries;
|
|
|
|
|
|
/** All #pragma code segments. Collected during parsing. These are used by the bank() pragmas to validate if the code segment exists during compilation.*/
|
|
public Program() {
|
|
this.outputFileManager = new OutputFileManager();
|
|
this.scope = new ProgramScope();
|
|
this.log = new CompileLog();
|
|
this.includePaths = new ArrayList<>();
|
|
this.libraryPaths = new ArrayList<>();
|
|
this.targetPlatformPaths = new ArrayList<>();
|
|
this.asmLibraryPaths = new ArrayList<>();
|
|
this.loadedFiles = new ArrayList<>();
|
|
this.asmResourceFiles = new ArrayList<>();
|
|
this.reservedZps = new ArrayList<>();
|
|
this.procedureCompilations = new ArrayList<>();
|
|
this.asmImportLibraries = new LinkedHashMap<>();
|
|
this.asmExportLibraries = new LinkedHashMap<>();
|
|
this.currentPath = new File(".").toPath();
|
|
this.asmLibraryNamespace = false;
|
|
this.asmLibraryNamespaceSymbols = new HashMap<>();
|
|
}
|
|
|
|
/**
|
|
* Clears all data that is only used in PASS 1
|
|
*/
|
|
public void endPass1() {
|
|
this.includePaths = null;
|
|
this.libraryPaths = null;
|
|
this.targetPlatformPaths = null;
|
|
this.loadedFiles = null;
|
|
this.procedureModifiedVars = null;
|
|
this.structVariableMemberUnwinding = null;
|
|
}
|
|
|
|
/**
|
|
* Clears all data that is only used in PASS 2-4
|
|
*/
|
|
public void endPass4() {
|
|
this.mainFileComments = null;
|
|
this.callGraph = null;
|
|
this.variableReferenceInfos = null;
|
|
this.dominators = null;
|
|
this.symbolInfos = null;
|
|
this.phiTransitions = null;
|
|
this.liveRangeVariables = null;
|
|
this.liveRangeVariablesEffective = null;
|
|
this.registerPotentials = null;
|
|
this.registerUpliftProgram = null;
|
|
}
|
|
|
|
/**
|
|
* Get a read-only control flow graph for the entire program.
|
|
* @return The control flow graph
|
|
*/
|
|
public Graph getGraph() {
|
|
return new Graph() {
|
|
@Override
|
|
public Block getBlock(LabelRef symbol) {
|
|
return getProcedureCompilations().stream().map(ProcedureCompilation::getGraph).filter(Objects::nonNull).map(graph -> graph.getBlock(symbol)).filter(Objects::nonNull).findFirst().orElse(null);
|
|
}
|
|
|
|
@Override
|
|
public List<Block> getAllBlocks() {
|
|
return getProcedureCompilations().stream().map(ProcedureCompilation::getGraph).filter(Objects::nonNull).map(ControlFlowGraph::getAllBlocks).flatMap(Collection::stream).toList();
|
|
}
|
|
|
|
@Override
|
|
public void addBlock(Block block) {
|
|
throw new CompileError("Internal Error! Cannot add blocks to the read-only total control flow graph!");
|
|
}
|
|
};
|
|
}
|
|
|
|
public List<ProcedureCompilation> getProcedureCompilations() {
|
|
return procedureCompilations;
|
|
}
|
|
|
|
public void setProcedureCompilations(List<ProcedureCompilation> procedureCompilations) {
|
|
this.procedureCompilations = procedureCompilations;
|
|
}
|
|
|
|
/**
|
|
* Pretty-print the entire control flow graph of all procedures.
|
|
* @return The pretty-printed control flow graph
|
|
*/
|
|
public String prettyControlFlowGraph() {
|
|
StringBuilder graphPretty = new StringBuilder();
|
|
for(ProcedureCompilation procedureCompilation : getProcedureCompilations()) {
|
|
if(procedureCompilation.getGraph() != null) {
|
|
graphPretty.append(procedureCompilation.getGraph().toString(this));
|
|
} else {
|
|
graphPretty.append(procedureCompilation.toString()).append(": null");
|
|
}
|
|
}
|
|
return graphPretty.toString();
|
|
}
|
|
|
|
public Path getCurrentPath() {
|
|
return currentPath;
|
|
}
|
|
|
|
public void setCurrentPath(Path currentPath) {
|
|
this.currentPath = currentPath;
|
|
}
|
|
|
|
public OutputFileManager getOutputFileManager() {
|
|
return outputFileManager;
|
|
}
|
|
|
|
public void setEnableLiveRangeCallPath(boolean enableLiveRangeCallPath) {
|
|
this.enableLiveRangeCallPath = enableLiveRangeCallPath;
|
|
}
|
|
|
|
public boolean isWarnFragmentMissing() {
|
|
return warnFragmentMissing;
|
|
}
|
|
|
|
public void setWarnFragmentMissing(boolean warnFragmentMissing) {
|
|
this.warnFragmentMissing = warnFragmentMissing;
|
|
}
|
|
|
|
public boolean isWarnArrayType() {
|
|
return warnArrayType;
|
|
}
|
|
|
|
public void setWarnArrayType(boolean errorArrayKickC) {
|
|
this.warnArrayType = errorArrayKickC;
|
|
}
|
|
|
|
public Path getAsmFragmentBaseFolder() {
|
|
return asmFragmentBaseFolder;
|
|
}
|
|
|
|
public void setAsmFragmentBaseFolder(Path asmFragmentBaseFolder) {
|
|
this.asmFragmentBaseFolder = asmFragmentBaseFolder;
|
|
}
|
|
|
|
public AsmFragmentTemplateMasterSynthesizer getAsmFragmentMasterSynthesizer() {
|
|
return asmFragmentMasterSynthesizer;
|
|
}
|
|
|
|
public void initAsmFragmentMasterSynthesizer(Path asmFragmentCacheFolder) {
|
|
this.asmFragmentMasterSynthesizer = new AsmFragmentTemplateMasterSynthesizer(asmFragmentCacheFolder, asmFragmentBaseFolder, getLog());
|
|
}
|
|
|
|
public TargetPlatform getTargetPlatform() {
|
|
return targetPlatform;
|
|
}
|
|
|
|
|
|
public String getAsmLibraryName() {
|
|
return asmLibraryName != null ? asmLibraryName.toString() : null;
|
|
}
|
|
|
|
public String getAsmLibraryLabel() {
|
|
return asmLibraryName != null ? asmLibraryName.getAsm() : null;
|
|
}
|
|
|
|
public void setAsmLibraryName(String asmLibraryName) {
|
|
if(asmLibraryName != null)
|
|
this.asmLibraryName = new AsmIdentifier(asmLibraryName);
|
|
}
|
|
|
|
public void setTargetPlatform(TargetPlatform targetPlatform) {
|
|
this.targetPlatform = targetPlatform;
|
|
}
|
|
|
|
public TargetCpu getTargetCpu() {
|
|
return getTargetPlatform().getCpu();
|
|
}
|
|
|
|
public StructVariableMemberUnwinding getStructVariableMemberUnwinding() {
|
|
return structVariableMemberUnwinding;
|
|
}
|
|
|
|
public void setStructVariableMemberUnwinding(StructVariableMemberUnwinding structVariableMemberUnwinding) {
|
|
this.structVariableMemberUnwinding = structVariableMemberUnwinding;
|
|
}
|
|
|
|
public List<Comment> getMainFileComments() {
|
|
return mainFileComments;
|
|
}
|
|
|
|
public void setMainFileComments(List<Comment> mainFileComments) {
|
|
this.mainFileComments = mainFileComments;
|
|
}
|
|
|
|
public List<String> getIncludePaths() {
|
|
return includePaths;
|
|
}
|
|
|
|
public List<String> getLibraryPaths() {
|
|
return libraryPaths;
|
|
}
|
|
|
|
public List<String> getTargetPlatformPaths() {
|
|
return targetPlatformPaths;
|
|
}
|
|
|
|
public List<String> getAsmLibraryPaths() { return asmLibraryPaths; }
|
|
|
|
public List<String> getLoadedFiles() {
|
|
return loadedFiles;
|
|
}
|
|
|
|
public List<Path> getAsmResourceFiles() {
|
|
return asmResourceFiles;
|
|
}
|
|
|
|
public void addAsmResourceFile(Path asmResourceFile) {
|
|
asmResourceFiles.add(asmResourceFile);
|
|
}
|
|
|
|
public ProcedureCompilation createProcedureCompilation(ProcedureRef procedureRef) {
|
|
if(getProcedureCompilation(procedureRef)!=null)
|
|
throw new CompileError("Error! Procedure already defined "+procedureRef.getFullName());
|
|
final ProcedureCompilation procedureCompilation = new ProcedureCompilation(procedureRef);
|
|
procedureCompilations.add(procedureCompilation);
|
|
return procedureCompilation;
|
|
}
|
|
|
|
public ProcedureCompilation getProcedureCompilation(ProcedureRef procedureRef) {
|
|
return procedureCompilations.stream().filter(pc -> pc.getProcedureRef().equals(procedureRef)).findFirst().orElse(null);
|
|
}
|
|
|
|
public ProgramScope getScope() {
|
|
return scope;
|
|
}
|
|
|
|
public void setScope(ProgramScope scope) {
|
|
this.scope = scope;
|
|
}
|
|
|
|
public ProcedureRef getStartProcedure() {
|
|
return startProcedure;
|
|
}
|
|
|
|
public String getStartProcedureName() {
|
|
return startProcedure.getFullName();
|
|
}
|
|
|
|
public void setStartProcedure(ProcedureRef startProcedure) {
|
|
this.startProcedure = startProcedure;
|
|
}
|
|
|
|
public ProcedureModifiedVars getProcedureModifiedVars() {
|
|
return procedureModifiedVars;
|
|
}
|
|
|
|
public void setProcedureModifiedVars(ProcedureModifiedVars procedureModifiedVars) {
|
|
this.procedureModifiedVars = procedureModifiedVars;
|
|
}
|
|
|
|
public AsmProgram getAsm() {
|
|
return asm;
|
|
}
|
|
|
|
public void setAsm(AsmProgram asm) {
|
|
this.asm = asm;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the call-graph for the program. Calculates the call-graph if it has not already been calculated.
|
|
*
|
|
* @return The call-graph
|
|
*/
|
|
public CallGraph getCallGraph() {
|
|
if(callGraph == null)
|
|
this.callGraph = new PassNCalcCallGraph(this).calculate();
|
|
return callGraph;
|
|
}
|
|
|
|
/**
|
|
* Clears the call-graph ensuring it will be re-calculated if used.
|
|
*/
|
|
public void clearCallGraph() {
|
|
this.callGraph = null;
|
|
}
|
|
|
|
public VariableReferenceInfos getVariableReferenceInfos() {
|
|
if(variableReferenceInfos == null)
|
|
this.variableReferenceInfos = new PassNCalcVariableReferenceInfos(this).calculate();
|
|
return variableReferenceInfos;
|
|
}
|
|
|
|
public void clearVariableReferenceInfos() {
|
|
this.variableReferenceInfos = null;
|
|
}
|
|
|
|
public ControlFlowBlockSuccessorClosure getControlFlowBlockSuccessorClosure() {
|
|
if(blockSuccessorClosure == null)
|
|
this.blockSuccessorClosure = new PassNCalcBlockSuccessorClosure(this).calculate();
|
|
return blockSuccessorClosure;
|
|
}
|
|
|
|
public void clearControlFlowBlockSuccessorClosure() {
|
|
this.blockSuccessorClosure = null;
|
|
}
|
|
|
|
public DominatorsGraph getDominators() {
|
|
if(dominators == null)
|
|
this.dominators = new PassNCalcDominators(this).calculate();
|
|
return dominators;
|
|
}
|
|
|
|
public void clearDominators() {
|
|
this.dominators = null;
|
|
}
|
|
|
|
public Map<LabelRef, PhiTransitions> getPhiTransitions() {
|
|
if(phiTransitions == null)
|
|
this.phiTransitions = new PassNCalcPhiTransitions(this).calculate();
|
|
return phiTransitions;
|
|
}
|
|
|
|
public void clearPhiTransitions() {
|
|
this.phiTransitions = null;
|
|
}
|
|
|
|
public LiveRangeVariables getLiveRangeVariables() {
|
|
if(liveRangeVariables == null)
|
|
this.liveRangeVariables = new PassNCalcLiveRangeVariables(this).calculate();
|
|
return liveRangeVariables;
|
|
}
|
|
|
|
public void setLiveRangeVariables(LiveRangeVariables liveRangeVariables) {
|
|
this.liveRangeVariables = liveRangeVariables;
|
|
}
|
|
|
|
public boolean hasLiveRangeVariables() {
|
|
return this.liveRangeVariables != null;
|
|
}
|
|
|
|
public void clearLiveRangeVariables() {
|
|
this.liveRangeVariables = null;
|
|
}
|
|
|
|
public StatementInfos getStatementInfos() {
|
|
if(statementInfos == null)
|
|
this.statementInfos = new PassNCalcStatementInfos(this).calculate();
|
|
return statementInfos;
|
|
}
|
|
|
|
public void clearStatementInfos() {
|
|
this.statementInfos = null;
|
|
}
|
|
|
|
/**
|
|
* Clear index numbers for all statements in the control flow graph.
|
|
*/
|
|
public void clearStatementIndices() {
|
|
getProcedureCompilations().stream().map(ProcedureCompilation::getGraph).filter(Objects::nonNull).forEach(ControlFlowGraph::clearStatementIndices);
|
|
}
|
|
|
|
public SymbolInfos getSymbolInfos() {
|
|
if(symbolInfos == null)
|
|
this.symbolInfos = new PassNCalcSymbolInfos(this).calculate();
|
|
return symbolInfos;
|
|
}
|
|
|
|
public boolean hasLiveRangeVariablesEffective() {
|
|
return liveRangeVariablesEffective != null;
|
|
}
|
|
|
|
public LiveRangeVariablesEffective getLiveRangeVariablesEffective() {
|
|
if(liveRangeVariablesEffective == null) {
|
|
if(enableLiveRangeCallPath) {
|
|
this.liveRangeVariablesEffective = new PassNCalcLiveRangesEffectiveCallPaths(this).calculate();
|
|
} else {
|
|
this.liveRangeVariablesEffective = new PassNCalcLiveRangesEffectiveSimple(this).calculate();
|
|
}
|
|
}
|
|
return liveRangeVariablesEffective;
|
|
}
|
|
|
|
public void clearLiveRangeVariablesEffective() {
|
|
this.liveRangeVariablesEffective = null;
|
|
}
|
|
|
|
public RegisterUpliftProgram getRegisterUpliftProgram() {
|
|
if(registerUpliftProgram == null)
|
|
this.registerUpliftProgram = new PassNCalcRegisterUpliftProgram(this).calculate();
|
|
return registerUpliftProgram;
|
|
}
|
|
|
|
public NaturalLoopSet getLoopSet() {
|
|
if(loopSet == null)
|
|
this.loopSet = new PassNCalcLoopSet(this).calculate();
|
|
return loopSet;
|
|
}
|
|
|
|
public void clearLoopSet() {
|
|
this.loopSet = null;
|
|
}
|
|
|
|
public VariableRegisterWeights getVariableRegisterWeights() {
|
|
if(variableRegisterWeights == null)
|
|
this.variableRegisterWeights = new PassNCalcVariableRegisterWeight(this).calculate();
|
|
return variableRegisterWeights;
|
|
}
|
|
|
|
public VariableRegisterWeights getOrNullVariableRegisterWeights() {
|
|
return variableRegisterWeights;
|
|
}
|
|
|
|
public LiveRangeEquivalenceClassSet getLiveRangeEquivalenceClassSet() {
|
|
return liveRangeEquivalenceClassSet;
|
|
}
|
|
|
|
public void setLiveRangeEquivalenceClassSet(LiveRangeEquivalenceClassSet liveRangeEquivalenceClassSet) {
|
|
this.liveRangeEquivalenceClassSet = liveRangeEquivalenceClassSet;
|
|
}
|
|
|
|
public RegisterPotentials getRegisterPotentials() {
|
|
return registerPotentials;
|
|
}
|
|
|
|
public void setRegisterPotentials(RegisterPotentials registerPotentials) {
|
|
this.registerPotentials = registerPotentials;
|
|
}
|
|
|
|
/**
|
|
* Adds a bunch of reserved zero-page addresses that the compiler is not allowed to use.
|
|
*
|
|
* @param reservedZp addresses to reserve
|
|
*/
|
|
public void addReservedZps(List<Integer> reservedZp) {
|
|
for(Integer zp : reservedZp) {
|
|
if(!this.reservedZps.contains(zp)) {
|
|
this.reservedZps.add(zp);
|
|
}
|
|
}
|
|
}
|
|
|
|
public List<Integer> getReservedZps() {
|
|
return reservedZps;
|
|
}
|
|
|
|
public void setReservedZps(List<Integer> reservedZps) {
|
|
this.reservedZps = reservedZps;
|
|
}
|
|
|
|
public CompileLog getLog() {
|
|
return log;
|
|
}
|
|
|
|
public void setLog(CompileLog log) {
|
|
this.log = log;
|
|
}
|
|
|
|
/**
|
|
* Get information about the size of the program
|
|
*
|
|
* @return Size information
|
|
*/
|
|
public String getSizeInfo() {
|
|
StringBuilder sizeInfo = new StringBuilder();
|
|
sizeInfo.append(getScope().getSizeInfo());
|
|
|
|
final List<Graph.Block> allBlocks = getProcedureCompilations().stream().map(ProcedureCompilation::getGraph).map(ControlFlowGraph::getAllBlocks).flatMap(Collection::stream).toList();
|
|
sizeInfo.append("SIZE blocks ").append(allBlocks.size()).append("\n");
|
|
int numStmt = allBlocks.stream().mapToInt(block -> block.getStatements().size()).sum();
|
|
sizeInfo.append("SIZE statements ").append(numStmt).append("\n");
|
|
int numPhiVars = allBlocks.stream().mapToInt(value -> value.getStatements().stream().mapToInt(value1 -> (value1 instanceof StatementPhiBlock) ? ((StatementPhiBlock) value1).getPhiVariables().size() : 0).sum()).sum();
|
|
sizeInfo.append("SIZE phi variables ").append(numPhiVars).append("\n");
|
|
|
|
if(variableReferenceInfos != null)
|
|
sizeInfo.append(variableReferenceInfos.getSizeInfo());
|
|
if(getLiveRangeEquivalenceClassSet() != null)
|
|
sizeInfo.append(getLiveRangeEquivalenceClassSet().getSizeInfo());
|
|
if(liveRangeVariablesEffective != null)
|
|
sizeInfo.append(liveRangeVariablesEffective.getSizeInfo());
|
|
if(getAsm() != null)
|
|
sizeInfo.append(getAsm().getSizeInfo());
|
|
return sizeInfo.toString();
|
|
}
|
|
|
|
/**
|
|
* Get the procedure, that the block is part of. Null if the block is not part of a procedure.
|
|
*
|
|
* @param block The control flow graph block
|
|
* @return the procedure, that the block is part of
|
|
*/
|
|
public Procedure getProcedure(Graph.Block block) {
|
|
final ScopeRef scopeRef = block.getScope();
|
|
final Scope scope = getScope().getScope(scopeRef);
|
|
if(scope instanceof Procedure) {
|
|
return (Procedure) scope;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete an entire procedure.
|
|
* Removes the entire control flow graph and all symbols.
|
|
* Does not check that the removal results in a legal program.
|
|
* @param procedureRef The procedure to remove.
|
|
*/
|
|
public void removeProcedure(ProcedureRef procedureRef) {
|
|
Procedure procedure = getScope().getProcedure(procedureRef);
|
|
procedure.getScope().remove(procedure);
|
|
procedureCompilations.remove(getProcedureCompilation(procedureRef));
|
|
}
|
|
|
|
/**
|
|
* Is the block the entry of a procedure, ie. the first block of the code of the procedure.
|
|
*
|
|
* @param block
|
|
* @return true if this is the entry of a procedure
|
|
*/
|
|
public boolean isProcedureEntry(Graph.Block block) {
|
|
Symbol symbol = getScope().getSymbol(block.getLabel());
|
|
return (symbol instanceof Procedure);
|
|
}
|
|
|
|
/**
|
|
* Get all blocks that are program entry points.
|
|
* This is the start-block and any blocks referenced by the address-off operator (&)
|
|
*/
|
|
public List<Graph.Block> getEntryPointBlocks() {
|
|
final Graph graph = getGraph();
|
|
List<Graph.Block> entryPointBlocks = new ArrayList<>();
|
|
for(Procedure procedure : getScope().getAllProcedures(true)) {
|
|
if(ProcedureUtils.isEntrypoint(procedure.getRef(), this) || Procedure.CallingConvention.STACK_CALL.equals(procedure.getCallingConvention()) || Procedure.CallingConvention.VAR_CALL.equals(procedure.getCallingConvention())) {
|
|
// Address-of is used on the procedure
|
|
Label procedureLabel = procedure.getLabel();
|
|
Graph.Block procedureBlock = graph.getBlock(procedureLabel.getRef());
|
|
entryPointBlocks.add(procedureBlock);
|
|
}
|
|
}
|
|
return entryPointBlocks;
|
|
}
|
|
|
|
public Map<String, AsmImportLibrary> getAsmImportLibraries() {
|
|
return asmImportLibraries;
|
|
}
|
|
|
|
public void setAsmImportLibraries(Map<String, AsmImportLibrary> asmImports) {
|
|
this.asmImportLibraries = asmImports;
|
|
}
|
|
|
|
/**
|
|
* Checks if there is an .asm import library with the given base name.
|
|
* @param asmImportLibraryName The .asm import library base name.
|
|
* @return true if there is an .asm import library with the given base name.
|
|
*/
|
|
public boolean hasAsmImportLibrary(String asmImportLibraryName) {
|
|
return asmImportLibraries.get(asmImportLibraryName) != null;
|
|
}
|
|
|
|
/**
|
|
* Checks if there is an .asm import library with the given base name.
|
|
* @param asmImportLibraryName The .asm import library base name.
|
|
* @return The .asm import library.
|
|
*/
|
|
public AsmImportLibrary getAsmImportLibrary(String asmImportLibraryName) {
|
|
return asmImportLibraries.get(asmImportLibraryName);
|
|
}
|
|
|
|
/**
|
|
* #820/20 - Add a new library in the asm import list.
|
|
* When adding an asm import library, it is important for the compiler to handle these input
|
|
* .asm source files correctly. KICKC in pass5 does optimizations using kickasm (yes!).
|
|
* For this, kickc copies in pass5 (in @link Pass5FixLongBranches) the .asm source files
|
|
* to the temporary directory where kickasm will compile and assemble. For this, the
|
|
* resource must first be copied into this temporary directory.
|
|
* So the asmImportResource ensures that the asm import library file gets declared in kickc
|
|
* as a resource, and copied into the temp directory when kickass needs it.
|
|
* The asmImportResource gets the OUTPUT directory of KICKC as the input source directory
|
|
* from where the .asm import library file will be copied, for kickasm to assemble.
|
|
*/
|
|
public AsmImportLibrary addAsmImportLibrary(String asmImportLibraryName) {
|
|
// Path asmImportResource = outputFileManager.getFile(outputFileManager.getOutputDirectory(),asmImportLibraryName, "asm");;
|
|
String asmImportResourceName = asmImportLibraryName + ".asm";
|
|
File asmImportResource = SourceLoader.loadFile(asmImportResourceName, getCurrentPath(), getAsmLibraryPaths());
|
|
if(asmImportResource != null) {
|
|
AsmImportLibrary asmLibrary = new AsmImportLibrary(asmImportLibraryName, asmImportResource.toPath());
|
|
this.asmImportLibraries.putIfAbsent(asmImportLibraryName, asmLibrary);
|
|
if (!this.getAsmResourceFiles().contains(asmImportResource.toPath()))
|
|
this.addAsmResourceFile(asmImportResource.toPath()); // #820/20 here ...
|
|
return asmLibrary;
|
|
} else {
|
|
throw new CompileError("Could not find .asm library \"" + asmImportResourceName + "\" from folders [" + getCurrentPath().toString() + "], " + getAsmLibraryPaths().toString());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 820/20 - Add the procedure to an .asm import library.
|
|
*/
|
|
public void addAsmImportProcedures(AsmImportLibrary asmImportLibrary, Procedure.CallingConvention callingConvention, List<String> procedures) {
|
|
|
|
for(String procedureName : procedures) {
|
|
asmImportLibrary.addSymbol(procedureName, callingConvention);
|
|
this.getLog().append("Procedure " + procedureName + " imported from .asm library " + asmImportLibrary.getAsmLibraryName() );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 820/20 - Get the .asm import library of the procedure.
|
|
*/
|
|
public AsmImportLibrary getProcedureAsmImportLibrary(String procedureName) {
|
|
Map<String, AsmImportLibrary> asmImports = this.getAsmImportLibraries();
|
|
for (String asmImportKey : asmImports.keySet()) {
|
|
AsmImportLibrary library = asmImports.get(asmImportKey);
|
|
if (library.hasSymbol(procedureName)) {
|
|
return library;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* #820/20 - Configure the procedure as an .asm import library, and set the relevant properties!
|
|
*
|
|
* @param procedure The procedure to be configured.
|
|
* @param asmImport The .asm import library.
|
|
*
|
|
* TODO: Inlined and Intrinsic procedures!
|
|
*/
|
|
public void setProcedureAsAsmImport(Procedure procedure, AsmImportLibrary asmImport) {
|
|
// Here we check if the procedure declaration is already included in the asm import.
|
|
// If yes, the procedure should:,
|
|
// - have a stackcall calling convention
|
|
// - be external
|
|
// - not inlined
|
|
// - have a library name tagged for further processing of the namespace and asm inclusion etc.
|
|
procedure.setCallingConvention(asmImport.getProcedureCallingConvention(procedure.getFullName()));
|
|
procedure.setAsmImportLibrary(asmImport.getAsmLibraryName().toString());
|
|
procedure.setDeclaredExtern(true);
|
|
procedure.setDeclaredInline(false);
|
|
}
|
|
|
|
/**
|
|
* #820/20 - Retrieve all registered .asm exported libraries!
|
|
*
|
|
* @return All .asm export libraries!
|
|
*/
|
|
public Map<String, AsmExportLibrary> getAsmExportLibraries() {
|
|
return asmExportLibraries;
|
|
}
|
|
|
|
/**
|
|
* #820/20 - Creates a new AsmLibrary from a given .asm library name.
|
|
*
|
|
* @param asmExportLibraryName The library name of the .asm export library.
|
|
* @return The new object of the .asm export library.
|
|
*/
|
|
public AsmExportLibrary addAsmExportLibrary(String asmExportLibraryName, boolean exportAll) {
|
|
Path asmExportResource = Paths.get(asmExportLibraryName + ".asm").toAbsolutePath();
|
|
AsmExportLibrary asmLibrary = null;
|
|
if(asmExportLibraryName!=null) {
|
|
// Only calculate the .asm export library when the .asm library is declared in the main program.
|
|
// Otherwise ignore. This is important for .asm library imports, where library sources are
|
|
// imported that contain the #pragma asm_export pro-processor statements.
|
|
asmLibrary = asmExportLibraries.get(asmExportLibraryName);
|
|
if (asmLibrary == null) {
|
|
asmLibrary = new AsmExportLibrary(asmExportLibraryName, asmExportResource, exportAll);
|
|
asmExportLibraries.put(asmExportLibraryName, asmLibrary);
|
|
} else {
|
|
asmLibrary.setExportAll(exportAll);
|
|
}
|
|
}
|
|
// this.addAsmResourceFile(asmExportResource);
|
|
return asmLibrary;
|
|
}
|
|
|
|
/**
|
|
* #820/20 - Add to an .asm export library (AsmLibrary) object
|
|
* a procedures with a specified calling convention.
|
|
*
|
|
* @param asmExportLibrary The AsmLibrary object of the .asm export library.
|
|
* @param callingConvention The CallingConvention, which is either __stackcall or __varcall.
|
|
* @param procedureName The procedure to be added.
|
|
*/
|
|
public void addAsmExportProcedure(AsmExportLibrary asmExportLibrary, Procedure.CallingConvention callingConvention, String procedureName) {
|
|
this.asmExportLibraries.get(asmExportLibrary.getAsmLibraryName()).addSymbol(procedureName, callingConvention);
|
|
this.getLog().append("Procedure " + procedureName + " exported to .asm library " + asmExportLibrary.getAsmLibraryName() );
|
|
}
|
|
|
|
/**
|
|
* #820/20 - Add to an .asm export library (AsmLibrary) object
|
|
* a list of procedures with a specified calling convention.
|
|
*
|
|
* @param asmExportLibrary The AsmLibrary object of the .asm export library.
|
|
* @param callingConvention The CallingConvention, which is either __stackcall or __varcall.
|
|
* @param procedures The list of procedures to be added.
|
|
*/
|
|
public void addAsmExportProcedures(AsmExportLibrary asmExportLibrary, Procedure.CallingConvention callingConvention, List<String> procedures) {
|
|
for(String procedureName : procedures) {
|
|
addAsmExportProcedure(asmExportLibrary, callingConvention, procedureName);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* #820/20 - Evaluate if the procedure is an .asm exported library.
|
|
*
|
|
* @param procedureName The name of the procedure to be evaluated.
|
|
* @return true if the procedure is part of an .asm export library.
|
|
*/
|
|
public boolean isProcedureAsmExport(String procedureName) {
|
|
Map<String, AsmExportLibrary> asmExports = this.getAsmExportLibraries();
|
|
for (String asmExportKey : asmExports.keySet()) {
|
|
AsmExportLibrary library = asmExports.get(asmExportKey);
|
|
if (library.hasSymbol(procedureName)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 820/20 - Retrieve the .asm export library (AsmLibrary) object from a given Symbol object.
|
|
*
|
|
* @param symbol The Symbol object.
|
|
* @return The AsmLibrary object that is an .asm export library.
|
|
*/
|
|
public AsmExportLibrary getAsmExportLibraryFromSymbol(Symbol symbol) {
|
|
Map<String, AsmExportLibrary> asmExports = this.getAsmExportLibraries();
|
|
for (String asmExportKey : asmExports.keySet()) {
|
|
AsmExportLibrary library = asmExports.get(asmExportKey);
|
|
if(symbol instanceof Procedure procedure) {
|
|
if (library.isExportAll((Procedure) procedure) || library.hasSymbol(procedure.getFullName())) {
|
|
return library;
|
|
}
|
|
}
|
|
if(symbol instanceof Variable variable) {
|
|
if (library.hasSymbol(variable.getFullName())) {
|
|
return library;
|
|
}
|
|
}
|
|
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
/**
|
|
* #820/20 - Configure the Procedure object as an .asm import library,
|
|
* and set the relevant properties!
|
|
*
|
|
* @param procedure The Procedure object to be configured.
|
|
* @param asmExportLibrary The .asm export library.
|
|
*
|
|
* TODO: Inlined and Intrinsic procedures!
|
|
*/
|
|
public void setProcedureAsAsmExport(Procedure procedure, AsmExportLibrary asmExportLibrary) {
|
|
// Here we check if the procedure declaration is already included in the asm export.
|
|
// If yes, the procedure should:,
|
|
// - have a stackcall or varcall calling convention
|
|
// - be external
|
|
// - not inlined
|
|
// - have a library name tagged for further processing of the namespace and asm inclusion etc.
|
|
procedure.setCallingConvention(asmExportLibrary.getProcedureCallingConvention(procedure.getFullName()));
|
|
procedure.setAsmExportLibrary(asmExportLibrary.getAsmLibraryName().toString());
|
|
procedure.setDeclaredExtern(false);
|
|
procedure.setDeclaredInline(false);
|
|
}
|
|
|
|
public boolean isAsmLibraryNamespace() {
|
|
return asmLibraryNamespace;
|
|
}
|
|
|
|
public void setAsmLibraryNamespace(boolean asmLibraryNamespace) {
|
|
this.asmLibraryNamespace = asmLibraryNamespace;
|
|
}
|
|
|
|
public HashMap<String, Symbol> getAsmLibraryNamespaceSymbols() {
|
|
return asmLibraryNamespaceSymbols;
|
|
}
|
|
|
|
public void setAsmLibraryNamespaceSymbols(HashMap<String, Symbol> asmLibraryNamespaceSymbols) {
|
|
this.asmLibraryNamespaceSymbols = asmLibraryNamespaceSymbols;
|
|
}
|
|
|
|
|
|
}
|