mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-06-09 18:29:36 +00:00
- Successful export and import of conio.
- TODO: Create header file for import. - TODO: Resolve coalesce of exported procedure parameters. - TODO: Remove constant optimizations for exported procedure parameters.
This commit is contained in:
parent
de6dad44db
commit
1209353803
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -18,4 +18,6 @@ kickc.iml
|
|||
**/.target/*
|
||||
*/workspace.xml
|
||||
/gen/*
|
||||
**/fragment/cache/*
|
||||
**/fragment/cache/*
|
||||
/src/test/kc/library/.target/
|
||||
/src/test/kc/library/lib/.target/
|
||||
|
|
|
@ -151,7 +151,7 @@ directive
|
|||
: CONST #directiveConst
|
||||
| ALIGN PAR_BEGIN NUMBER PAR_END #directiveAlign
|
||||
| REGISTER ( PAR_BEGIN ( NAME ) PAR_END)? #directiveRegister
|
||||
| ADDRESS_ZEROPAGE #directiveMemoryAreaZp
|
||||
| ADDRESS_ZEROPAGE ( PAR_BEGIN ( NUMBER ) PAR_END )? #directiveMemoryAreaZp
|
||||
| ADDRESS_MAINMEM #directiveMemoryAreaMain
|
||||
| ADDRESS PAR_BEGIN ( expr ) PAR_END #directiveMemoryAreaAddress
|
||||
| VOLATILE #directiveVolatile
|
||||
|
|
1
src/main/fragment/mos6502-common/call_stack_near.asm
Normal file
1
src/main/fragment/mos6502-common/call_stack_near.asm
Normal file
|
@ -0,0 +1 @@
|
|||
jsr {la1}
|
1
src/main/fragment/mos6502-common/call_var_near.asm
Normal file
1
src/main/fragment/mos6502-common/call_var_near.asm
Normal file
|
@ -0,0 +1 @@
|
|||
jsr {la1}
|
|
@ -27,6 +27,12 @@ public class CompileLog {
|
|||
*/
|
||||
private boolean verboseAsmOptimize = false;
|
||||
|
||||
/**
|
||||
* Should ASM output be verbose.
|
||||
*/
|
||||
|
||||
private boolean verboseAsm = true;
|
||||
|
||||
/**
|
||||
* Should SSA optimization be verbose.
|
||||
*/
|
||||
|
@ -273,6 +279,10 @@ public class CompileLog {
|
|||
return this;
|
||||
}
|
||||
|
||||
public boolean isVerboseAsm() { return verboseAsm; }
|
||||
|
||||
public void setVerboseAsm(boolean verboseAsm) { this.verboseAsm = verboseAsm; }
|
||||
|
||||
public boolean isVerboseAsmOptimize() {
|
||||
return verboseAsmOptimize;
|
||||
}
|
||||
|
|
|
@ -249,7 +249,9 @@ public class Compiler {
|
|||
new Pass1AssertNoLValueIntermediate(program).execute();
|
||||
new PassNAddTypeConversionAssignment(program, true).execute();
|
||||
new Pass1AddressOfHandling(program).execute();
|
||||
|
||||
new Pass1AsmUsesHandling(program).execute();
|
||||
|
||||
new Pass1AssertProcedureCallParameters(program).execute();
|
||||
new Pass1ModifiedVarsAnalysis(program).execute();
|
||||
new Pass1CallStackVarPrepare(program).execute();
|
||||
|
@ -747,8 +749,10 @@ public class Compiler {
|
|||
|
||||
private void pass5GenerateAndOptimizeAsm() {
|
||||
|
||||
getLog().append("\nASSEMBLER BEFORE OPTIMIZATION");
|
||||
getLog().append(program.getAsm().toString(new AsmProgram.AsmPrintState(true), program));
|
||||
if(getLog().isVerboseAsm()) {
|
||||
getLog().append("\nASSEMBLER BEFORE OPTIMIZATION");
|
||||
getLog().append(program.getAsm().toString(new AsmProgram.AsmPrintState(true), program));
|
||||
}
|
||||
|
||||
getLog().append("ASSEMBLER OPTIMIZATIONS");
|
||||
List<Pass5AsmOptimization> pass5Optimizations = new ArrayList<>();
|
||||
|
@ -769,8 +773,10 @@ public class Compiler {
|
|||
getLog().append("Succesful ASM optimization " + optimization.getClass().getSimpleName());
|
||||
asmOptimized = true;
|
||||
if(getLog().isVerboseAsmOptimize()) {
|
||||
getLog().append("ASSEMBLER");
|
||||
getLog().append(program.getAsm().toString());
|
||||
if(getLog().isVerboseAsm()) {
|
||||
getLog().append("ASSEMBLER");
|
||||
getLog().append(program.getAsm().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -786,9 +792,11 @@ public class Compiler {
|
|||
getLog().append("\nFINAL SYMBOL TABLE");
|
||||
getLog().append(program.getScope().toStringVars(program, false, false));
|
||||
|
||||
getLog().append("\nFINAL ASSEMBLER");
|
||||
getLog().append("Score: " + Pass4RegisterUpliftCombinations.getAsmScore(program) + "\n");
|
||||
getLog().append(program.getAsm().toString(new AsmProgram.AsmPrintState(false, true, true, false), program));
|
||||
if(getLog().isVerboseAsm()) {
|
||||
getLog().append("\nFINAL ASSEMBLER");
|
||||
getLog().append("Score: " + Pass4RegisterUpliftCombinations.getAsmScore(program) + "\n");
|
||||
getLog().append(program.getAsm().toString(new AsmProgram.AsmPrintState(false, true, true, false), program));
|
||||
}
|
||||
|
||||
getLog().append("\nZERO PAGE TABLE");
|
||||
getLog().append(program.getScope().toStringVars(program, true, true));
|
||||
|
|
|
@ -114,6 +114,9 @@ public class KickC implements Callable<Integer> {
|
|||
@CommandLine.Option(names = {"-v"}, description = "Verbose output describing the compilation process")
|
||||
private boolean verbose = false;
|
||||
|
||||
@CommandLine.Option(names = {"-vnoasm"}, description = "Verbosity Option. Don't show any generated assembler during compilation.")
|
||||
private boolean verboseAsm = false;
|
||||
|
||||
@CommandLine.Option(names = {"-vasmout"}, description = "Verbosity Option. Show KickAssembler standard output during compilation.")
|
||||
private boolean verboseAsmOut = false;
|
||||
|
||||
|
@ -585,6 +588,10 @@ public class KickC implements Callable<Integer> {
|
|||
compiler.getLog().setVerboseAsmOptimize(true);
|
||||
compiler.getLog().setSysOut(true);
|
||||
}
|
||||
if(verboseAsm) {
|
||||
compiler.getLog().setVerboseAsm(false);
|
||||
compiler.getLog().setSysOut(true);
|
||||
}
|
||||
if(verboseFixLongBranch) {
|
||||
compiler.getLog().setVerboseFixLongBranch(true);
|
||||
compiler.getLog().setSysOut(true);
|
||||
|
|
|
@ -287,13 +287,17 @@ public class AsmFormat {
|
|||
public static String getAsmSymbolName(Program program, Symbol symbol, ScopeRef codeScopeRef) {
|
||||
ScopeRef symbolScopeRef = symbol.getScope().getRef();
|
||||
String asmName = symbol.getLocalName();
|
||||
if(symbol instanceof Variable && ((Variable) symbol).getAsmName() != null) {
|
||||
asmName = ((Variable) symbol).getAsmName();
|
||||
String asmLibrary = "";
|
||||
if(symbol instanceof Variable boundVariable) {
|
||||
if (boundVariable.getAsmName() != null) {
|
||||
asmName = boundVariable.getAsmName();
|
||||
asmLibrary = boundVariable.getAsmLibrary();
|
||||
}
|
||||
}
|
||||
if(!symbolScopeRef.equals(codeScopeRef)) {
|
||||
if(symbolScopeRef.getFullName().length() > 0)
|
||||
if(!symbolScopeRef.getFullName().isEmpty())
|
||||
// Reference to symbol in another scope
|
||||
return asmFix2(symbolScopeRef.getFullName() + "." + asmName, symbolScopeRef.getFullName());
|
||||
return asmFix2(asmLibrary + symbolScopeRef.getFullName() + "." + asmName, symbolScopeRef.getFullName());
|
||||
else {
|
||||
// Check if the local code scope has a symbol with the same name
|
||||
final Scope codeScope = program.getScope().getScope(codeScopeRef);
|
||||
|
@ -303,7 +307,7 @@ public class AsmFormat {
|
|||
else
|
||||
// Implicit reference to global symbol
|
||||
if (symbol instanceof Procedure) {
|
||||
String namespace = ((Procedure)symbol).getLibrary();
|
||||
String namespace = ((Procedure)symbol).getAsmLibrary();
|
||||
if (namespace != null)
|
||||
asmName = namespace + "." + asmName;
|
||||
}
|
||||
|
@ -312,7 +316,7 @@ public class AsmFormat {
|
|||
} else {
|
||||
// Reference to local symbol
|
||||
if (symbol instanceof Procedure) {
|
||||
String namespace = ((Procedure)symbol).getLibrary();
|
||||
String namespace = ((Procedure)symbol).getAsmLibrary();
|
||||
if (namespace != null)
|
||||
asmName = namespace + "." + asmName;
|
||||
}
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
package dk.camelot64.kickc.asm;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
/** Define a KickAss library that is imported. */
|
||||
public class AsmImport extends AsmLine {
|
||||
|
||||
public String getFullName() {
|
||||
return name;
|
||||
}
|
||||
private final String name;
|
||||
|
||||
public Path getPath() {
|
||||
return resource;
|
||||
}
|
||||
private final Path resource;
|
||||
|
||||
|
||||
private final Set<String> procedures;
|
||||
|
||||
public AsmImport(String name, Path resource, Set<String> procedures) {
|
||||
this.name = name;
|
||||
this.resource = resource;
|
||||
this.procedures = procedures;
|
||||
}
|
||||
|
||||
public AsmImport(String name, Path resource) {
|
||||
this(name, resource, new HashSet<>());
|
||||
}
|
||||
|
||||
public AsmImport addProcedure(String procedureName) {
|
||||
procedures.add(procedureName);
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasProcedure(String procedureName) {
|
||||
return procedures.contains(procedureName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLineBytes() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLineCycles() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsm() {
|
||||
StringBuilder asm = new StringBuilder();
|
||||
asm.append(".library ").append(" [ ");
|
||||
asm.append("name=\"").append(name).append("\"");
|
||||
for(String procedureName : procedures) {
|
||||
asm.append(",");
|
||||
asm.append(procedureName);
|
||||
}
|
||||
asm.append(" ]");
|
||||
return asm.toString();
|
||||
}
|
||||
|
||||
}
|
73
src/main/java/dk/camelot64/kickc/asm/AsmLibrary.java
Normal file
73
src/main/java/dk/camelot64/kickc/asm/AsmLibrary.java
Normal file
|
@ -0,0 +1,73 @@
|
|||
package dk.camelot64.kickc.asm;
|
||||
|
||||
import dk.camelot64.kickc.model.symbols.Procedure;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
/** Define a KickAss library that is exported. */
|
||||
public class AsmLibrary extends AsmLine {
|
||||
|
||||
public String getFullName() {
|
||||
return name;
|
||||
}
|
||||
private final String name;
|
||||
|
||||
public Path getPath() {
|
||||
return resource;
|
||||
}
|
||||
private final Path resource;
|
||||
|
||||
|
||||
private final HashMap<String, Procedure.CallingConvention> procedures;
|
||||
|
||||
public AsmLibrary(String name, Path resource, HashMap<String, Procedure.CallingConvention> procedures) {
|
||||
this.name = name;
|
||||
this.resource = resource;
|
||||
this.procedures = procedures;
|
||||
}
|
||||
|
||||
public AsmLibrary(String name, Path resource) {
|
||||
this(name, resource, new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
public Set<String> getProcedures() {
|
||||
return this.procedures.keySet();
|
||||
}
|
||||
public AsmLibrary addProcedure(String procedureName, Procedure.CallingConvention callingConvention) {
|
||||
procedures.put(procedureName, callingConvention);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Procedure.CallingConvention getProcedureCallingConvention(String procedureName) {
|
||||
return procedures.get(procedureName);
|
||||
}
|
||||
|
||||
public boolean hasProcedure(String procedureName) {
|
||||
return procedures.containsKey(procedureName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLineBytes() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLineCycles() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsm() {
|
||||
StringBuilder asm = new StringBuilder();
|
||||
asm.append(".export ").append(" [ ");
|
||||
asm.append("name=\"").append(name).append("\"");
|
||||
for(String procedureName : procedures.keySet()) {
|
||||
asm.append(",");
|
||||
asm.append(procedureName);
|
||||
}
|
||||
asm.append(" ]");
|
||||
return asm.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -57,7 +57,7 @@ final public class AsmFragmentInstanceSpecBuilder {
|
|||
(callingDistance.equals(Bank.CallingDistance.NEAR)?null:toBank.bankArea()));
|
||||
ScopeRef codeScope = program.getScope().getRef();
|
||||
bindings.bind("c1", new ConstantInteger(toBank.bankNumber()));
|
||||
bindings.bind("la1", new LabelRef(((toProcedure.getLibrary() != null)?toProcedure.getLibrary().toLowerCase() + ".":"") + toProcedure.getFullName()));
|
||||
bindings.bind("la1", new LabelRef(((toProcedure.getAsmLibrary() != null)?toProcedure.getAsmLibrary().toLowerCase() + ".":"") + toProcedure.getFullName()));
|
||||
return new AsmFragmentInstanceSpec(program, signature, bindings, codeScope);
|
||||
}
|
||||
|
||||
|
|
|
@ -94,7 +94,13 @@ public class Directive {
|
|||
|
||||
/** Variable __zp */
|
||||
public static class MemZp extends Directive {
|
||||
public MemZp() { super("__zp"); }
|
||||
public Integer zp;
|
||||
public MemZp(Integer zp) {
|
||||
super("__zp");
|
||||
this.zp = zp;
|
||||
}
|
||||
|
||||
public MemZp() {super( "__zp");}
|
||||
}
|
||||
|
||||
/** Variable __mem */
|
||||
|
|
|
@ -61,7 +61,7 @@ public class LiveRangeEquivalenceClassSet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Consolidates two live range equivalence calsses into one.
|
||||
* Consolidates two live range equivalence classes into one.
|
||||
* All variables and live ranges from the other class is added to the first one - and the other one is deleted.
|
||||
*
|
||||
* @param equivalenceClass The first live range equivalence class.
|
||||
|
|
|
@ -2,7 +2,7 @@ package dk.camelot64.kickc.model;
|
|||
|
||||
import dk.camelot64.kickc.CompileLog;
|
||||
import dk.camelot64.kickc.OutputFileManager;
|
||||
import dk.camelot64.kickc.asm.AsmImport;
|
||||
import dk.camelot64.kickc.asm.AsmLibrary;
|
||||
import dk.camelot64.kickc.asm.AsmProgram;
|
||||
import dk.camelot64.kickc.fragment.synthesis.AsmFragmentTemplateMasterSynthesizer;
|
||||
import dk.camelot64.kickc.model.statements.StatementPhiBlock;
|
||||
|
@ -110,9 +110,14 @@ public class Program {
|
|||
private String asmLibrary;
|
||||
|
||||
/**
|
||||
* Defines the asm source to be imported with extern __stackcall functions.
|
||||
* Defines the asm source to be imported with the defined procedures.
|
||||
*/
|
||||
private Map<String, AsmImport> asmImports;
|
||||
private Map<String, AsmLibrary> asmImports;
|
||||
|
||||
/**
|
||||
* Defines the asm source to be exported with the defined procedures.
|
||||
*/
|
||||
private Map<String, AsmLibrary> asmExports;
|
||||
|
||||
/** 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() {
|
||||
|
@ -127,6 +132,7 @@ public class Program {
|
|||
this.reservedZps = new ArrayList<>();
|
||||
this.procedureCompilations = new ArrayList<>();
|
||||
this.asmImports = new LinkedHashMap<>();
|
||||
this.asmExports = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -195,7 +201,11 @@ public class Program {
|
|||
public String prettyControlFlowGraph() {
|
||||
StringBuilder graphPretty = new StringBuilder();
|
||||
for(ProcedureCompilation procedureCompilation : getProcedureCompilations()) {
|
||||
graphPretty.append(procedureCompilation.getGraph().toString(this));
|
||||
if(procedureCompilation.getGraph() != null) {
|
||||
graphPretty.append(procedureCompilation.getGraph().toString(this));
|
||||
} else {
|
||||
graphPretty.append(procedureCompilation.toString()).append(": null");
|
||||
}
|
||||
}
|
||||
return graphPretty.toString();
|
||||
}
|
||||
|
@ -617,33 +627,34 @@ public class Program {
|
|||
return entryPointBlocks;
|
||||
}
|
||||
|
||||
public Map<String, AsmImport> getAsmImports() {
|
||||
public Map<String, AsmLibrary> getAsmImports() {
|
||||
return asmImports;
|
||||
}
|
||||
|
||||
public void setAsmImports(Map<String, AsmImport> asmImports) {
|
||||
public void setAsmImports(Map<String, AsmLibrary> asmImports) {
|
||||
this.asmImports = asmImports;
|
||||
}
|
||||
|
||||
public void addAsmImportProcedures(String asmImport, List<String> procedures) {
|
||||
|
||||
public void addAsmImportProcedures(String asmImport, Procedure.CallingConvention callingConvention, List<String> procedures) {
|
||||
Path asmImportResource = Paths.get(asmImport + ".asm").toAbsolutePath();
|
||||
String asmImportName = Paths.get(asmImport).getFileName().toString();
|
||||
|
||||
AsmImport library = this.asmImports.get(asmImportName);
|
||||
AsmLibrary library = this.asmImports.get(asmImportName);
|
||||
|
||||
if(library == null) {
|
||||
this.asmImports.put(asmImportName, new AsmImport(asmImportName, asmImportResource));
|
||||
this.asmImports.put(asmImportName, new AsmLibrary(asmImportName, asmImportResource));
|
||||
this.addAsmResourceFile(asmImportResource);
|
||||
}
|
||||
for(String procedureName : procedures) {
|
||||
this.asmImports.get(asmImportName).addProcedure(procedureName);
|
||||
this.asmImports.get(asmImportName).addProcedure(procedureName, callingConvention);
|
||||
}
|
||||
}
|
||||
|
||||
public AsmImport isProcedureInAsmImport(String procedureName) {
|
||||
Map<String, AsmImport> asmImports = this.getAsmImports();
|
||||
public AsmLibrary isProcedureInAsmImport(String procedureName) {
|
||||
Map<String, AsmLibrary> asmImports = this.getAsmImports();
|
||||
for (String asmImportKey : asmImports.keySet()) {
|
||||
AsmImport library = asmImports.get(asmImportKey);
|
||||
AsmLibrary library = asmImports.get(asmImportKey);
|
||||
if (library.hasProcedure(procedureName)) {
|
||||
return library;
|
||||
}
|
||||
|
@ -651,16 +662,70 @@ public class Program {
|
|||
return null;
|
||||
}
|
||||
|
||||
public void setProcedureAsAsmImport(Procedure procedure, AsmImport asmImport) {
|
||||
public void setProcedureAsAsmImport(Procedure procedure, AsmLibrary 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(Procedure.CallingConvention.STACK_CALL);
|
||||
procedure.setLibrary(asmImport.getFullName());
|
||||
procedure.setCallingConvention(asmImport.getProcedureCallingConvention(procedure.getFullName()));
|
||||
procedure.setAsmLibrary(asmImport.getFullName());
|
||||
procedure.setDeclaredExtern(true);
|
||||
procedure.setDeclaredInline(false);
|
||||
}
|
||||
public Map<String, AsmLibrary> getAsmExports() {
|
||||
return asmExports;
|
||||
}
|
||||
|
||||
public void addAsmExportProcedures(String asmExport, Procedure.CallingConvention callingConvention, List<String> procedures) {
|
||||
Path asmExportResource = Paths.get(asmExport + ".asm").toAbsolutePath();
|
||||
String asmExportName = Paths.get(asmExport).getFileName().toString();
|
||||
|
||||
AsmLibrary library = this.asmExports.get(asmExportName);
|
||||
|
||||
if(library == null) {
|
||||
this.asmExports.put(asmExportName, new AsmLibrary(asmExportName, asmExportResource));
|
||||
// this.addAsmResourceFile(asmExportResource); // Not yet, only when designing the multiple exports from one source file.
|
||||
}
|
||||
for(String procedureName : procedures) {
|
||||
this.asmExports.get(asmExportName).addProcedure(procedureName, callingConvention);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isProcedureAsmExport(String procedureName) {
|
||||
Map<String, AsmLibrary> asmExports = this.getAsmExports();
|
||||
for (String asmExportKey : asmExports.keySet()) {
|
||||
AsmLibrary library = asmExports.get(asmExportKey);
|
||||
if (library.hasProcedure(procedureName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public AsmLibrary getAsmExportLibrary(Procedure procedure) {
|
||||
Map<String, AsmLibrary> asmExports = this.getAsmExports();
|
||||
for (String asmExportKey : asmExports.keySet()) {
|
||||
AsmLibrary library = asmExports.get(asmExportKey);
|
||||
if (library.hasProcedure(procedure.getFullName())) {
|
||||
return library;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setProcedureAsAsmExport(Procedure procedure, AsmLibrary asmLibrary) {
|
||||
// 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(asmLibrary.getProcedureCallingConvention(procedure.getFullName()));
|
||||
procedure.setAsmLibrary(asmLibrary.getFullName());
|
||||
procedure.setDeclaredExtern(false);
|
||||
procedure.setDeclaredInline(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -199,7 +199,7 @@ public class Registers {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "zp[" + getBytes() + "]:" + getZp();
|
||||
return "zp[" + getBytes() + "]:" + String.format("%03d", getZp()) + String.format(" (0x%02x)", getZp());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -301,6 +301,15 @@ public class VariableBuilder {
|
|||
return hasDirective(Directive.Export.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Declared as extern (__extern keyword)
|
||||
*
|
||||
* @return true if declared as __extern
|
||||
*/
|
||||
public boolean isExtern() {
|
||||
return hasDirective(Directive.Extern.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Declared but not defined. ( "extern" keyword)
|
||||
*
|
||||
|
@ -461,6 +470,11 @@ public class VariableBuilder {
|
|||
return register;
|
||||
}
|
||||
|
||||
Directive.MemZp zpDirective = findDirective(Directive.MemZp.class, directives);
|
||||
if(zpDirective != null) {
|
||||
return new Registers.RegisterZpMem(zpDirective.zp, -1, true);
|
||||
}
|
||||
|
||||
// No hard-coded register
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package dk.camelot64.kickc.model.symbols;
|
||||
|
||||
import dk.camelot64.kickc.model.Comment;
|
||||
import dk.camelot64.kickc.model.Directive;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
import dk.camelot64.kickc.model.statements.StatementSource;
|
||||
import dk.camelot64.kickc.model.types.SymbolType;
|
||||
|
@ -46,15 +47,15 @@ public class Procedure extends Scope {
|
|||
/** The source of the procedure definition. */
|
||||
private StatementSource definitionSource;
|
||||
|
||||
/** The library where the function resides */
|
||||
private String library;
|
||||
/** The .asm library where the procedure resides in compiled form. */
|
||||
private String asmLibrary;
|
||||
|
||||
public String getLibrary() {
|
||||
return library;
|
||||
public String getAsmLibrary() {
|
||||
return asmLibrary;
|
||||
}
|
||||
|
||||
public void setLibrary(String library) {
|
||||
this.library = library;
|
||||
public void setAsmLibrary(String asmLibrary) {
|
||||
this.asmLibrary = asmLibrary;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -133,7 +134,7 @@ public class Procedure extends Scope {
|
|||
this.callingConvention = callingConvention;
|
||||
this.constructorRefs = new ArrayList<>();
|
||||
this.isConstructor = false;
|
||||
this.library = null;
|
||||
this.asmLibrary = null;
|
||||
}
|
||||
|
||||
public StatementSource getDefinitionSource() {
|
||||
|
@ -207,6 +208,10 @@ public class Procedure extends Scope {
|
|||
this.parameterNames = new ArrayList<>();
|
||||
for(Variable parameter : parameters) {
|
||||
parameterNames.add(parameter.getLocalName());
|
||||
if(parameter.getRegister() instanceof Directive.Register.MemZp) {
|
||||
if (((Directive.MemZp) parameter.getRegister()).zp != null)
|
||||
this.addReservedZp(((Directive.MemZp) parameter.getRegister()).zp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -274,6 +279,10 @@ public class Procedure extends Scope {
|
|||
this.reservedZps = reservedZps;
|
||||
}
|
||||
|
||||
public void addReservedZp(Integer reservedZp) {
|
||||
this.reservedZps.add(reservedZp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get references to all constructors needed for this procedure
|
||||
*
|
||||
|
|
|
@ -104,6 +104,18 @@ public class Variable implements Symbol {
|
|||
/** If the variable is a struct that is/should be unwound to variables for each member. */
|
||||
private boolean structUnwind;
|
||||
|
||||
|
||||
public void setAsmLibrary(String asmLibrary) {
|
||||
this.asmLibrary = asmLibrary;
|
||||
}
|
||||
|
||||
public String getAsmLibrary() {
|
||||
return asmLibrary != null ? asmLibrary + "." : "";
|
||||
}
|
||||
|
||||
/** The imported asm library where the variable is declared for specific cases */
|
||||
private String asmLibrary;
|
||||
|
||||
/**
|
||||
* Create a variable (or constant)
|
||||
*
|
||||
|
|
|
@ -127,6 +127,7 @@ public class CParser {
|
|||
public static final String PRAGMA_ASM_LIBRARY = "asm_library";
|
||||
|
||||
public static final String PRAGMA_ASM_IMPORT = "asm_import";
|
||||
public static final String PRAGMA_ASM_EXPORT = "asm_export";
|
||||
|
||||
/**
|
||||
* The Program.
|
||||
|
|
|
@ -3,7 +3,7 @@ package dk.camelot64.kickc.passes;
|
|||
import dk.camelot64.cpufamily6502.CpuClobber;
|
||||
import dk.camelot64.kickc.NumberParser;
|
||||
import dk.camelot64.kickc.SourceLoader;
|
||||
import dk.camelot64.kickc.asm.AsmImport;
|
||||
import dk.camelot64.kickc.asm.AsmLibrary;
|
||||
import dk.camelot64.kickc.model.InternalError;
|
||||
import dk.camelot64.kickc.model.*;
|
||||
import dk.camelot64.kickc.model.operators.*;
|
||||
|
@ -324,14 +324,22 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
|
|||
return null;
|
||||
}
|
||||
case CParser.PRAGMA_ASM_LIBRARY -> { // Defines that the result is an asm routine or capability library instead of a program.
|
||||
this.asmLibrary = pragmaParamName(pragmaParamSingle(ctx));
|
||||
this.asmLibrary = pragmaParamString(pragmaParamSingle(ctx));
|
||||
program.setAsmLibrary(this.asmLibrary);
|
||||
}
|
||||
case CParser.PRAGMA_ASM_EXPORT -> { // Defines that an C routine is exported into the asm_library.
|
||||
String libraryName = pragmaParamString(pragmaParamFirst(ctx));
|
||||
Procedure.CallingConvention callingConvention = pragmaParamCallingConvention(ctx.pragmaParam(1));
|
||||
List<String> procedures = pragmaParamAsmExportProcedures(ctx.pragmaParam());
|
||||
|
||||
program.addAsmExportProcedures(libraryName, callingConvention, procedures);
|
||||
}
|
||||
case CParser.PRAGMA_ASM_IMPORT -> { // Defines that an asm routine or capability library is imported into the program.
|
||||
String libraryName = pragmaParamString(pragmaParamFirst(ctx));
|
||||
List<String> procedures = pragmaParamAsmImportProcedures(ctx.pragmaParam(), libraryName);
|
||||
Procedure.CallingConvention callingConvention = pragmaParamCallingConvention(ctx.pragmaParam(1));
|
||||
List<String> procedures = pragmaParamAsmImportProcedures(ctx.pragmaParam());
|
||||
|
||||
program.addAsmImportProcedures(libraryName, procedures);
|
||||
program.addAsmImportProcedures(libraryName, callingConvention, procedures);
|
||||
}
|
||||
default -> program.getLog().append("Warning! Unknown #pragma " + pragmaName);
|
||||
}
|
||||
|
@ -461,9 +469,30 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
|
|||
* @param libraryFunctionParams The library function params
|
||||
* @return The list of parsed function parameters
|
||||
*/
|
||||
private List<String> pragmaParamAsmImportProcedures(List<KickCParser.PragmaParamContext> libraryParams, String libraryName) {
|
||||
private List<String> pragmaParamAsmImportProcedures(List<KickCParser.PragmaParamContext> libraryParams) {
|
||||
List<String> procedures = new ArrayList<>();
|
||||
for(KickCParser.PragmaParamContext reserveCtx : libraryParams.subList(1, libraryParams.size())) {
|
||||
for(KickCParser.PragmaParamContext reserveCtx : libraryParams.subList(2, libraryParams.size())) {
|
||||
if(reserveCtx instanceof KickCParser.PragmaParamNameContext) {
|
||||
final TerminalNode name = ((KickCParser.PragmaParamNameContext) reserveCtx).NAME();
|
||||
// Only a single reserved address
|
||||
String procedureName = name.getText();
|
||||
procedures.add(procedureName);
|
||||
} else {
|
||||
throw new CompileError("Expected a NAME parameter. Found '" + reserveCtx.getText() + "'.", new StatementSource(reserveCtx.getParent()));
|
||||
}
|
||||
}
|
||||
return procedures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse all the function names from the library export function list, delimited by comma.
|
||||
*
|
||||
* @param libraryParams The library function params
|
||||
* @return The list of parsed function parameters
|
||||
*/
|
||||
private List<String> pragmaParamAsmExportProcedures(List<KickCParser.PragmaParamContext> libraryParams) {
|
||||
List<String> procedures = new ArrayList<>();
|
||||
for(KickCParser.PragmaParamContext reserveCtx : libraryParams.subList(2, libraryParams.size())) {
|
||||
if(reserveCtx instanceof KickCParser.PragmaParamNameContext) {
|
||||
final TerminalNode name = ((KickCParser.PragmaParamNameContext) reserveCtx).NAME();
|
||||
// Only a single reserved address
|
||||
|
@ -503,7 +532,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
|
|||
|
||||
String procedureName = varDecl.getVarName();
|
||||
Procedure procDeclared = (Procedure)program.getScope().getSymbol(new SymbolRef(procedureName));
|
||||
AsmImport asmImport = this.program.isProcedureInAsmImport(procedureName);
|
||||
AsmLibrary asmImport = this.program.isProcedureInAsmImport(procedureName);
|
||||
|
||||
// We skip the procedure definition if:
|
||||
// - it is already defined in an asm library.
|
||||
|
@ -580,14 +609,21 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
|
|||
|
||||
procedure = (Procedure) existingSymbol;
|
||||
} else {
|
||||
AsmImport asmImport = this.program.isProcedureInAsmImport(procedure.getFullName());
|
||||
AsmLibrary asmImport = this.program.isProcedureInAsmImport(procedure.getFullName());
|
||||
if(asmImport != null)
|
||||
this.program.setProcedureAsAsmImport(procedure, asmImport);
|
||||
program.getScope().add(procedure);
|
||||
program.createProcedureCompilation(procedure.getRef());
|
||||
}
|
||||
|
||||
if(procedure.isDeclaredExtern() && !defineProcedure) {
|
||||
// TODO: remove the library dependency and rework to one routine call.
|
||||
AsmLibrary asmExport = this.program.getAsmExportLibrary(procedure);
|
||||
if(asmExport != null)
|
||||
this.program.setProcedureAsAsmExport(procedure, asmExport);
|
||||
|
||||
|
||||
// if(procedure.isDeclaredExtern() && !defineProcedure) {
|
||||
if(procedure.isDeclaredExtern() && !defineProcedure && existingSymbol == null) {
|
||||
|
||||
/** This is an almost exact copy of a procedure definition.
|
||||
* When a procedure is defined external, the required control blocks are to be allocated
|
||||
|
@ -627,7 +663,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
|
|||
if (parameter.name == null)
|
||||
throw new CompileError("Illegal unnamed parameter.", statementSource);
|
||||
|
||||
VariableBuilder varBuilder = new VariableBuilder(parameter.name, getCurrentScope(), true, false, parameter.type, null, currentSegmentData, program.getTargetPlatform().getVariableBuilderConfig());
|
||||
VariableBuilder varBuilder = new VariableBuilder(parameter.name, getCurrentScope(), true, false, parameter.type, parameter.directives, currentSegmentData, program.getTargetPlatform().getVariableBuilderConfig());
|
||||
final Variable paramVar = varBuilder.build();
|
||||
parameterList.add(paramVar);
|
||||
}
|
||||
|
@ -730,7 +766,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
|
|||
public Object visitParameterDeclTypeDeclarator(KickCParser.ParameterDeclTypeDeclaratorContext ctx) {
|
||||
this.visit(ctx.declType());
|
||||
this.visit(ctx.declarator());
|
||||
ParameterDecl paramDecl = new ParameterDecl(varDecl.getVarName(), varDecl.getEffectiveType());
|
||||
ParameterDecl paramDecl = new ParameterDecl(varDecl.getVarName(), varDecl.getEffectiveType(), varDecl.getDeclDirectives());
|
||||
varDecl.exitType();
|
||||
return paramDecl;
|
||||
}
|
||||
|
@ -738,12 +774,12 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
|
|||
@Override
|
||||
public Object visitParameterDeclTypeName(KickCParser.ParameterDeclTypeNameContext ctx) {
|
||||
SymbolType paramType = (SymbolType) this.visit(ctx.typeName());
|
||||
return new ParameterDecl(null, paramType);
|
||||
return new ParameterDecl(null, paramType, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitParameterDeclList(KickCParser.ParameterDeclListContext ctx) {
|
||||
return new ParameterDecl(null, SymbolType.PARAM_LIST);
|
||||
return new ParameterDecl(null, SymbolType.PARAM_LIST, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -976,10 +1012,12 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
|
|||
static class ParameterDecl {
|
||||
final public String name;
|
||||
final public SymbolType type;
|
||||
final public List<Directive> directives;
|
||||
|
||||
public ParameterDecl(String name, SymbolType type) {
|
||||
public ParameterDecl(String name, SymbolType type, List<Directive> directives) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.directives = directives;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1339,7 +1377,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
|
|||
Bank bank = new Bank(directiveBank.getBankArea(), directiveBank.getBankNumber());
|
||||
procedure.setBank(bank);
|
||||
} else if(directive instanceof Directive.Library) {
|
||||
procedure.setLibrary(((Directive.Library) directive).getLibrary());
|
||||
procedure.setAsmLibrary(((Directive.Library) directive).getLibrary());
|
||||
} else if(directive instanceof Directive.CallingConvention) {
|
||||
procedure.setCallingConvention(((Directive.CallingConvention) directive).callingConvention);
|
||||
} else if(directive instanceof Directive.Interrupt) {
|
||||
|
@ -1439,7 +1477,16 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
|
|||
|
||||
@Override
|
||||
public Directive visitDirectiveMemoryAreaZp(KickCParser.DirectiveMemoryAreaZpContext ctx) {
|
||||
return new Directive.MemZp();
|
||||
String zpText = ctx.NUMBER().getText();
|
||||
if(zpText != null)
|
||||
try {
|
||||
Number zpNumber = NumberParser.parseLiteral(zpText);
|
||||
return new Directive.MemZp(zpNumber.intValue());
|
||||
} catch(NumberFormatException e) {
|
||||
throw new CompileError(e.getMessage(), new StatementSource(ctx));
|
||||
}
|
||||
else
|
||||
return new Directive.MemZp();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -75,8 +75,7 @@ public class Pass1AddressOfHandling extends Pass2SsaOptimization {
|
|||
//getLog().append("Setting inferred volatile on symbol affected by address-of: " + variable.toString() + " in " + stmtStr);
|
||||
}
|
||||
}
|
||||
} else if(toSymbol instanceof Procedure) {
|
||||
Procedure procedure = (Procedure) toSymbol;
|
||||
} else if(toSymbol instanceof Procedure procedure) {
|
||||
if(procedure.getParameters().size() > 0 || !SymbolType.VOID.equals(procedure.getReturnType())) {
|
||||
procedure.setCallingConvention(Procedure.CallingConvention.STACK_CALL);
|
||||
getLog().append("Setting inferred __stackcall on procedure affected by address-of " + toSymbol.toString(getProgram()) + " caused by statement " + stmtStr);
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package dk.camelot64.kickc.passes;
|
||||
|
||||
import dk.camelot64.kickc.model.Comment;
|
||||
import dk.camelot64.kickc.model.ControlFlowBlock;
|
||||
import dk.camelot64.kickc.model.Graph;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
import dk.camelot64.kickc.model.statements.*;
|
||||
import dk.camelot64.kickc.model.symbols.Procedure;
|
||||
|
@ -87,6 +85,7 @@ public class Pass1CallVar extends Pass2SsaOptimization {
|
|||
for(int i = 0; i < parameterDefs.size(); i++) {
|
||||
final RValue parameterVal = call.getParameters().get(i);
|
||||
final Variable parameterDef = parameterDefs.get(i);
|
||||
parameterDef.setAsmLibrary(procedure.getAsmLibrary());
|
||||
stmtIt.add(new StatementAssignment(parameterDef.getVariableRef(), parameterVal, false, source, comments));
|
||||
comments = Comment.NO_COMMENTS;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package dk.camelot64.kickc.passes;
|
|||
|
||||
import dk.camelot64.kickc.model.Graph;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
import dk.camelot64.kickc.model.statements.Statement;
|
||||
import dk.camelot64.kickc.model.statements.StatementCalling;
|
||||
import dk.camelot64.kickc.model.symbols.Procedure;
|
||||
import dk.camelot64.kickc.model.values.ProcedureRef;
|
||||
|
@ -28,12 +27,14 @@ public class Pass1EliminateUncalledProcedures extends Pass1Base {
|
|||
Collection<Procedure> allProcedures = getProgram().getScope().getAllProcedures(true);
|
||||
for(Procedure procedure : allProcedures)
|
||||
if(!ProcedureUtils.isEntrypoint(procedure.getRef(), getProgram()))
|
||||
if(!calledProcedures.contains(procedure.getRef()))
|
||||
// The procedure is not used - mark for removal!
|
||||
unusedProcedures.add(procedure.getRef());
|
||||
if(!this.getProgram().isProcedureAsmExport(procedure.getFullName()))
|
||||
if(!calledProcedures.contains(procedure.getRef()))
|
||||
// The procedure is not used - mark for removal!
|
||||
unusedProcedures.add(procedure.getRef());
|
||||
|
||||
for(ProcedureRef unusedProcedure : unusedProcedures) {
|
||||
removeProcedure(getProgram(), unusedProcedure);
|
||||
if(!this.getProgram().isProcedureAsmExport(unusedProcedure.getFullName()))
|
||||
removeProcedure(getProgram(), unusedProcedure);
|
||||
}
|
||||
|
||||
return unusedProcedures.size() > 0;
|
||||
|
|
|
@ -130,8 +130,16 @@ public class Pass2AliasElimination extends Pass2SsaOptimization {
|
|||
// Alias assignment
|
||||
VariableRef alias = (VariableRef) assignment.getrValue2();
|
||||
List<VarAssignments.VarAssignment> assignments = VarAssignments.get(alias, program.getGraph(), program.getScope());
|
||||
|
||||
/* TODO: Sven; Here I need to build the logic to evaluate that if a var has
|
||||
never been assigned, that it only accepts to continue if the procedure
|
||||
is exported.
|
||||
Instead of continue always, the throw exception is to be done when
|
||||
the assignment.size() == 0 and the procedure is exported.
|
||||
:throw new InternalError("Error! Var is never assigned! " + alias);:
|
||||
*/
|
||||
if(assignments.size() == 0)
|
||||
throw new InternalError("Error! Var is never assigned! " + alias);
|
||||
continue;
|
||||
else if(assignments.size() > 1)
|
||||
// Multiple assignments exist
|
||||
continue;
|
||||
|
|
|
@ -9,6 +9,7 @@ import dk.camelot64.kickc.model.operators.Operators;
|
|||
import dk.camelot64.kickc.model.statements.Statement;
|
||||
import dk.camelot64.kickc.model.statements.StatementAssignment;
|
||||
import dk.camelot64.kickc.model.statements.StatementPhiBlock;
|
||||
import dk.camelot64.kickc.model.symbols.Procedure;
|
||||
import dk.camelot64.kickc.model.symbols.ProgramScope;
|
||||
import dk.camelot64.kickc.model.symbols.Scope;
|
||||
import dk.camelot64.kickc.model.symbols.Variable;
|
||||
|
@ -49,6 +50,13 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
|
|||
SymbolType valueType = SymbolTypeInference.inferType(getProgramScope(), constVal);
|
||||
SymbolType variableType = variable.getType();
|
||||
|
||||
if(scope instanceof Procedure procedure) {
|
||||
if (getProgram().isProcedureAsmExport(procedure.getFullName()))
|
||||
if (procedure.getParameters().contains(variable))
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if(!SymbolType.NUMBER.equals(variableType) && SymbolType.NUMBER.equals(valueType)) {
|
||||
// Value is number - wait til it is cast to a proper type
|
||||
constants.remove(constRef);
|
||||
|
|
|
@ -200,12 +200,26 @@ public class Pass4CodeGeneration {
|
|||
asm.addNamespaceEnd();
|
||||
}
|
||||
|
||||
// Now we import each asm library imported through the #pragma import() statement.
|
||||
// In case of an asm library import, we generate the instructions to import the library.
|
||||
// TODO: Solve the need for the #define __asm_import__, to solve the .segmentdef conflict.
|
||||
for(String asmLibraryName : program.getAsmImports().keySet()) {
|
||||
asm.addComment("Asm library " + asmLibraryName + ":", false);
|
||||
asm.addLine( new AsmInlineKickAsm( "#define __asm_import__", null, null, null));
|
||||
asm.addLine( new AsmInlineKickAsm("#import \"" + asmLibraryName + ".asm\"", null, null, null));
|
||||
}
|
||||
|
||||
// In case of an asm library export, we generate the exported procedure prototypes,
|
||||
// to be imported into the C program. These prototypes set the register and zeropage usages.
|
||||
// TODO: I need to add an assert function to validate that each exported procedure is also declared and defined!
|
||||
for(AsmLibrary asmLibrary : program.getAsmExports().values()) {
|
||||
for(String procedureName : asmLibrary.getProcedures()) {
|
||||
Procedure procedure = getScope().getProcedure(new ProcedureRef(procedureName));
|
||||
if(procedure != null) {
|
||||
// Generate parameter information
|
||||
generateSignatureComments(asm, procedure);
|
||||
}
|
||||
}
|
||||
}
|
||||
program.setAsm(asm);
|
||||
}
|
||||
|
||||
|
@ -269,9 +283,8 @@ public class Pass4CodeGeneration {
|
|||
signature.append(param.getType().toCDecl(parameter.getLocalName()));
|
||||
}
|
||||
signature.append(")");
|
||||
if (i > 0) {
|
||||
asm.addComment(signature.toString(), false);
|
||||
}
|
||||
// Always add the signature comments...
|
||||
asm.addComment(signature.toString(), false);
|
||||
if(!procedure.getBank().isCommon()) {
|
||||
asm.addComment(" " + procedure.getBank(), false);
|
||||
}
|
||||
|
@ -918,12 +931,12 @@ public class Pass4CodeGeneration {
|
|||
} 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.getLibrary() == null) {
|
||||
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.getLibrary() + "." + call.getProcedure().getFullName(), false);
|
||||
asm.addInstruction("jsr", CpuAddressingMode.ABS, toProcedure.getAsmLibrary() + "." + call.getProcedure().getFullName(), false);
|
||||
asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL);
|
||||
}
|
||||
} else {
|
||||
|
@ -944,7 +957,7 @@ public class Pass4CodeGeneration {
|
|||
// if (toProcedure.isDeclaredBanked() && fromProcedure.getBank() != toProcedure.getBank()) {
|
||||
throw new CompileError("Stack Call procedure not supported in banked mode " + toProcedure.toString(program));
|
||||
} else {
|
||||
if (toProcedure.getLibrary() == null) {
|
||||
if (toProcedure.getAsmLibrary() == null) {
|
||||
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.call(call, indirectCallCount++, program), program);
|
||||
} else {
|
||||
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.callBanked(toProcedure, callingDistance, program), program);
|
||||
|
|
|
@ -4,6 +4,7 @@ import dk.camelot64.kickc.model.*;
|
|||
import dk.camelot64.kickc.model.statements.StatementAssignment;
|
||||
import dk.camelot64.kickc.model.statements.StatementCall;
|
||||
import dk.camelot64.kickc.model.statements.StatementCallFinalize;
|
||||
import dk.camelot64.kickc.model.symbols.Procedure;
|
||||
import dk.camelot64.kickc.model.symbols.StructDefinition;
|
||||
import dk.camelot64.kickc.model.symbols.Variable;
|
||||
import dk.camelot64.kickc.model.values.RValue;
|
||||
|
@ -58,6 +59,19 @@ public class Pass4LiveRangeEquivalenceClassesFinalize extends Pass2Base {
|
|||
EquivalenceClassAdder equivalenceClassAdder = new EquivalenceClassAdder(liveRangeEquivalenceClassSet);
|
||||
equivalenceClassAdder.visitGraph(getGraph());
|
||||
|
||||
// Add any load/store variables with an initializer - and load/store struct variables
|
||||
for(Variable variable : getSymbols().getAllVariables(true)) {
|
||||
if(variable.getScope() instanceof Procedure procedure) {
|
||||
// check if export
|
||||
if(getProgram().isProcedureAsmExport((procedure.getFullName()))) {
|
||||
List<Variable> params = procedure.getParameters();
|
||||
if(params.contains(variable)) {
|
||||
addToEquivalenceClassSet(variable.getVariableRef(), new ArrayList<>(), liveRangeEquivalenceClassSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add any load/store variables with an initializer - and load/store struct variables
|
||||
for(Variable variable : getSymbols().getAllVariables(true)) {
|
||||
if(variable.getScope() instanceof StructDefinition)
|
||||
|
|
|
@ -56,6 +56,7 @@ public abstract class Pass4MemoryCoalesce extends Pass2Base {
|
|||
canCoalesceSegments(ec1, ec2, program) &&
|
||||
canCoalesceVolatile(ec1, ec2, program) &&
|
||||
canCoalesceThreads(ec1, ec2, threadHeads, program) &&
|
||||
// canCoalesceExports(ec1, ec2, program) &&
|
||||
canCoalesceClobber(ec1, ec2, unknownFragments, program);
|
||||
}
|
||||
|
||||
|
@ -85,6 +86,8 @@ public abstract class Pass4MemoryCoalesce extends Pass2Base {
|
|||
return dataSegment1.equals(dataSegment2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Determines if two live range equivalence classes can be coalesced without cross-thread clobber.
|
||||
* This is possible if they are both only called from the same thread head.
|
||||
|
@ -105,7 +108,51 @@ public abstract class Pass4MemoryCoalesce extends Pass2Base {
|
|||
if(threads1.isEmpty() || threads2.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
return threads1.equals(threads2);
|
||||
|
||||
|
||||
Procedure proc1 = program.getScope().getProcedure((ProcedureRef)threads1.toArray()[0]);
|
||||
Procedure proc2 = program.getScope().getProcedure((ProcedureRef)threads2.toArray()[0]);
|
||||
if(program.isProcedureAsmExport(proc1.getFullName()) || program.isProcedureAsmExport(proc2.getFullName()) ) {
|
||||
return true;
|
||||
} else {
|
||||
return threads1.equals(threads2);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isExportParameter(VariableRef varRef, Program program) {
|
||||
Variable variable = program.getScope().getVariable(varRef);
|
||||
ScopeRef scopeRef = variable.getScope().getRef();
|
||||
if (scopeRef instanceof ProcedureRef) {
|
||||
if (program.isProcedureAsmExport(scopeRef.getFullName())) {
|
||||
Procedure procedure = program.getScope().getProcedure((ProcedureRef)scopeRef);
|
||||
if(procedure.getParameters().contains(variable))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if two live range equivalence classes can be coalesced when exported.
|
||||
* This is possible if they are both exported and input parameters.
|
||||
*
|
||||
* @param ec1 One equivalence class
|
||||
* @param ec2 Another equivalence class
|
||||
* @param threadHeads The heads (in the call graph) from each thread in the program
|
||||
* @param program The program
|
||||
* @return True if the two equivalence classes can be coalesced into one without problems.
|
||||
*/
|
||||
private static boolean canCoalesceExports(LiveRangeEquivalenceClass ec1, LiveRangeEquivalenceClass ec2, Program program) {
|
||||
for(VariableRef varRef : ec1.getVariables()) {
|
||||
if(isExportParameter(varRef, program)) {
|
||||
for(VariableRef varRef2 : ec2.getVariables()) {
|
||||
if(isExportParameter(varRef2, program))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -170,11 +217,15 @@ public abstract class Pass4MemoryCoalesce extends Pass2Base {
|
|||
int zp = ((Registers.RegisterZpMem) register1).getZp();
|
||||
if(program.getReservedZps().contains(zp))
|
||||
return false;
|
||||
if(register1.isAddressHardcoded())
|
||||
return false;
|
||||
}
|
||||
if(register2 instanceof Registers.RegisterZpMem) {
|
||||
int zp = ((Registers.RegisterZpMem) register2).getZp();
|
||||
if(program.getReservedZps().contains(zp))
|
||||
return false;
|
||||
if(register2.isAddressHardcoded())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ public class Pass5FixLongBranches extends Pass5AsmOptimization {
|
|||
// Copy Resource Files
|
||||
for(Path asmResourceFile : getProgram().getAsmResourceFiles()) {
|
||||
File binFile = getTmpFile(tmpDir, asmResourceFile.getFileName().toString());
|
||||
getLog().append("Preparing to copy " + asmResourceFile.toString() + " to " + binFile.toPath());
|
||||
Files.copy(asmResourceFile, binFile.toPath());
|
||||
}
|
||||
} catch(IOException e) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package dk.camelot64.kickc.passes;
|
||||
|
||||
import dk.camelot64.kickc.model.ControlFlowBlock;
|
||||
import dk.camelot64.kickc.model.Graph;
|
||||
import dk.camelot64.kickc.asm.AsmLibrary;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
import dk.camelot64.kickc.model.VariableReferenceInfos;
|
||||
import dk.camelot64.kickc.model.statements.*;
|
||||
|
@ -36,6 +35,8 @@ public class PassNEliminateUnusedVars extends Pass2SsaOptimization {
|
|||
VariableReferenceInfos referenceInfos = getProgram().getVariableReferenceInfos();
|
||||
boolean modified = false;
|
||||
for(var block : getGraph().getAllBlocks()) {
|
||||
Procedure procedure = getProgram().getProcedure(block);
|
||||
AsmLibrary asmLibrary = getProgram().getAsmExportLibrary(procedure);
|
||||
ListIterator<Statement> stmtIt = block.getStatements().listIterator();
|
||||
while(stmtIt.hasNext()) {
|
||||
Statement statement = stmtIt.next();
|
||||
|
@ -102,15 +103,15 @@ public class PassNEliminateUnusedVars extends Pass2SsaOptimization {
|
|||
} else if(statement instanceof StatementPhiBlock) {
|
||||
StatementPhiBlock statementPhi = (StatementPhiBlock) statement;
|
||||
ListIterator<StatementPhiBlock.PhiVariable> phiVarIt = statementPhi.getPhiVariables().listIterator();
|
||||
while(phiVarIt.hasNext()) {
|
||||
while (phiVarIt.hasNext()) {
|
||||
StatementPhiBlock.PhiVariable phiVariable = phiVarIt.next();
|
||||
VariableRef variableRef = phiVariable.getVariable();
|
||||
Variable variable = getProgramScope().getVariable(variableRef);
|
||||
if(eliminate(variable, referenceInfos, statement)) {
|
||||
if(pass2 || getLog().isVerbosePass1CreateSsa()) {
|
||||
if (eliminate(variable, referenceInfos, statement)) {
|
||||
if (pass2 || getLog().isVerbosePass1CreateSsa()) {
|
||||
getLog().append("Eliminating unused variable - keeping the phi block " + variableRef.toString(getProgram()));
|
||||
}
|
||||
if(variable != null) {
|
||||
if (variable != null) {
|
||||
variable.getScope().remove(variable);
|
||||
}
|
||||
phiVarIt.remove();
|
||||
|
@ -154,20 +155,25 @@ public class PassNEliminateUnusedVars extends Pass2SsaOptimization {
|
|||
* @return true if the variable & statement should be eliminated
|
||||
*/
|
||||
private boolean eliminate(Variable variable, VariableReferenceInfos referenceInfos, Statement statement) {
|
||||
if(variable == null)
|
||||
if (variable == null)
|
||||
// Eliminate if already deleted
|
||||
return true;
|
||||
if(variable.getScope() instanceof EnumDefinition || variable.getScope() instanceof StructDefinition)
|
||||
if (variable.getScope() instanceof EnumDefinition || variable.getScope() instanceof StructDefinition)
|
||||
// Do not eliminate inside enums or structs
|
||||
return false;
|
||||
if(!pass2 && isReturnValue(variable)) {
|
||||
// Do not eliminate return variables in pass 1
|
||||
return false;
|
||||
}
|
||||
if(variable.getScope() instanceof Procedure && ((Procedure) variable.getScope()).getParameters().contains(variable)) {
|
||||
// Do not eliminate parameters
|
||||
|
||||
if (!pass2 && isReturnValue(variable))
|
||||
return false;
|
||||
|
||||
if (variable.getScope() instanceof Procedure procedure) {
|
||||
if (((Procedure) variable.getScope()).getParameters().contains(variable))
|
||||
// Do not eliminate parameters
|
||||
return false;
|
||||
if (isReturnValue(variable) && getProgram().isProcedureAsmExport(procedure.getFullName()))
|
||||
// Do not eliminate unused return value is the procedure is exported.
|
||||
return false;
|
||||
}
|
||||
|
||||
final boolean unused = referenceInfos.isUnused(variable.getRef());
|
||||
if(!unused)
|
||||
// Do not eliminate is not unused
|
||||
|
|
|
@ -30,6 +30,8 @@ public class ProcedureUtils {
|
|||
return true;
|
||||
if(Pass2ConstantIdentification.isAddressOfUsed(procedureRef, program))
|
||||
return true;
|
||||
if(program.isProcedureAsmExport(procedureRef.getFullName()))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -171,7 +171,7 @@ public class TestProgramsThorough extends TestPrograms {
|
|||
|
||||
@Test
|
||||
public void testNesConio() throws IOException {
|
||||
compileAndCompare("examples/nes/nes-conio.c");
|
||||
compileAndCompare("examples/nes/nes-conio-stack.c");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -241,7 +241,7 @@ public class TestProgramsThorough extends TestPrograms {
|
|||
|
||||
@Test
|
||||
public void testAtariXlConioTest() throws IOException {
|
||||
compileAndCompare("examples/atarixl/atarixl-conio.c");
|
||||
compileAndCompare("examples/atarixl/atarixl-conio-stack.c");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
1563
src/test/kc/library/conio_var.asm
Normal file
1563
src/test/kc/library/conio_var.asm
Normal file
File diff suppressed because it is too large
Load Diff
38
src/test/kc/library/lib/conio-stack.c
Normal file
38
src/test/kc/library/lib/conio-stack.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
|
||||
#pragma link("conio.ld")
|
||||
#pragma encoding(petscii_mixed)
|
||||
#pragma var_model(zp)
|
||||
#pragma asm_library("conio")
|
||||
|
||||
#pragma zp_reserve(0x10..0xFF)
|
||||
|
||||
#pragma code_seg(CodeConIO)
|
||||
#pragma data_seg(DataConIO)
|
||||
|
||||
#include <conio.h>
|
||||
//#include <cx16-conio.h>
|
||||
|
||||
__export volatile void* funcs[] = {
|
||||
&clrscr,
|
||||
&gotoxy,
|
||||
// &wherex,
|
||||
// &wherey,
|
||||
// &screensize,
|
||||
// &screensizex,
|
||||
// &screensizey,
|
||||
// &cputc,
|
||||
// &cputln,
|
||||
// &cputcxy,
|
||||
// &cputs,
|
||||
// &cputsxy,
|
||||
// &textcolor,
|
||||
// &bgcolor,
|
||||
// &bordercolor,
|
||||
// &kbhit,
|
||||
// &cursor,
|
||||
// &scroll,
|
||||
// &screenlayer0,
|
||||
// &screenlayer1,
|
||||
// &cpeekc,
|
||||
// &cpeekcxy,
|
||||
};
|
24
src/test/kc/library/lib/conio-test.c
Normal file
24
src/test/kc/library/lib/conio-test.c
Normal file
|
@ -0,0 +1,24 @@
|
|||
typedef struct {
|
||||
unsigned char cursor_x; ///< current cursor x-position
|
||||
unsigned char cursor_y; ///< current cursor y-position
|
||||
} cx16_conio_t;
|
||||
|
||||
cx16_conio_t __conio;
|
||||
|
||||
|
||||
void gotoxy(unsigned char x, unsigned char y)
|
||||
{
|
||||
__conio.cursor_x = (x>=1)?1:x;
|
||||
__conio.cursor_y = (y>=2)?2:y;
|
||||
// __conio.offset = __conio.offsets[y] + __conio.cursor_x << 1;
|
||||
}
|
||||
|
||||
void cputs(const char* s) {
|
||||
__conio.cursor_x = (s==0)?1:2;
|
||||
}
|
||||
|
||||
void cputsxy(unsigned char x, unsigned char y, const char* s) {
|
||||
// gotoxy(x, y);
|
||||
cputs(s);
|
||||
|
||||
}
|
5
src/test/kc/library/lib/conio-test.h
Normal file
5
src/test/kc/library/lib/conio-test.h
Normal file
|
@ -0,0 +1,5 @@
|
|||
void gotoxy(unsigned char x, unsigned char y);
|
||||
|
||||
void cputsxy(unsigned char x, unsigned char y, const char* s);
|
||||
|
||||
void cputs(const char* s);
|
27
src/test/kc/library/lib/conio_var.c
Normal file
27
src/test/kc/library/lib/conio_var.c
Normal file
|
@ -0,0 +1,27 @@
|
|||
|
||||
#pragma link("conio.ld")
|
||||
#pragma encoding(petscii_mixed)
|
||||
#pragma var_model(zp)
|
||||
#pragma asm_library("conio_var")
|
||||
#pragma asm_export("conio_var", __varcall, __start, conio_x16_init)
|
||||
#pragma asm_export("conio_var", __varcall, clrscr)
|
||||
#pragma asm_export("conio_var", __varcall, gotoxy)
|
||||
#pragma asm_export("conio_var", __varcall, wherex, wherey)
|
||||
#pragma asm_export("conio_var", __varcall, screensize, screensizex, screensizey, cputln )
|
||||
#pragma asm_export("conio_var", __stackcall, cputc )
|
||||
#pragma asm_export("conio_var", __varcall, cputcxy, cputs, cputsxy, textcolor, bgcolor, bordercolor )
|
||||
#pragma asm_export("conio_var", __varcall, kbhit, cursor, scroll )
|
||||
#pragma asm_export("conio_var", __varcall, screenlayer1, screenlayer2)
|
||||
#pragma asm_export("conio_var", __varcall, cpeekc, cpeekcxy)
|
||||
|
||||
|
||||
|
||||
#pragma zp_reserve(0x80..0xFF)
|
||||
|
||||
#pragma code_seg(CodeConIO)
|
||||
#pragma data_seg(DataConIO)
|
||||
|
||||
//#include "conio-test.h"
|
||||
|
||||
#include <conio.h>
|
||||
#include <cx16-conio.h>
|
|
@ -4,21 +4,44 @@
|
|||
// Instead of using conio.c and conio.h, this code imports a
|
||||
// conio (.asm) library, already pre-compiled.
|
||||
// The functions, defined using the #pragma directive,
|
||||
// are declared as external __stackcall by the compiler.
|
||||
// are declared as external __varcall by the compiler.
|
||||
// So any function in your code using the conio functions will follow
|
||||
// stack calling convention.
|
||||
// In the ideal world, these functions should not be provided but
|
||||
// linked by a "linker".
|
||||
|
||||
|
||||
#pragma asm_import("lib/conio", clrscr, gotoxy, wherex, wherey)
|
||||
#pragma asm_import("lib/conio", screensize, screensizex, screensizey)
|
||||
#pragma asm_import("lib/conio", cputc, cputln, cputcxy, cputs, cputsxy)
|
||||
#pragma asm_import("lib/conio", textcolor, bgcolor, bordercolor, kbhit)
|
||||
#pragma asm_import("lib/conio", cursor, scroll)
|
||||
#pragma asm_import("lib/conio", screenlayer0, screenlayer1)
|
||||
#pragma asm_import("lib/conio", cpeekc, cpeekcxy)
|
||||
#pragma asm_import("conio_var", __varcall, conio_x16_init, clrscr, gotoxy)
|
||||
#pragma asm_import("conio_var", __varcall, wherex, wherey)
|
||||
#pragma asm_import("conio_var", __varcall, screensize, screensizex, screensizey)
|
||||
#pragma asm_import("conio_var", __stackcall, cputc )
|
||||
#pragma asm_import("conio_var", __varcall, cputln, cputcxy, cputs, cputsxy)
|
||||
#pragma asm_import("conio_var", __varcall, textcolor, bgcolor, bordercolor, kbhit)
|
||||
#pragma asm_import("conio_var", __varcall, cursor, scroll)
|
||||
#pragma asm_import("conio_var", __varcall, screenlayer0, screenlayer1)
|
||||
//#pragma asm_import("conio_var", __varcall, cpeekc, cpeekcxy)
|
||||
|
||||
// void __start()
|
||||
extern void conio_x16_init();
|
||||
extern void clrscr();
|
||||
extern void gotoxy(__zp($d) char x, __zp(2) char y);
|
||||
extern __zp(4) char wherex();
|
||||
extern __zp(3) char wherey();
|
||||
extern void screensize(__zp(8) char *x, __zp($10) char *y);
|
||||
extern __zp($d) char screensizex();
|
||||
extern __zp(2) char screensizey();
|
||||
extern void cputln();
|
||||
extern void cputc(__zp($a) char c);
|
||||
extern void cputcxy(__zp($d) char x, __zp(2) char y, __zp(7) char c);
|
||||
extern void cputs(__zp($e) const char *s);
|
||||
extern void cputsxy(__zp($d) char x, __zp(2) char y, __zp($10) const char *s);
|
||||
extern __zp(2) char textcolor(__zp(6) char color);
|
||||
extern __zp(2) char bgcolor(__zp(3) char color);
|
||||
extern __zp(4) char bordercolor(__zp(3) char color);
|
||||
extern char kbhit();
|
||||
extern __zp(4) char cursor(__zp(5) char onoff);
|
||||
extern __zp($d) char scroll(__zp(5) char onoff);
|
||||
extern void screenlayer1();
|
||||
|
||||
|
||||
// Linkage
|
||||
|
@ -29,8 +52,14 @@
|
|||
#include <printf.h>
|
||||
|
||||
void main() {
|
||||
// clrscr();
|
||||
char c = 'x';
|
||||
|
||||
asm { jsr conio_var.__start }
|
||||
|
||||
textcolor(WHITE);
|
||||
bgcolor(GREEN);
|
||||
clrscr();
|
||||
|
||||
char c = 'e';
|
||||
signed char sc = -12;
|
||||
unsigned char uc = 34;
|
||||
signed int si = -1234;
|
||||
|
@ -38,22 +67,39 @@ void main() {
|
|||
signed long sl = -123456;
|
||||
unsigned long ul = 567890;
|
||||
|
||||
gotoxy(10,2);
|
||||
// char
|
||||
printf("A char: %c\n", c);
|
||||
|
||||
gotoxy(10,4);
|
||||
// pointer
|
||||
printf("A pointer: %p\n", &c);
|
||||
|
||||
gotoxy(10,6);
|
||||
// percent sign
|
||||
printf("A percent: %%\n");
|
||||
|
||||
gotoxy(10,8);
|
||||
// signed char
|
||||
printf("A signed char: %hhd\n", sc);
|
||||
|
||||
gotoxy(10,10);
|
||||
// unsigned char
|
||||
printf("An unsigned char: %hhu\n", uc);
|
||||
|
||||
gotoxy(50,2);
|
||||
// signed int
|
||||
printf("A signed int: %d\n", si);
|
||||
|
||||
gotoxy(50,4);
|
||||
// unsigned int
|
||||
printf("An unsigned int: %u\n", ui);
|
||||
|
||||
gotoxy(50,6);
|
||||
// signed long
|
||||
printf("A signed long: %ld\n", sl);
|
||||
|
||||
gotoxy(50,8);
|
||||
// unsigned long
|
||||
printf("An unsigned long: %lu\n", ul);
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user