1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-05-30 10:41:36 +00:00
kickc/src/main/java/dk/camelot64/kickc/model/Program.java
Flight_Control 1a53d6a913 - Refer .asm library imported global variables to the the namespace where the global variable resides.
- 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.
2024-04-26 12:44:28 +03:00

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;
}
}