diff --git a/.gitignore b/.gitignore index c7169a380..0ae827ae2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +/gen +**/.vscode/* */*.mon */*.vs */*.brk @@ -5,11 +7,12 @@ */*.sym */.tmpdirs */bin/ -/target/ +**/target/* */workspace.xml **/.DS_Store .project .tmpdirs **/.vscode/*.log /.idea/* -kickc.iml \ No newline at end of file +kickc.iml +/src/main/fragment/cache diff --git a/src/main/antlr4/dk/camelot64/kickc/parser/KickCLexer.g4 b/src/main/antlr4/dk/camelot64/kickc/parser/KickCLexer.g4 index b6828d097..98ef7914a 100644 --- a/src/main/antlr4/dk/camelot64/kickc/parser/KickCLexer.g4 +++ b/src/main/antlr4/dk/camelot64/kickc/parser/KickCLexer.g4 @@ -75,6 +75,7 @@ LOCAL_RESERVE: '__zp_reserve' ; ADDRESS: '__address' ; ADDRESS_ZEROPAGE: '__zp' ; ADDRESS_MAINMEM: '__mem' ; +BANK: '__bank' ; FORM_SSA: '__ssa' ; FORM_MA: '__ma' ; INTRINSIC: '__intrinsic' ; diff --git a/src/main/antlr4/dk/camelot64/kickc/parser/KickCParser.g4 b/src/main/antlr4/dk/camelot64/kickc/parser/KickCParser.g4 index 574d23c4b..9dcea6f49 100644 --- a/src/main/antlr4/dk/camelot64/kickc/parser/KickCParser.g4 +++ b/src/main/antlr4/dk/camelot64/kickc/parser/KickCParser.g4 @@ -161,6 +161,7 @@ directive | EXTERN #directiveExtern | EXPORT #directiveExport | INLINE #directiveInline + | BANK PAR_BEGIN NAME COMMA NUMBER PAR_END #directiveBank | INTRINSIC #directiveIntrinsic | INTERRUPT ( PAR_BEGIN NAME PAR_END )? #directiveInterrupt | LOCAL_RESERVE PAR_BEGIN pragmaParam ( COMMA pragmaParam )* PAR_END #directiveReserveZp diff --git a/src/main/fragment/mos6502-common/call_phi_close_cx16_ram.asm b/src/main/fragment/mos6502-common/call_phi_close_cx16_ram.asm new file mode 100644 index 000000000..87678c012 --- /dev/null +++ b/src/main/fragment/mos6502-common/call_phi_close_cx16_ram.asm @@ -0,0 +1,11 @@ +sta $ff +lda $0 +pha +lda #{c1} +sta $0 +lda $ff +jsr {la1} +sta $ff +pla +sta $0 +lda $ff \ No newline at end of file diff --git a/src/main/fragment/mos6502-common/call_phi_close_cx16_rom.asm b/src/main/fragment/mos6502-common/call_phi_close_cx16_rom.asm new file mode 100644 index 000000000..5eb46e849 --- /dev/null +++ b/src/main/fragment/mos6502-common/call_phi_close_cx16_rom.asm @@ -0,0 +1,11 @@ +sta $ff +lda $1 +pha +lda #{c1} +sta $1 +lda $ff +jsr {la1} +sta $ff +pla +sta $1 +lda $ff \ No newline at end of file diff --git a/src/main/fragment/mos6502-common/call_phi_far_cx16_ram.asm b/src/main/fragment/mos6502-common/call_phi_far_cx16_ram.asm new file mode 100644 index 000000000..6be1d836e --- /dev/null +++ b/src/main/fragment/mos6502-common/call_phi_far_cx16_ram.asm @@ -0,0 +1,4 @@ +jsr $FF6E +.byte <{la1} +.byte >{la1} +.byte {c1} \ No newline at end of file diff --git a/src/main/fragment/mos6502-common/call_phi_far_cx16_rom.asm b/src/main/fragment/mos6502-common/call_phi_far_cx16_rom.asm new file mode 100644 index 000000000..6be1d836e --- /dev/null +++ b/src/main/fragment/mos6502-common/call_phi_far_cx16_rom.asm @@ -0,0 +1,4 @@ +jsr $FF6E +.byte <{la1} +.byte >{la1} +.byte {c1} \ No newline at end of file diff --git a/src/main/fragment/mos6502-common/call_phi_near.asm b/src/main/fragment/mos6502-common/call_phi_near.asm new file mode 100644 index 000000000..443c46caa --- /dev/null +++ b/src/main/fragment/mos6502-common/call_phi_near.asm @@ -0,0 +1 @@ +jsr {la1} \ No newline at end of file diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index cc8b197cd..f5a16b1d9 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -224,6 +224,7 @@ public class Compiler { new Pass1Procedures(program).execute(); new PassNTypeInference(program).execute(); new PassNFixIntermediateMemoryArea(program).execute(); + new Pass1FixProcedureParamSegment(program).execute(); new PassNTypeIdSimplification(program).execute(); new Pass1StructTypeSizeFix(program).execute(); new Pass1PrintfIntrinsicRewrite(program).execute(); diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecBuilder.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecBuilder.java index 6ab66739f..acd60c256 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecBuilder.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecBuilder.java @@ -13,7 +13,9 @@ import dk.camelot64.kickc.model.operators.OperatorBinary; import dk.camelot64.kickc.model.operators.OperatorUnary; import dk.camelot64.kickc.model.operators.Operators; import dk.camelot64.kickc.model.statements.*; +import dk.camelot64.kickc.model.symbols.Bank; import dk.camelot64.kickc.model.symbols.Label; +import dk.camelot64.kickc.model.symbols.Procedure; import dk.camelot64.kickc.model.symbols.Symbol; import dk.camelot64.kickc.model.types.SymbolType; import dk.camelot64.kickc.model.types.SymbolTypeInference; @@ -41,6 +43,27 @@ final public class AsmFragmentInstanceSpecBuilder { return new AsmFragmentInstanceSpec(program, signature, bindings, codeScope); } + /** + * Create a fragment instance spec factory for a banked call + * + * @param toProcedure The procedure being called + * @param callingDistance The calling distance of the call + * @param program The program + * @return the fragment instance spec factory + */ + public static AsmFragmentInstanceSpec callBanked(Procedure toProcedure, Bank.CallingDistance callingDistance, Program program) { + final Bank toBank = toProcedure.getBank(); + AsmFragmentBindings bindings = new AsmFragmentBindings(program); + AsmFragmentSignature signature = new AsmFragmentSignature.CallBanked( + toProcedure.getCallingConvention().getShortName(), + callingDistance.toString(), + (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.getFullName())); + return new AsmFragmentInstanceSpec(program, signature, bindings, codeScope); + } + /** * Create a fragment instance spec factory for an interrupt routine entry * diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java index 94f2aa4b8..150d8de48 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java @@ -143,6 +143,7 @@ public class AsmFragmentTemplate { if(signature.contains("c6")) bindings.put("c6", new ConstantInteger(360L)); if(signature.contains("la1")) bindings.put("la1", new Label("@1", scope, true)); if(signature.startsWith("call_")) bindings.put("la1", new Label("@1", scope, true)); + if(signature.startsWith("call_")) bindings.put("c1", new ConstantInteger(400L)); AsmFragmentInstance fragmentInstance = new AsmFragmentInstance(new Program(), signature, ScopeRef.ROOT, this, bindings); AsmProgram asm = new AsmProgram(targetCpu); diff --git a/src/main/java/dk/camelot64/kickc/fragment/signature/AsmFragmentSignature.java b/src/main/java/dk/camelot64/kickc/fragment/signature/AsmFragmentSignature.java index 33982bc5c..16c8b76a9 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/signature/AsmFragmentSignature.java +++ b/src/main/java/dk/camelot64/kickc/fragment/signature/AsmFragmentSignature.java @@ -54,7 +54,7 @@ public interface AsmFragmentSignature { } /** - * ASM fragment signature for a conditional jump if(A) goto B. + * ASM fragment signature for a call */ class Call implements AsmFragmentSignature { @@ -70,6 +70,29 @@ public interface AsmFragmentSignature { } } + /** + * ASM fragment signature for a banked call + */ + class CallBanked implements AsmFragmentSignature { + + final private String callingConvention; + + final private String callingDistance; + + final private String toBankArea; + + public CallBanked(String callingConvention, String callingDistance, String toBankArea) { + this.callingConvention = callingConvention; + this.callingDistance = callingDistance; + this.toBankArea = toBankArea; + } + + @Override + public String getName() { + return "call_" + callingConvention + "_" + callingDistance + ((toBankArea==null)?"":("_"+toBankArea)); + } + } + /** * ASM fragment signature for a conditional jump if(A) goto B. */ diff --git a/src/main/java/dk/camelot64/kickc/model/Directive.java b/src/main/java/dk/camelot64/kickc/model/Directive.java index 968153e0c..248a554e9 100644 --- a/src/main/java/dk/camelot64/kickc/model/Directive.java +++ b/src/main/java/dk/camelot64/kickc/model/Directive.java @@ -43,6 +43,29 @@ public class Directive { public Inline() { super("inline"); } } + /** + * Bank to place code into. Used for determining call distance. + */ + static public class Bank extends Directive { + + private String bankArea; + private Long bankNumber; + + public Bank(String bankArea, Long bankNumber) { + super("bank" ); + this.bankArea = bankArea; + this.bankNumber = bankNumber; + } + + public String getBankArea() { + return bankArea; + } + + public Long getBankNumber() { + return bankNumber; + } + } + /** Function declared intrinsic. */ public static class Intrinsic extends Directive { public Intrinsic() { super("intrinsic"); } diff --git a/src/main/java/dk/camelot64/kickc/model/symbols/Bank.java b/src/main/java/dk/camelot64/kickc/model/symbols/Bank.java new file mode 100644 index 000000000..8c2796371 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/model/symbols/Bank.java @@ -0,0 +1,173 @@ +package dk.camelot64.kickc.model.symbols; + +/** + *

+ * Specific target computer platforms implement a memory layout that can be banked either in ram or rom. + * This class models the capability to calculate which function call implementations are banked and which not. + *
Since banking implementations are specific to the target computer platform, + * specific assembler fragments are available in the compiler, + * that model the implementation of the banking, depending on: + * + *

+ * + *

The method can implement different banking implementations, + * depending on the required function call speed, + * the banking routine availability in the kernal (rom) of the target computer platform + * or even using specific designed banking routines by kickc to provide alternatives for banking methods. + * The method and the platform are the key combinations that select the fragments + * in kickc to generate the banked function call implementations used by the compiler. + * + *


Your C-code can be augmented with 3 new directives, that define which function(s) will be declared as banked: + * + *

+ *

Examples of bank areas are different RAM and/or ROM areas at different + * zones in the linear memory addressing space, and may or may not overlap. + * Each banking area has its own configuration in the target computer platform and + * operate independently from each other.
+ * For example, the Commander X16 has 256 RAM bank between 0xA000 and 0xBFFF and + * 256 ROM banks from 0xC000 till 0xFFFF. Each RAM bank is configured through zero page $00 + * and each ROM bank is configured through zero page $01. + * The compiler configures for you these registers, depending on the configured banking area of the function, + * when a banked call is required.

+ * + *


There are different types of function calls that can be implemented, depending on: + *

+ * + *

The types of (banked) function call implementations are: + *

+ * Depending on the target platform of the system the far calls are likely the most complex + * and will consume the most CPU cycles. Therefore, careful design of the program flow and the + * placement of the functions in the banks need to be done, to avoid as much as possible these far calls. + * + *


The exact rules when near, close and far calls are implemented by the compiler, + * can be summarized in the following 6 cases: + * + *

+ * + *

The usage of #pragma code_seg( segment ) directive + * is very important! The #pragma code_seg( segment ) directive utilization + * in harmony with the #pragma bank( method, number ) directive + * makes the overall banking implementation work for your program. + * + *

+ *

+ *

+ * The KickC compiler contains several test cases and examples which demonstrate the usage of the banking system. + * + * @param bankArea The bankable area name. + * @param bankNumber The bank number. + */ +public record Bank(String bankArea, Long bankNumber) { + + /** + * Creates a new Bank which collects the necessary data to handle banking. + * For example, on the Commander X16, RAM is banked from address 0xA000 till 0xBFFF. + * Zeropage 0x00 configures this banked RAM, with a number from 0x00 till 0xff. + * So "Banked RAM" is is a bankArea, and the bank is a configurable bank number in the bankArea. + * + * @param bankArea A bank area is a memory range that is banked on a target platform. + * @param bankNumber A bank is a number that defines a bank configuration in a bank area. + */ + public Bank { + } + + /** The common/shared bank which is always visible. */ + public static Bank COMMON = new Bank("", 0L); + + /** + * Is this the common/shared bank which is always visible. + * @return True if this is the common bank + */ + public boolean isCommon() { + return COMMON.equals(this); + } + + @Override + public String toString() { + if(isCommon()) { + return ""; + } else { + return "__bank(" + this.bankArea() + ", " + this.bankNumber() + ") "; + } + } + + /** The bank distance between a caller and callee, which will determine the type of call needed. */ + public enum CallingDistance { + /** Caller and callee are both in the same bank or in the common bank. No bank change is needed. */ + NEAR, + /** Caller is in the common bank or a different banking area. A direct bank change is needed. */ + CLOSE, + /** Caller and callee are different banks of the same banking area. A trampoline bank change is needed. */ + FAR; + + public static CallingDistance forCall(Bank from, Bank to) { + if(to.isCommon()) { + // NEAR: call to the common bank + return NEAR; + } else if(to.equals(from)) { + // NEAR: call to the same bank in the same banking area + return NEAR; + } else if(from.isCommon()) { + // CLOSE: call from common bank to any bank + return CLOSE; + } else if(!from.bankArea().equals(to.bankArea())) { + // CLOSE: from one banking area to another + return CLOSE; + } else { + // FAR: banked to different bank in same bank area + return FAR; + } + } + + @Override + public String toString() { + return name().toLowerCase(); + } + + } +} diff --git a/src/main/java/dk/camelot64/kickc/model/symbols/Procedure.java b/src/main/java/dk/camelot64/kickc/model/symbols/Procedure.java index ce46bb5f1..0f59f17a3 100644 --- a/src/main/java/dk/camelot64/kickc/model/symbols/Procedure.java +++ b/src/main/java/dk/camelot64/kickc/model/symbols/Procedure.java @@ -33,14 +33,19 @@ public class Procedure extends Scope { private List comments; /** Reserved zeropage addresses. */ private List reservedZps; - /** The code segment to put the procedure into. */ - private String codeSegment; + /** The code segment to put the procedure code into. When null the procedure is not assigned to the code segment. */ + private String segmentCode; /** The list of constructor procedures for this procedure. The constructor procedures are called during program initialization. */ private final List constructorRefs; /** Is this procedure declared as a constructor procedure. */ private boolean isConstructor; /** The source of the procedure definition. */ private StatementSource definitionSource; + /** + * The bank that the procedure code is placed in. + * Used to decide whether to produce near, close or far call code when generating calls. + */ + private Bank bank; /** The names of all legal intrinsic procedures. */ final public static List INTRINSIC_PROCEDURES = Arrays.asList( @@ -50,27 +55,42 @@ public class Procedure extends Scope { Pass1ByteXIntrinsicRewrite.INTRINSIC_MAKELONG4 ); + public Bank getBank() { + return bank; + } + + public void setBank(Bank bank) { + this.bank = Objects.requireNonNull(bank); + } + /** The method for passing parameters and return value to the procedure. */ public enum CallingConvention { /** Parameters and return value handled through PHI-transitions. */ - PHI_CALL("__phicall"), + PHI_CALL("__phicall", "phi"), /** Parameters and return value over the stack. */ - STACK_CALL("__stackcall"), + STACK_CALL("__stackcall", "stack"), /** Parameters and return value handled through shared variables. */ - VAR_CALL("__varcall"), + VAR_CALL("__varcall", "var"), /** Intrinsic calling. Will be converted to intrinsic ASM late in the compile. */ - INTRINSIC_CALL("__intrinsiccall"); + INTRINSIC_CALL("__intrinsiccall", "intrinsic"); private final String name; - CallingConvention(String name) { + private final String shortName; + + CallingConvention(String name, String shortName) { this.name = name; + this.shortName = shortName; } public String getName() { return name; } + public String getShortName() { + return shortName; + } + /** Get a calling convention by name. */ public static CallingConvention getCallingConvension(String name) { for(CallingConvention value : CallingConvention.values()) { @@ -85,13 +105,14 @@ public class Procedure extends Scope { /** The calling convention used for this procedure. */ private CallingConvention callingConvention; - public Procedure(String name, SymbolTypeProcedure procedureType, Scope parentScope, String codeSegment, String dataSegment, CallingConvention callingConvention) { - super(name, parentScope, dataSegment); + public Procedure(String name, SymbolTypeProcedure procedureType, Scope parentScope, String segmentCode, String segmentData, CallingConvention callingConvention, Bank bank) { + super(name, parentScope, segmentData); this.procedureType = procedureType; this.declaredInline = false; + this.bank = Objects.requireNonNull(bank); this.interruptType = null; this.comments = new ArrayList<>(); - this.codeSegment = codeSegment; + this.segmentCode = Objects.requireNonNull(segmentCode); this.callingConvention = callingConvention; this.constructorRefs = new ArrayList<>(); this.isConstructor = false; @@ -113,12 +134,12 @@ public class Procedure extends Scope { this.callingConvention = callingConvention; } - public String getCodeSegment() { - return codeSegment; + public String getSegmentCode() { + return segmentCode; } - public void setCodeSegment(String codeSegment) { - this.codeSegment = codeSegment; + public void setSegmentCode(String segmentCode) { + this.segmentCode = segmentCode; } public List getParameterNames() { @@ -262,6 +283,7 @@ public class Procedure extends Scope { if(declaredIntrinsic) { res.append("__intrinsic "); } + res.append(bank.toString()); if(!callingConvention.equals(CallingConvention.PHI_CALL)) { res.append(getCallingConvention().getName()).append(" "); } @@ -307,13 +329,14 @@ public class Procedure extends Scope { Objects.equals(interruptType, procedure.interruptType) && Objects.equals(comments, procedure.comments) && Objects.equals(reservedZps, procedure.reservedZps) && - Objects.equals(codeSegment, procedure.codeSegment) && + Objects.equals(segmentCode, procedure.segmentCode) && Objects.equals(constructorRefs, procedure.constructorRefs) && + Objects.equals(bank, procedure.bank) && callingConvention == procedure.callingConvention; } @Override public int hashCode() { - return Objects.hash(super.hashCode(), procedureType, parameterNames, variableLengthParameterList, declaredInline, declaredIntrinsic, interruptType, comments, reservedZps, codeSegment, constructorRefs, isConstructor, callingConvention); + return Objects.hash(super.hashCode(), procedureType, parameterNames, variableLengthParameterList, declaredInline, declaredIntrinsic, interruptType, comments, reservedZps, segmentCode, constructorRefs, isConstructor, bank, callingConvention); } } diff --git a/src/main/java/dk/camelot64/kickc/model/symbols/Scope.java b/src/main/java/dk/camelot64/kickc/model/symbols/Scope.java index ed32ce6ba..a4b6e0736 100644 --- a/src/main/java/dk/camelot64/kickc/model/symbols/Scope.java +++ b/src/main/java/dk/camelot64/kickc/model/symbols/Scope.java @@ -30,7 +30,7 @@ public abstract class Scope implements Symbol { this.name = name; this.parentScope = parentScope; this.symbols = new LinkedHashMap<>(); - this.segmentData = segmentData; + this.segmentData = Objects.requireNonNull(segmentData); setFullName(); } @@ -43,6 +43,10 @@ public abstract class Scope implements Symbol { return segmentData; } + public void setSegmentData(String segmentData) { + this.segmentData = segmentData; + } + public HashMap getSymbols() { return symbols; } diff --git a/src/main/java/dk/camelot64/kickc/parser/CParser.java b/src/main/java/dk/camelot64/kickc/parser/CParser.java index f56daca13..76b52a81c 100644 --- a/src/main/java/dk/camelot64/kickc/parser/CParser.java +++ b/src/main/java/dk/camelot64/kickc/parser/CParser.java @@ -106,6 +106,14 @@ public class CParser { * The resource-file is copied to the output directory when compiling. */ public static final String PRAGMA_RESOURCE = "resource"; + /** + * #pragma bank(...) changes the current bank. Functions and variables will be placed in the specified bank. + */ + public static final String PRAGMA_BANK = "bank"; + /** + * #pragma nobank Changes the current bank to the default/common/shared bank. + */ + public static final String PRAGMA_NOBANK = "nobank"; /** * The Program. diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java index c8604b28d..a62082a72 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java @@ -16,7 +16,6 @@ import dk.camelot64.kickc.parser.KickCParserBaseVisitor; import dk.camelot64.kickc.passes.utils.SizeOfConstants; import org.antlr.v4.runtime.BufferedTokenStream; import org.antlr.v4.runtime.ParserRuleContext; -import org.antlr.v4.runtime.RuleContext; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNode; @@ -131,7 +130,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor()), program.getScope(), Scope.SEGMENT_CODE_DEFAULT, Scope.SEGMENT_DATA_DEFAULT, Procedure.CallingConvention.PHI_CALL); + initProc = new Procedure(SymbolRef.INIT_PROC_NAME, new SymbolTypeProcedure(SymbolType.VOID, new ArrayList<>()), program.getScope(), Scope.SEGMENT_CODE_DEFAULT, Scope.SEGMENT_DATA_DEFAULT, Procedure.CallingConvention.PHI_CALL, Bank.COMMON); initProc.setDeclaredInline(true); initProc.setParameters(new ArrayList<>()); program.getScope().add(initProc); @@ -188,7 +187,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor()), program.getScope(), Scope.SEGMENT_CODE_DEFAULT, Scope.SEGMENT_DATA_DEFAULT, Procedure.CallingConvention.PHI_CALL); + final Procedure startProcedure = new Procedure(SymbolRef.START_PROC_NAME, new SymbolTypeProcedure(SymbolType.VOID, new ArrayList<>()), program.getScope(), Scope.SEGMENT_CODE_DEFAULT, Scope.SEGMENT_DATA_DEFAULT, Procedure.CallingConvention.PHI_CALL, Bank.COMMON); startProcedure.setParameters(new ArrayList<>()); program.getScope().add(startProcedure); final ProcedureCompilation startProcedureCompilation = program.createProcedureCompilation(startProcedure.getRef()); @@ -289,10 +288,24 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor effectiveDirectives = varDecl.getDeclDirectives(); final List declComments = varDecl.getDeclComments(); varDecl.exitVar(); - VariableBuilder varBuilder = new VariableBuilder(varName, getCurrentScope(), false, false, effectiveType, effectiveDirectives, currentDataSegment, program.getTargetPlatform().getVariableBuilderConfig()); + VariableBuilder varBuilder = new VariableBuilder(varName, getCurrentScope(), false, false, effectiveType, effectiveDirectives, currentSegmentData, program.getTargetPlatform().getVariableBuilderConfig()); Variable variable = varBuilder.build(); if(isStructMember && (initializer != null)) throw new CompileError("Initializer not supported inside structs " + effectiveType.toCDecl(), declSource); @@ -1136,7 +1156,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor allVariables = getScope().getAllVariables(true); + for(Variable variable : allVariables) { + if(variable.isKindLoadStore() || variable.isKindPhiMaster() || variable.isKindIntermediate()) { + if(variable.getRegister() instanceof Registers.RegisterMainMem registerMainMem && registerMainMem.isAddressHardcoded()) + continue; + if(variable.getDataSegment().equals(Scope.SEGMENT_DATA_DEFAULT)) + continue; + + final Scope scope = variable.getScope(); + if(scope instanceof Procedure procedure) { + if(!procedure.getBank().isCommon()) { + List parameters = procedure.getParameters(); + if(parameters.contains(variable) || variable.getLocalName().equals("return")) { + variable.setDataSegment(Scope.SEGMENT_DATA_DEFAULT); + getLog().append("Fixing banked procedure parameter/return value to default segment " + variable.getFullName()); + } + } + } + } + } + return false; + } +} diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index d2f3bc5a1..29000ba0b 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -115,7 +115,7 @@ public class Pass4CodeGeneration { currentScope = block.getScope(); if (block.isProcedureEntry(program)) { Procedure procedure = block.getProcedure(program); - currentCodeSegmentName = procedure.getCodeSegment(); + currentCodeSegmentName = procedure.getSegmentCode(); } setCurrentSegment(currentCodeSegmentName, asm); asm.startChunk(currentScope, null, block.getLabel().getFullName()); @@ -219,7 +219,7 @@ public class Pass4CodeGeneration { } /** - * Generate a comment that describes the procedure signature and parameter transfer + * Generate a comment that describes the procedure signature and parameter transfer. * * @param asm The assembler program being generated * @param procedure The procedure @@ -240,6 +240,9 @@ public class Pass4CodeGeneration { if (i > 0) { asm.addComment(signature.toString(), false); } + if(!procedure.getBank().isCommon()) { + asm.addComment(" " + procedure.getBank(), false); + } } /** @@ -853,14 +856,15 @@ public class Pass4CodeGeneration { AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.conditionalJump((StatementConditionalJump) statement, block, program), program); } else if (statement instanceof StatementCall) { StatementCall call = (StatementCall) statement; - Procedure procedure = getScope().getProcedure(call.getProcedure()); - if (procedure.isDeclaredIntrinsic()) { - if (Pass1ByteXIntrinsicRewrite.INTRINSIC_MAKELONG4.equals(procedure.getFullName())) { + Procedure toProcedure = getScope().getProcedure(call.getProcedure()); + Procedure fromProcedure = block.getProcedure(this.program); // We obtain from where the procedure is called, to validate the bank equality. + if (toProcedure.isDeclaredIntrinsic()) { + if (Pass1ByteXIntrinsicRewrite.INTRINSIC_MAKELONG4.equals(toProcedure.getFullName())) { AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.makelong4(call, program), program); } else { - throw new CompileError("Intrinsic procedure not supported " + procedure.toString(program)); + throw new CompileError("Intrinsic procedure not supported " + toProcedure.toString(program)); } - } else if (Procedure.CallingConvention.PHI_CALL.equals(procedure.getCallingConvention())) { + } else if (Procedure.CallingConvention.PHI_CALL.equals(toProcedure.getCallingConvention())) { // Generate PHI transition if (genCallPhiEntry) { ControlFlowBlock callSuccessor = getGraph().getCallSuccessor(block); @@ -872,17 +876,44 @@ public class Pass4CodeGeneration { genBlockPhiTransition(asm, block, callSuccessor, block.getScope()); } } - asm.addInstruction("jsr", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false); - } else if (Procedure.CallingConvention.STACK_CALL.equals(procedure.getCallingConvention())) { - asm.addInstruction("jsr", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false); + final Bank.CallingDistance callingDistance = Bank.CallingDistance.forCall(fromProcedure.getBank(), toProcedure.getBank()); + if(Bank.CallingDistance.NEAR.equals(callingDistance)) { + asm.addInstruction("jsr", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false); + } else { + AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.callBanked(toProcedure, callingDistance, program), program); + } + } else if (Procedure.CallingConvention.STACK_CALL.equals(toProcedure.getCallingConvention())) { + final Bank.CallingDistance callingDistance = Bank.CallingDistance.forCall(fromProcedure.getBank(), toProcedure.getBank()); + if(Bank.CallingDistance.NEAR.equals(callingDistance)) { + asm.addInstruction("jsr", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false); + } else { + throw new CompileError("Stack Call procedure not supported in banked mode " + toProcedure.toString(program)); + } } } else if (statement instanceof StatementCallExecute) { StatementCallExecute call = (StatementCallExecute) statement; - RValue procedureRVal = call.getProcedureRVal(); - // Generate ASM for a call - AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.call(call, indirectCallCount++, program), program); - if (!(procedureRVal instanceof ProcedureRef)) { - asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL); + ProcedureRef procedureRef = call.getProcedure(); + if(procedureRef != null) { + ProgramScope scope = getScope(); + Procedure toProcedure = scope.getProcedure(procedureRef); + Procedure fromProcedure = block.getProcedure(this.program); + final Bank.CallingDistance callingDistance = Bank.CallingDistance.forCall(fromProcedure.getBank(), toProcedure.getBank()); + if(Bank.CallingDistance.NEAR.equals(callingDistance)) { + AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.call(call, indirectCallCount++, program), program); + } else { + throw new CompileError("Stack Call procedure not supported in banked mode " + toProcedure.toString(program)); + } + RValue procedureRVal = call.getProcedureRVal(); + if (!(procedureRVal instanceof ProcedureRef)) { + asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL); + } + } else { + RValue procedureRVal = call.getProcedureRVal(); + // Generate ASM for a call + AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.call(call, indirectCallCount++, program), program); + if (!(procedureRVal instanceof ProcedureRef)) { + asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL); + } } } else if (statement instanceof StatementExprSideEffect) { AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.exprSideEffect((StatementExprSideEffect) statement, program), program); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4MemoryCoalesce.java b/src/main/java/dk/camelot64/kickc/passes/Pass4MemoryCoalesce.java index c229e98fe..03c531702 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4MemoryCoalesce.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4MemoryCoalesce.java @@ -53,6 +53,7 @@ public abstract class Pass4MemoryCoalesce extends Pass2Base { return canCoalesceNotEqual(ec1, ec2) && canCoalesceCompatible(ec1, ec2, program) && + canCoalesceSegments(ec1, ec2, program) && canCoalesceVolatile(ec1, ec2, program) && canCoalesceThreads(ec1, ec2, threadHeads, program) && canCoalesceClobber(ec1, ec2, unknownFragments, program); @@ -69,6 +70,21 @@ public abstract class Pass4MemoryCoalesce extends Pass2Base { return !ec1.equals(ec2); } + /** + * Determines if two live range equivalence classes are candidates for coalescing by ensuring they are not from different data segments. + * @param ec1 One equivalence class + * @param ec2 Another equivalence class + * @param program The program + * @return True if the two equivalence classes can be coalesced into one without problems. + */ + private static boolean canCoalesceSegments(LiveRangeEquivalenceClass ec1, LiveRangeEquivalenceClass ec2, Program program) { + final VariableRef variableRef1 = ec1.getVariables().get(0); + final String dataSegment1 = program.getScope().getVar(variableRef1).getDataSegment(); + final VariableRef variableRef2 = ec2.getVariables().get(0); + final String dataSegment2 = program.getScope().getVar(variableRef2).getDataSegment(); + 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. diff --git a/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java b/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java index 042f930e2..df4514641 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java +++ b/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java @@ -767,6 +767,11 @@ public class TestProgramsFast extends TestPrograms { compileAndCompare("examples/cx16/cx16-rasterbars.c"); } + //@Test + //public void testCx16Banking() throws IOException { + // compileAndCompare("examples/cx16/banking/cx16-banking.c"); + //} + @Test public void testMega65Camelot1536Dots() throws IOException { compileAndCompare("examples/mega65/camelot-1536dots.c"); @@ -1516,6 +1521,83 @@ public class TestProgramsFast extends TestPrograms { compileAndCompare("declared-memory-var-0.c"); } + @Test + public void testBankedPhiCase1Near0() throws IOException { + compileAndCompare("call-banked-phi-case-1-near-0.c"); + } + + @Test + public void testBankedPhiCase1Near1() throws IOException { + compileAndCompare("call-banked-phi-case-1-near-1.c"); + } + + @Test + public void testBankedPhiCase2Close0() throws IOException { + compileAndCompare("call-banked-phi-case-2-close-0.c"); + } + + @Test + public void testBankedPhiCase2Close1() throws IOException { + compileAndCompare("call-banked-phi-case-2-close-1.c"); + } + + @Test + public void testBankedPhiCase3Near0() throws IOException { + compileAndCompare("call-banked-phi-case-3-near-0.c"); + } + + @Test + public void testBankedPhiCase3Near1() throws IOException { + compileAndCompare("call-banked-phi-case-3-near-1.c"); + } + + @Test + public void testBankedPhiCase4Near0() throws IOException { + compileAndCompare("call-banked-phi-case-4-near-0.c"); + } + + @Test + public void testBankedPhiCase4Near1() throws IOException { + compileAndCompare("call-banked-phi-case-4-near-1.c"); + } + + @Test + public void testBankedPhiCase5Far0() throws IOException { + compileAndCompare("call-banked-phi-case-5-far-0.c"); + } + + @Test + public void testBankedPhiCase5Far1() throws IOException { + compileAndCompare("call-banked-phi-case-5-far-1.c"); + } + + @Test + public void testBankedPhiCase6Close0() throws IOException { + compileAndCompare("call-banked-phi-case-6-close-0.c"); + } + + @Test + public void testBankedPhiCase6Close1() throws IOException { + compileAndCompare("call-banked-phi-case-6-close-1.c"); + } + + @Test + public void testBankedPhiMemvars() throws IOException { + compileAndCompare("call-banked-phi-memvars.c"); + } + + + @Test + public void testBankedStackCase2Close0() throws IOException { + assertError("call-banked-stack-case-2-close-0.c", "Stack Call procedure not supported in banked mode"); + } + + @Test + public void testBankedStackCase5Far0() throws IOException { + assertError("call-banked-stack-case-5-far-0.c", "Stack Call procedure not supported in banked mode"); + } + + @Test public void testProcedureCallingConventionStack13() throws IOException { compileAndCompare("procedure-callingconvention-stack-13.c"); diff --git a/src/test/kc/call-banked-phi-case-1-near-0.c b/src/test/kc/call-banked-phi-case-1-near-0.c new file mode 100644 index 000000000..07b4a9513 --- /dev/null +++ b/src/test/kc/call-banked-phi-case-1-near-0.c @@ -0,0 +1,41 @@ +/** + * @file call-banked-phi-case-1-near-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be) + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #1. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + +#pragma link("call-banked-phi.ld") + +char* const SCREEN = (char*)0x0400; + +#pragma code_seg(Code) +void main(void) { + SCREEN[0] = plus('0', 7); // near call + SCREEN[1] = plus('1', 6); // near call +} + +#pragma code_seg(Code) +char plus(char a, char b) { + return a+b; +} diff --git a/src/test/kc/call-banked-phi-case-1-near-1.c b/src/test/kc/call-banked-phi-case-1-near-1.c new file mode 100644 index 000000000..7b5b79ef6 --- /dev/null +++ b/src/test/kc/call-banked-phi-case-1-near-1.c @@ -0,0 +1,46 @@ +/** + * @file call-banked-phi-case-1-near-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #1 + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + +#pragma link("call-banked-phi.ld") + +char* const SCREEN = (char*)0x0400; + +#pragma code_seg(Code) +void main(void) { + SCREEN[0] = plus('0', 7); // near call + SCREEN[1] = plus('1', 6); // near call +} + +#pragma code_seg(Code) +char plus(char a, char b) { + return add(a, b); // near call +} + +#pragma code_seg(Code) +char add(char a, char b) { + return a+b; +} diff --git a/src/test/kc/call-banked-phi-case-2-close-0.c b/src/test/kc/call-banked-phi-case-2-close-0.c new file mode 100644 index 000000000..ed836b407 --- /dev/null +++ b/src/test/kc/call-banked-phi-case-2-close-0.c @@ -0,0 +1,42 @@ +/** + * @file call-banked-phi-case-2-close-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #2. + * Implementation using the __bank() directive. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + +#pragma link("call-banked-phi.ld") + +char* const SCREEN = (char*)0x0400; + +#pragma code_seg(Code) +void main(void) { + SCREEN[0] = plus('0', 7); // close call + SCREEN[1] = plus('1', 6); // close call +} + +#pragma code_seg(RAM_Bank1) +__bank(cx16_ram,1) char plus(char a, char b) { + return a+b; +} \ No newline at end of file diff --git a/src/test/kc/call-banked-phi-case-2-close-1.c b/src/test/kc/call-banked-phi-case-2-close-1.c new file mode 100644 index 000000000..038007c95 --- /dev/null +++ b/src/test/kc/call-banked-phi-case-2-close-1.c @@ -0,0 +1,43 @@ +/** + * @file call-banked-phi-case-2-close-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #2. + * Implementation using the #pragma bank and nobank directives. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + +#pragma link("call-banked-phi.ld") + +char* const SCREEN = (char*)0x0400; + +#pragma code_seg(Code) +void main(void) { + SCREEN[0] = plus('0', 7); // close call + SCREEN[1] = plus('1', 6); // close call +} + +#pragma code_seg(RAM_Bank1) +#pragma bank(cx16_ram, 1) +char plus(char a, char b) { + return a+b; +} \ No newline at end of file diff --git a/src/test/kc/call-banked-phi-case-3-near-0.c b/src/test/kc/call-banked-phi-case-3-near-0.c new file mode 100644 index 000000000..76812ead5 --- /dev/null +++ b/src/test/kc/call-banked-phi-case-3-near-0.c @@ -0,0 +1,50 @@ +/** + * @file call-banked-phi-case-3-near-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #3. + * Implementation using the __bank() directive. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + +#pragma link("call-banked-phi.ld") + +char* const SCREEN = (char*)0x0400; + +#pragma code_seg(Code) +void main(void) { + SCREEN[0] = plus('0', 7); // close call + SCREEN[1] = plus('1', 6); // close call +} + +#pragma code_seg(RAM_Bank1) +__bank(cx16_ram,1) char plus(char a, char b) { + return add(a, b); // near call +} + +#pragma code_seg(Code) +#pragma nobank +char add(char a, char b) { + return a+b; +} + + diff --git a/src/test/kc/call-banked-phi-case-3-near-1.c b/src/test/kc/call-banked-phi-case-3-near-1.c new file mode 100644 index 000000000..25bce37f7 --- /dev/null +++ b/src/test/kc/call-banked-phi-case-3-near-1.c @@ -0,0 +1,51 @@ +/** + * @file call-banked-phi-case-3-near-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #3. + * Implementation using the #pragma bank and nobank directives. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + +#pragma link("call-banked-phi.ld") + +char* const SCREEN = (char*)0x0400; + +#pragma code_seg(Code) +void main(void) { + SCREEN[0] = plus('0', 7); // close call + SCREEN[1] = plus('1', 6); // close call +} + +#pragma code_seg(RAM_Bank1) +#pragma bank(cx16_ram, 1) +char plus(char a, char b) { + return add(a, b); // near call +} + +#pragma code_seg(Code) +#pragma nobank +char add(char a, char b) { + return a+b; +} + + diff --git a/src/test/kc/call-banked-phi-case-4-near-0.c b/src/test/kc/call-banked-phi-case-4-near-0.c new file mode 100644 index 000000000..5c7ffc1f0 --- /dev/null +++ b/src/test/kc/call-banked-phi-case-4-near-0.c @@ -0,0 +1,47 @@ +/** + * @file call-banked-phi-case-4-near-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #4. + * Implementation using the __bank() directive. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + +#pragma link("call-banked-phi.ld") + +char* const SCREEN = (char*)0x0400; + +#pragma code_seg(Code) +void main(void) { + SCREEN[0] = plus('0', 7); // close call + SCREEN[1] = plus('1', 6); // close call +} + +#pragma code_seg(RAM_Bank1) +char __bank(cx16_ram, 1) plus(char a, char b) { + return add(a, b); // near call +} + +#pragma code_seg(RAM_Bank1) +char __bank(cx16_ram, 1) add(char a, char b) { + return a+b; +} diff --git a/src/test/kc/call-banked-phi-case-4-near-1.c b/src/test/kc/call-banked-phi-case-4-near-1.c new file mode 100644 index 000000000..319ac2483 --- /dev/null +++ b/src/test/kc/call-banked-phi-case-4-near-1.c @@ -0,0 +1,49 @@ +/** + * @file call-banked-phi-case-4-near-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #4. + * Implementation using the #pragma bank and nobank directives. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + +#pragma link("call-banked-phi.ld") + +char* const SCREEN = (char*)0x0400; + +#pragma code_seg(Code) +void main(void) { + SCREEN[0] = plus('0', 7); // close call + SCREEN[1] = plus('1', 6); // close call +} + +#pragma code_seg(RAM_Bank1) +#pragma bank(cx16_ram, 1) +char plus(char a, char b) { + return add(a, b); // near call +} + +#pragma code_seg(RAM_Bank1) +#pragma bank(cx16_ram, 1) +char add(char a, char b) { + return a+b; +} diff --git a/src/test/kc/call-banked-phi-case-5-far-0.c b/src/test/kc/call-banked-phi-case-5-far-0.c new file mode 100644 index 000000000..dceba3f63 --- /dev/null +++ b/src/test/kc/call-banked-phi-case-5-far-0.c @@ -0,0 +1,47 @@ +/** + * @file call-banked-phi-case-5-far-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #5. + * Implementation using the __bank() directive. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + +#pragma link("call-banked-phi.ld") + +char* const SCREEN = (char*)0x0400; + +#pragma code_seg(Code) +void main(void) { + SCREEN[0] = plus('0', 7); // close call + SCREEN[1] = plus('1', 6); // close call +} + +#pragma code_seg(RAM_Bank1) +__bank(cx16_ram, 1) char plus(char a, char b) { + return add(a, b); // far call +} + +#pragma code_seg(RAM_Bank2) +__bank(cx16_ram, 2) char add(char a, char b) { + return a+b; +} diff --git a/src/test/kc/call-banked-phi-case-5-far-1.c b/src/test/kc/call-banked-phi-case-5-far-1.c new file mode 100644 index 000000000..754dd1402 --- /dev/null +++ b/src/test/kc/call-banked-phi-case-5-far-1.c @@ -0,0 +1,49 @@ +/** + * @file call-banked-phi-case-5-far-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #5. + * Implementation using the #pragma bank and nobank directives. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + +#pragma link("call-banked-phi.ld") + +char* const SCREEN = (char*)0x0400; + +#pragma code_seg(Code) +void main(void) { + SCREEN[0] = plus('0', 7); // close call + SCREEN[1] = plus('1', 6); // close call +} + +#pragma code_seg(RAM_Bank1) +#pragma bank(cx16_ram, 1) +char plus(char a, char b) { + return add(a, b); // far call +} + +#pragma code_seg(RAM_Bank2) +#pragma bank(cx16_ram, 2) +char add(char a, char b) { + return a+b; +} diff --git a/src/test/kc/call-banked-phi-case-6-close-0.c b/src/test/kc/call-banked-phi-case-6-close-0.c new file mode 100644 index 000000000..8fada9193 --- /dev/null +++ b/src/test/kc/call-banked-phi-case-6-close-0.c @@ -0,0 +1,47 @@ +/** + * @file call-banked-phi-case-6-close-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #6. + * Implementation using the __bank() directive. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + +#pragma link("call-banked-phi.ld") + +char* const SCREEN = (char*)0x0400; + +#pragma code_seg(Code) +void main(void) { + SCREEN[0] = plus('0', 7); // close call + SCREEN[1] = plus('1', 6); // close call +} + +#pragma code_seg(RAM_Bank1) +__bank(cx16_ram, 1) char plus(char a, char b) { + return add(a, b); // close call +} + +#pragma code_seg(ROM_Bank1) +__bank(cx16_rom, 1) char add(char a, char b) { + return a+b; +} diff --git a/src/test/kc/call-banked-phi-case-6-close-1.c b/src/test/kc/call-banked-phi-case-6-close-1.c new file mode 100644 index 000000000..5f8141443 --- /dev/null +++ b/src/test/kc/call-banked-phi-case-6-close-1.c @@ -0,0 +1,49 @@ +/** + * @file call-banked-phi-case-6-close-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #6. + * Implementation using the #pragma bank and nobank directives. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + +#pragma link("call-banked-phi.ld") + +char* const SCREEN = (char*)0x0400; + +#pragma code_seg(Code) +void main(void) { + SCREEN[0] = plus('0', 7); // close call + SCREEN[1] = plus('1', 6); // close call +} + +#pragma code_seg(RAM_Bank1) +#pragma bank(cx16_ram, 1) +char plus(char a, char b) { + return add(a, b); // close call +} + +#pragma code_seg(ROM_Bank1) +#pragma bank(cx16_rom, 1) +char add(char a, char b) { + return a+b; +} diff --git a/src/test/kc/call-banked-phi-memvars.c b/src/test/kc/call-banked-phi-memvars.c new file mode 100644 index 000000000..420b2796f --- /dev/null +++ b/src/test/kc/call-banked-phi-memvars.c @@ -0,0 +1,30 @@ +/** + * Test banked calls with memory variables. + * The parameters & return should end up in the shared/common bank. + */ + +#pragma link("call-banked-phi.ld") +#pragma var_model(mem) + +int* const SCREEN = (int*)0x0400; + +#pragma code_seg(Code) +#pragma nobank +void main(void) { + for(char i=0;i<5; i++) { + SCREEN[i] = plus(100, (int)i); + SCREEN[10+i] = plus(200, (int)i); + } +} + +#pragma code_seg(RAM_Bank1) +#pragma data_seg(RAM_Bank1) +#pragma bank(cx16_ram, 1) +int plus(int a, int b) { + int r = 2; + r += a; + r += b; + r += a; + r += b; + return r; +} diff --git a/src/test/kc/call-banked-phi.ld b/src/test/kc/call-banked-phi.ld new file mode 100644 index 000000000..de9bdab5d --- /dev/null +++ b/src/test/kc/call-banked-phi.ld @@ -0,0 +1,13 @@ +.file [name="%O", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=%P] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(%E) +.segment Code +.segment Data + diff --git a/src/test/kc/call-banked-stack-case-2-close-0.c b/src/test/kc/call-banked-stack-case-2-close-0.c new file mode 100644 index 000000000..91f688bee --- /dev/null +++ b/src/test/kc/call-banked-stack-case-2-close-0.c @@ -0,0 +1,42 @@ +/** + * @file call-banked-stack-case-2-close-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #2 + * The compiler will throw an error because the far call is a __stackcall + * and this is not (yet) implemented and supported. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + +#pragma link("call-banked-stack.ld") + +char* const SCREEN = (char*)0x0400; + +#pragma code_seg(Code) +void main(void) { + SCREEN[0] = plus('0', 7); // close stack call +} + +#pragma code_seg(Bank1) +__stackcall __bank(cx16_ram, 1) char plus(char a, char b) { + return a+b; +} diff --git a/src/test/kc/call-banked-stack-case-5-far-0.c b/src/test/kc/call-banked-stack-case-5-far-0.c new file mode 100644 index 000000000..a5b7567f3 --- /dev/null +++ b/src/test/kc/call-banked-stack-case-5-far-0.c @@ -0,0 +1,47 @@ +/** + * @file call-banked-stack-case-5-far-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #5. + * The compiler will throw an error because the far call is a __stackcall + * and this is not (yet) implemented and supported. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + +#pragma link("call-banked-phi.ld") + +char* const SCREEN = (char*)0x0400; + +#pragma code_seg(Code) +void main(void) { + SCREEN[0] = plus('0', 7); // close call +} + +#pragma code_seg(RAM_Bank1) +__bank(cx16_ram, 1) char plus(char a, char b) { + return min(a, b); // far call +} + +#pragma code_seg(RAM_Bank2) +__stackcall __bank(cx16_ram, 2) char min(char a, char b) { + return a+b; +} diff --git a/src/test/kc/call-banked-stack.ld b/src/test/kc/call-banked-stack.ld new file mode 100644 index 000000000..de9bdab5d --- /dev/null +++ b/src/test/kc/call-banked-stack.ld @@ -0,0 +1,13 @@ +.file [name="%O", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=%P] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(%E) +.segment Code +.segment Data + diff --git a/src/test/kc/examples/cx16/banking/cx16-banking.c b/src/test/kc/examples/cx16/banking/cx16-banking.c new file mode 100644 index 000000000..8648d45d1 --- /dev/null +++ b/src/test/kc/examples/cx16/banking/cx16-banking.c @@ -0,0 +1,164 @@ +/** + * @file cx16-banking-1.c + * @author your name (you@domain.com) + * @brief This program demonstrates a simple example of a banked call. + * @version 0.1 + * @date 2023-04-05 + * + * @copyright Copyright (c) 2023 + * + */ + +// The linker specification of the different segments. +#pragma target(cx16) +#pragma link("cx16-banking.ld") +#pragma var_model(mem) + +#include +#include +#include + +// The target computer platform is the Commander X16, +// which implements banking in ram between 0xA0000 and 0xBFFF, +// and in ram between 0xC000 and 0xFFFF. +#pragma target(cx16) + +// Functional code + +#pragma code_seg(Bank1) // The sequent functions will be addressed specified by segment bank1 in the linker. +#pragma bank(cx16_ram, 1) // The sequent functions will be banked using call method ram in bank number 1. + +char add_a(char a) { + printf("add_a(%02x), ", bank_get_bram()); + return a+1; +} + +char add_c(char a) { + printf("add_c(%02x), ", bank_get_bram()); + return add_a(a)+1; // Non banked call in ram bank 1. +} + +char add_d(char a) { + printf("add_d(%02x), ",bank_get_bram()); + return mul_a(a)+1; // Banked call fram ram bank 1 to ram bank 2. +} + +char add_e(char a) { + printf("add_e(%02x), ",bank_get_bram()); + return mul_b(a)+1; // Banked call fram ram bank 1 to ram bank 2. +} + +char add_f(char a) { + printf("add_f(%02x), ",bank_get_bram()); + return add_m(a)+1; // Non banked call fram ram bank 1 to main memory. +} + + +#pragma code_seg(Bank2) // The sequent functions will be addressed specified by segment bank2 in the linker. +#pragma bank(cx16_ram, 2) // The sequent functions will be banked using call method ram in bank number 2. + +char mul_a(char m) { + printf("mul_a(%02x), ",bank_get_bram()); + return m * 2; +} + +char mul_c(char m) { + printf("mul_c(%02x), ",bank_get_bram()); + return add_a(m)*2; // Banked call fram ram bank 2 to ram bank 1. +} + +char mul_d(char m) { + printf("mul_d(%02x), ",bank_get_bram()); + return mul_a(m)*2; // Non banked call in ram bank 2. +} + +char mul_e(char a) { + printf("mul_e(%02x), ",bank_get_bram()); + return mul_b(a)*2; // Non Banked call in ram bank 2. +} + +char mul_f(char m) { + printf("mul_f(%02x), ",bank_get_bram()); + return add_m(m)*2; // Non banked call fram ram bank 2 to main memory. +} + + +#pragma nobank // The sequent functions will consider no banking calculations anymore. + +#pragma code_seg(Bank1) // The sequent functions will be addressed specified by segment bank1 in the linker. +// The __bank directive declares this function to be banked using call method ram in bank number 1 of banked ram. +char __bank(cx16_ram, 1) add_b(char a) { + printf("add_b(%02x), ",bank_get_bram()); + return a+1; +} + +#pragma code_seg(Bank2) // The sequent functions will be addressed specified by segment bank1 in the linker. +// The __bank directive declares this function to be banked using call method ram in bank number 2 of banked ram. +char __bank(cx16_ram, 2) mul_b(char m) { + printf("mul_b(%02x), ",bank_get_bram()); + return m*2; +} + +#pragma code_seg(Code) // The sequent functions will be addressed in the default main memory location (segment Code). + +// Allocated in main memory. +char add_m(char a) { + printf("add_m(%02x), ",bank_get_bram()); + return add_e(a)+1; // Banked call to ram in bank 1 fram main memory. +} + +// Allocated in main memory. +char mul_m(char m) { + printf("mul_m(%02x), ",bank_get_bram()); + return mul_e(m)*2; // Banked call to ram in bank 2 fram main memory. +} + + +// Practically this means that the main() function is placed in main memory ... + +void load_bank(char bank, char *file) { + bank_set_bram(bank); + cbm_k_setnam(file); + cbm_k_setlfs(1,8,2); + cbm_k_load((char*)0xA000, 0); + cbm_k_close(1); +} + +void main(void) { + + clrscr(); + + load_bank(1, "BANK1.BIN"); + load_bank(2, "BANK2.BIN"); + + bank_set_bram(0); + + asm{.byte $db} + printf("result = %u\n", add_a(1)); // Banked call to ram in bank 1 fram main memory. + asm{.byte $db} + printf("result = %u\n", add_b(1)); // Banked call to ram in bank 1 fram main memory. + asm{.byte $db} + printf("result = %u\n", add_c(1)); // Banked call to ram in bank 1 fram main memory. + asm{.byte $db} + printf("result = %u\n", add_d(1)); // Banked call to ram in bank 1 fram main memory. + asm{.byte $db} + printf("result = %u\n", add_e(1)); // Banked call to ram in bank 1 fram main memory. + asm{.byte $db} + printf("result = %u\n", add_f(1)); // Banked call to ram in bank 1 fram main memory. + asm{.byte $db} + printf("result = %u\n", mul_a(1)); // Banked call to ram in bank 2 fram main memory. + asm{.byte $db} + printf("result = %u\n", mul_b(1)); // Banked call to ram in bank 2 fram main memory. + asm{.byte $db} + printf("result = %u\n", mul_c(1)); // Banked call to ram in bank 2 fram main memory. + asm{.byte $db} + printf("result = %u\n", mul_d(1)); // Banked call to ram in bank 2 fram main memory. + asm{.byte $db} + printf("result = %u\n", mul_e(1)); // banked call to ram in bank 2 fram main memory. + asm{.byte $db} + printf("result = %u\n", mul_f(1)); // banked call to ram in bank 2 fram main memory. + asm{.byte $db} + printf("result = %u\n", add_m(1)); // Near call in main memory fram main memory. + asm{.byte $db} + printf("result = %u\n", mul_m(1)); // Near call in main memory fram main memory. +} diff --git a/src/test/kc/examples/cx16/banking/cx16-banking.ld b/src/test/kc/examples/cx16/banking/cx16-banking.ld new file mode 100644 index 000000000..e21277416 --- /dev/null +++ b/src/test/kc/examples/cx16/banking/cx16-banking.ld @@ -0,0 +1,14 @@ +.file [name="%O", type="prg", segments="Program"] +.file [name="BANK1.BIN", type="bin", segments="Bank1"] +.file [name="BANK2.BIN", type="bin", segments="Bank2"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=%P] +.segmentdef Data [startAfter="Code"] +.segmentdef Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segment Basic +:BasicUpstart(%E) +.segment Code +.segment Data + diff --git a/src/test/ref/call-banked-phi-case-1-near-0.asm b/src/test/ref/call-banked-phi-case-1-near-0.asm new file mode 100644 index 000000000..9c88aba31 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-1-near-0.asm @@ -0,0 +1,70 @@ +/** + * @file call-banked-phi-case-1-near-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be) + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #1. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + .file [name="call-banked-phi-case-1-near-0.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + .label SCREEN = $400 +.segment Code +main: { + // plus('0', 7) + lda #7 + ldx #'0' + jsr plus + // plus('0', 7) + // SCREEN[0] = plus('0', 7) + sta SCREEN + // plus('1', 6) + lda #6 + ldx #'1' + jsr plus + // plus('1', 6) + // SCREEN[1] = plus('1', 6) + // near call + sta SCREEN+1 + // } + rts +} +// __register(A) char plus(__register(X) char a, __register(A) char b) +plus: { + // a+b + stx.z $ff + clc + adc.z $ff + // } + rts +} diff --git a/src/test/ref/call-banked-phi-case-1-near-0.cfg b/src/test/ref/call-banked-phi-case-1-near-0.cfg new file mode 100644 index 000000000..936abd391 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-1-near-0.cfg @@ -0,0 +1,30 @@ + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] plus::return#2 = plus::a#2 + plus::b#2 + to:plus::@return +plus::@return: scope:[plus] from plus + [12] return + to:@return diff --git a/src/test/ref/call-banked-phi-case-1-near-0.log b/src/test/ref/call-banked-phi-case-1-near-0.log new file mode 100644 index 000000000..2ffa84883 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-1-near-0.log @@ -0,0 +1,462 @@ +Loading link script "call-banked-phi.ld" + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + plus::a#0 = '0' + plus::b#0 = 7 + call plus + plus::return#0 = plus::return#3 + to:main::@1 +main::@1: scope:[main] from main + plus::return#4 = phi( main/plus::return#0 ) + main::$0 = plus::return#4 + SCREEN[0] = main::$0 + plus::a#1 = '1' + plus::b#1 = 6 + call plus + plus::return#1 = plus::return#3 + to:main::@2 +main::@2: scope:[main] from main::@1 + plus::return#5 = phi( main::@1/plus::return#1 ) + main::$1 = plus::return#5 + SCREEN[1] = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + return + to:@return + +char plus(char a , char b) +plus: scope:[plus] from main main::@1 + plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 ) + plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 ) + plus::$0 = plus::a#2 + plus::b#2 + plus::return#2 = plus::$0 + to:plus::@return +plus::@return: scope:[plus] from plus + plus::return#6 = phi( plus/plus::return#2 ) + plus::return#3 = plus::return#6 + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant char * const SCREEN = (char *)$400 +void __start() +void main() +char main::$0 +char main::$1 +char plus(char a , char b) +char plus::$0 +char plus::a +char plus::a#0 +char plus::a#1 +char plus::a#2 +char plus::b +char plus::b#0 +char plus::b#1 +char plus::b#2 +char plus::return +char plus::return#0 +char plus::return#1 +char plus::return#2 +char plus::return#3 +char plus::return#4 +char plus::return#5 +char plus::return#6 + +Adding number conversion cast (unumber) 7 in plus::b#0 = 7 +Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0 +Adding number conversion cast (unumber) 6 in plus::b#1 = 6 +Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast plus::b#0 = (unumber)7 +Inlining cast plus::b#1 = (unumber)6 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 7 +Simplifying constant integer cast 0 +Simplifying constant integer cast 6 +Simplifying constant integer cast 1 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 7 +Finalized unsigned number type (char) 0 +Finalized unsigned number type (char) 6 +Finalized unsigned number type (char) 1 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias plus::return#0 = plus::return#4 +Alias plus::return#1 = plus::return#5 +Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3 +Successful SSA optimization Pass2AliasElimination +Constant plus::a#0 = '0' +Constant plus::b#0 = 7 +Constant plus::a#1 = '1' +Constant plus::b#1 = 6 +Successful SSA optimization Pass2ConstantIdentification +Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0 +Successful SSA optimization PassNSimplifyExpressionWithZero +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Inlining constant with var siblings plus::a#0 +Inlining constant with var siblings plus::b#0 +Inlining constant with var siblings plus::a#1 +Inlining constant with var siblings plus::b#1 +Constant inlined plus::b#1 = 6 +Constant inlined plus::b#0 = 7 +Constant inlined plus::a#1 = '1' +Constant inlined plus::a#0 = '0' +Successful SSA optimization Pass2ConstantInlining +Consolidated array index constant in *(SCREEN+1) +Successful SSA optimization Pass2ConstantAdditionElimination +Adding NOP phi() at start of main +CALL GRAPH +Calls in [main] to plus:1 plus:5 + +Created 2 initial phi equivalence classes +Coalesced down to 2 phi equivalence classes +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] plus::return#2 = plus::a#2 + plus::b#2 + to:plus::@return +plus::@return: scope:[plus] from plus + [12] return + to:@return + + +VARIABLE REGISTER WEIGHTS +void main() +char main::$0 // 4.0 +char main::$1 // 4.0 +char plus(char a , char b) +char plus::a +char plus::a#2 // 11.0 +char plus::b +char plus::b#2 // 11.0 +char plus::return +char plus::return#0 // 4.0 +char plus::return#1 // 4.0 +char plus::return#2 // 3.75 + +Initial phi equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +Added variable plus::return#0 to live range equivalence class [ plus::return#0 ] +Added variable main::$0 to live range equivalence class [ main::$0 ] +Added variable plus::return#1 to live range equivalence class [ plus::return#1 ] +Added variable main::$1 to live range equivalence class [ main::$1 ] +Added variable plus::return#2 to live range equivalence class [ plus::return#2 ] +Complete equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +[ plus::return#0 ] +[ main::$0 ] +[ plus::return#1 ] +[ main::$1 ] +[ plus::return#2 ] +Allocated zp[1]:2 [ plus::a#2 ] +Allocated zp[1]:3 [ plus::b#2 ] +Allocated zp[1]:4 [ plus::return#0 ] +Allocated zp[1]:5 [ main::$0 ] +Allocated zp[1]:6 [ plus::return#1 ] +Allocated zp[1]:7 [ main::$1 ] +Allocated zp[1]:8 [ plus::return#2 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [11] plus::return#2 = plus::a#2 + plus::b#2 [ plus::return#2 ] ( plus:1 [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } plus:5 [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Potential registers zp[1]:2 [ plus::a#2 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:3 [ plus::b#2 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:4 [ plus::return#0 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:5 [ main::$0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:6 [ plus::return#1 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:7 [ main::$1 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:8 [ plus::return#2 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [plus] 11: zp[1]:2 [ plus::a#2 ] 11: zp[1]:3 [ plus::b#2 ] 4: zp[1]:4 [ plus::return#0 ] 4: zp[1]:6 [ plus::return#1 ] 3.75: zp[1]:8 [ plus::return#2 ] +Uplift Scope [main] 4: zp[1]:5 [ main::$0 ] 4: zp[1]:7 [ main::$1 ] +Uplift Scope [] + +Uplifting [plus] best 81 combination reg byte x [ plus::a#2 ] reg byte a [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:8 [ plus::return#2 ] +Limited combination testing to 100 combinations of 1024 possible. +Uplifting [main] best 69 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ] +Uplifting [] best 69 combination +Attempting to uplift remaining variables inzp[1]:8 [ plus::return#2 ] +Uplifting [plus] best 60 combination reg byte a [ plus::return#2 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +/** + * @file call-banked-phi-case-1-near-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be) + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #1. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-1-near-0.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + plus_from_main: + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuaa=vbuc1 + lda #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- vbuxx=vbuc1 + ldx #'0' + jsr plus + // [2] plus::return#0 = plus::return#2 + jmp __b1 + // main::@1 + __b1: + // [3] main::$0 = plus::return#0 + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + plus_from___b1: + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuaa=vbuc1 + lda #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- vbuxx=vbuc1 + ldx #'1' + jsr plus + // [6] plus::return#1 = plus::return#2 + jmp __b2 + // main::@2 + __b2: + // [7] main::$1 = plus::return#1 + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // near call + sta SCREEN+1 + jmp __breturn + // main::@return + __breturn: + // [9] return + rts +} + // plus +// __register(A) char plus(__register(X) char a, __register(A) char b) +plus: { + // [11] plus::return#2 = plus::a#2 + plus::b#2 -- vbuaa=vbuxx_plus_vbuaa + stx.z $ff + clc + adc.z $ff + jmp __breturn + // plus::@return + __breturn: + // [12] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __b2 +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction plus_from_main: +Removing instruction __b1: +Removing instruction plus_from___b1: +Removing instruction __b2: +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte x 11.0 +char plus::b +char plus::b#2 // reg byte a 11.0 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte a 3.75 + +reg byte x [ plus::a#2 ] +reg byte a [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +reg byte a [ plus::return#2 ] + + +FINAL ASSEMBLER +Score: 48 + + // File Comments +/** + * @file call-banked-phi-case-1-near-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be) + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #1. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-1-near-0.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // plus('0', 7) + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuaa=vbuc1 + lda #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- vbuxx=vbuc1 + ldx #'0' + jsr plus + // plus('0', 7) + // [2] plus::return#0 = plus::return#2 + // main::@1 + // [3] main::$0 = plus::return#0 + // SCREEN[0] = plus('0', 7) + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // plus('1', 6) + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuaa=vbuc1 + lda #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- vbuxx=vbuc1 + ldx #'1' + jsr plus + // plus('1', 6) + // [6] plus::return#1 = plus::return#2 + // main::@2 + // [7] main::$1 = plus::return#1 + // SCREEN[1] = plus('1', 6) + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // near call + sta SCREEN+1 + // main::@return + // } + // [9] return + rts +} + // plus +// __register(A) char plus(__register(X) char a, __register(A) char b) +plus: { + // a+b + // [11] plus::return#2 = plus::a#2 + plus::b#2 -- vbuaa=vbuxx_plus_vbuaa + stx.z $ff + clc + adc.z $ff + // plus::@return + // } + // [12] return + rts +} + // File Data + diff --git a/src/test/ref/call-banked-phi-case-1-near-0.sym b/src/test/ref/call-banked-phi-case-1-near-0.sym new file mode 100644 index 000000000..aad9eb173 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-1-near-0.sym @@ -0,0 +1,21 @@ +__constant char * const SCREEN = (char *) 1024 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte x 11.0 +char plus::b +char plus::b#2 // reg byte a 11.0 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte a 3.75 + +reg byte x [ plus::a#2 ] +reg byte a [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +reg byte a [ plus::return#2 ] diff --git a/src/test/ref/call-banked-phi-case-1-near-1.asm b/src/test/ref/call-banked-phi-case-1-near-1.asm new file mode 100644 index 000000000..c5395e6ac --- /dev/null +++ b/src/test/ref/call-banked-phi-case-1-near-1.asm @@ -0,0 +1,78 @@ +/** + * @file call-banked-phi-case-1-near-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #1 + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + .file [name="call-banked-phi-case-1-near-1.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + .label SCREEN = $400 +.segment Code +main: { + // plus('0', 7) + lda #7 + ldx #'0' + jsr plus + // plus('0', 7) + // SCREEN[0] = plus('0', 7) + sta SCREEN + // plus('1', 6) + lda #6 + ldx #'1' + jsr plus + // plus('1', 6) + // SCREEN[1] = plus('1', 6) + // near call + sta SCREEN+1 + // } + rts +} +// __register(A) char plus(__register(X) char a, __register(A) char b) +plus: { + // add(a, b) + stx.z add.a + jsr add + // } + rts +} +// __register(A) char add(__zp(2) char a, __register(A) char b) +add: { + .label a = 2 + // a+b + clc + adc.z a + // } + rts +} diff --git a/src/test/ref/call-banked-phi-case-1-near-1.cfg b/src/test/ref/call-banked-phi-case-1-near-1.cfg new file mode 100644 index 000000000..bd8833228 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-1-near-1.cfg @@ -0,0 +1,44 @@ + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] add::a#0 = plus::a#2 + [12] add::b#0 = plus::b#2 + [13] call add + [14] add::return#0 = add::return#1 + to:plus::@1 +plus::@1: scope:[plus] from plus + [15] plus::return#2 = add::return#0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + [16] return + to:@return + +char add(char a , char b) +add: scope:[add] from plus + [17] add::return#1 = add::a#0 + add::b#0 + to:add::@return +add::@return: scope:[add] from add + [18] return + to:@return diff --git a/src/test/ref/call-banked-phi-case-1-near-1.log b/src/test/ref/call-banked-phi-case-1-near-1.log new file mode 100644 index 000000000..916bd8ab3 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-1-near-1.log @@ -0,0 +1,599 @@ +Loading link script "call-banked-phi.ld" + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + plus::a#0 = '0' + plus::b#0 = 7 + call plus + plus::return#0 = plus::return#3 + to:main::@1 +main::@1: scope:[main] from main + plus::return#4 = phi( main/plus::return#0 ) + main::$0 = plus::return#4 + SCREEN[0] = main::$0 + plus::a#1 = '1' + plus::b#1 = 6 + call plus + plus::return#1 = plus::return#3 + to:main::@2 +main::@2: scope:[main] from main::@1 + plus::return#5 = phi( main::@1/plus::return#1 ) + main::$1 = plus::return#5 + SCREEN[1] = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + return + to:@return + +char plus(char a , char b) +plus: scope:[plus] from main main::@1 + plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 ) + plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 ) + add::a#0 = plus::a#2 + add::b#0 = plus::b#2 + call add + add::return#0 = add::return#2 + to:plus::@1 +plus::@1: scope:[plus] from plus + add::return#3 = phi( plus/add::return#0 ) + plus::$0 = add::return#3 + plus::return#2 = plus::$0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + plus::return#6 = phi( plus::@1/plus::return#2 ) + plus::return#3 = plus::return#6 + return + to:@return + +char add(char a , char b) +add: scope:[add] from plus + add::b#1 = phi( plus/add::b#0 ) + add::a#1 = phi( plus/add::a#0 ) + add::$0 = add::a#1 + add::b#1 + add::return#1 = add::$0 + to:add::@return +add::@return: scope:[add] from add + add::return#4 = phi( add/add::return#1 ) + add::return#2 = add::return#4 + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant char * const SCREEN = (char *)$400 +void __start() +char add(char a , char b) +char add::$0 +char add::a +char add::a#0 +char add::a#1 +char add::b +char add::b#0 +char add::b#1 +char add::return +char add::return#0 +char add::return#1 +char add::return#2 +char add::return#3 +char add::return#4 +void main() +char main::$0 +char main::$1 +char plus(char a , char b) +char plus::$0 +char plus::a +char plus::a#0 +char plus::a#1 +char plus::a#2 +char plus::b +char plus::b#0 +char plus::b#1 +char plus::b#2 +char plus::return +char plus::return#0 +char plus::return#1 +char plus::return#2 +char plus::return#3 +char plus::return#4 +char plus::return#5 +char plus::return#6 + +Adding number conversion cast (unumber) 7 in plus::b#0 = 7 +Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0 +Adding number conversion cast (unumber) 6 in plus::b#1 = 6 +Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast plus::b#0 = (unumber)7 +Inlining cast plus::b#1 = (unumber)6 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 7 +Simplifying constant integer cast 0 +Simplifying constant integer cast 6 +Simplifying constant integer cast 1 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 7 +Finalized unsigned number type (char) 0 +Finalized unsigned number type (char) 6 +Finalized unsigned number type (char) 1 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias plus::return#0 = plus::return#4 +Alias plus::return#1 = plus::return#5 +Alias add::return#0 = add::return#3 +Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3 +Alias add::return#1 = add::$0 add::return#4 add::return#2 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values add::a#1 add::a#0 +Identical Phi Values add::b#1 add::b#0 +Successful SSA optimization Pass2IdenticalPhiElimination +Constant plus::a#0 = '0' +Constant plus::b#0 = 7 +Constant plus::a#1 = '1' +Constant plus::b#1 = 6 +Successful SSA optimization Pass2ConstantIdentification +Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0 +Successful SSA optimization PassNSimplifyExpressionWithZero +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Inlining constant with var siblings plus::a#0 +Inlining constant with var siblings plus::b#0 +Inlining constant with var siblings plus::a#1 +Inlining constant with var siblings plus::b#1 +Constant inlined plus::b#1 = 6 +Constant inlined plus::b#0 = 7 +Constant inlined plus::a#1 = '1' +Constant inlined plus::a#0 = '0' +Successful SSA optimization Pass2ConstantInlining +Consolidated array index constant in *(SCREEN+1) +Successful SSA optimization Pass2ConstantAdditionElimination +Adding NOP phi() at start of main +CALL GRAPH +Calls in [main] to plus:1 plus:5 +Calls in [plus] to add:13 + +Created 2 initial phi equivalence classes +Coalesced down to 2 phi equivalence classes +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] add::a#0 = plus::a#2 + [12] add::b#0 = plus::b#2 + [13] call add + [14] add::return#0 = add::return#1 + to:plus::@1 +plus::@1: scope:[plus] from plus + [15] plus::return#2 = add::return#0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + [16] return + to:@return + +char add(char a , char b) +add: scope:[add] from plus + [17] add::return#1 = add::a#0 + add::b#0 + to:add::@return +add::@return: scope:[add] from add + [18] return + to:@return + + +VARIABLE REGISTER WEIGHTS +char add(char a , char b) +char add::a +char add::a#0 // 56.0 +char add::b +char add::b#0 // 112.0 +char add::return +char add::return#0 // 22.0 +char add::return#1 // 37.33333333333333 +void main() +char main::$0 // 4.0 +char main::$1 // 4.0 +char plus(char a , char b) +char plus::a +char plus::a#2 // 11.0 +char plus::b +char plus::b#2 // 5.5 +char plus::return +char plus::return#0 // 4.0 +char plus::return#1 // 4.0 +char plus::return#2 // 3.75 + +Initial phi equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +Added variable plus::return#0 to live range equivalence class [ plus::return#0 ] +Added variable main::$0 to live range equivalence class [ main::$0 ] +Added variable plus::return#1 to live range equivalence class [ plus::return#1 ] +Added variable main::$1 to live range equivalence class [ main::$1 ] +Added variable add::a#0 to live range equivalence class [ add::a#0 ] +Added variable add::b#0 to live range equivalence class [ add::b#0 ] +Added variable add::return#0 to live range equivalence class [ add::return#0 ] +Added variable plus::return#2 to live range equivalence class [ plus::return#2 ] +Added variable add::return#1 to live range equivalence class [ add::return#1 ] +Complete equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +[ plus::return#0 ] +[ main::$0 ] +[ plus::return#1 ] +[ main::$1 ] +[ add::a#0 ] +[ add::b#0 ] +[ add::return#0 ] +[ plus::return#2 ] +[ add::return#1 ] +Allocated zp[1]:2 [ add::b#0 ] +Allocated zp[1]:3 [ add::a#0 ] +Allocated zp[1]:4 [ add::return#1 ] +Allocated zp[1]:5 [ add::return#0 ] +Allocated zp[1]:6 [ plus::a#2 ] +Allocated zp[1]:7 [ plus::b#2 ] +Allocated zp[1]:8 [ plus::return#0 ] +Allocated zp[1]:9 [ main::$0 ] +Allocated zp[1]:10 [ plus::return#1 ] +Allocated zp[1]:11 [ main::$1 ] +Allocated zp[1]:12 [ plus::return#2 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a +Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ] +Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ] +Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ] +Uplift Scope [] + +Uplifting [add] best 129 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte a [ add::return#1 ] reg byte a [ add::return#0 ] +Limited combination testing to 100 combinations of 256 possible. +Uplifting [plus] best 99 combination reg byte x [ plus::a#2 ] reg byte a [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ] +Limited combination testing to 100 combinations of 1024 possible. +Uplifting [main] best 87 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ] +Uplifting [] best 87 combination +Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ] +Uplifting [add] best 87 combination zp[1]:3 [ add::a#0 ] +Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ] +Uplifting [plus] best 78 combination reg byte a [ plus::return#2 ] +Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +/** + * @file call-banked-phi-case-1-near-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #1 + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-1-near-1.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + plus_from_main: + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuaa=vbuc1 + lda #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- vbuxx=vbuc1 + ldx #'0' + jsr plus + // [2] plus::return#0 = plus::return#2 + jmp __b1 + // main::@1 + __b1: + // [3] main::$0 = plus::return#0 + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + plus_from___b1: + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuaa=vbuc1 + lda #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- vbuxx=vbuc1 + ldx #'1' + jsr plus + // [6] plus::return#1 = plus::return#2 + jmp __b2 + // main::@2 + __b2: + // [7] main::$1 = plus::return#1 + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // near call + sta SCREEN+1 + jmp __breturn + // main::@return + __breturn: + // [9] return + rts +} + // plus +// __register(A) char plus(__register(X) char a, __register(A) char b) +plus: { + // [11] add::a#0 = plus::a#2 -- vbuz1=vbuxx + stx.z add.a + // [12] add::b#0 = plus::b#2 + // [13] call add + jsr add + // [14] add::return#0 = add::return#1 + jmp __b1 + // plus::@1 + __b1: + // [15] plus::return#2 = add::return#0 + jmp __breturn + // plus::@return + __breturn: + // [16] return + rts +} + // add +// __register(A) char add(__zp(2) char a, __register(A) char b) +add: { + .label a = 2 + // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa + clc + adc.z a + jmp __breturn + // add::@return + __breturn: + // [18] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __b2 +Removing instruction jmp __breturn +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __breturn: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction plus_from_main: +Removing instruction __b1: +Removing instruction plus_from___b1: +Removing instruction __b2: +Removing instruction __breturn: +Removing instruction __b1: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +char add(char a , char b) +char add::a +char add::a#0 // a zp[1]:2 56.0 +char add::b +char add::b#0 // reg byte a 112.0 +char add::return +char add::return#0 // reg byte a 22.0 +char add::return#1 // reg byte a 37.33333333333333 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte x 11.0 +char plus::b +char plus::b#2 // reg byte a 5.5 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte a 3.75 + +reg byte x [ plus::a#2 ] +reg byte a [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +zp[1]:2 [ add::a#0 ] +reg byte a [ add::b#0 ] +reg byte a [ add::return#0 ] +reg byte a [ plus::return#2 ] +reg byte a [ add::return#1 ] + + +FINAL ASSEMBLER +Score: 60 + + // File Comments +/** + * @file call-banked-phi-case-1-near-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #1 + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-1-near-1.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // plus('0', 7) + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuaa=vbuc1 + lda #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- vbuxx=vbuc1 + ldx #'0' + jsr plus + // plus('0', 7) + // [2] plus::return#0 = plus::return#2 + // main::@1 + // [3] main::$0 = plus::return#0 + // SCREEN[0] = plus('0', 7) + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // plus('1', 6) + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuaa=vbuc1 + lda #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- vbuxx=vbuc1 + ldx #'1' + jsr plus + // plus('1', 6) + // [6] plus::return#1 = plus::return#2 + // main::@2 + // [7] main::$1 = plus::return#1 + // SCREEN[1] = plus('1', 6) + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // near call + sta SCREEN+1 + // main::@return + // } + // [9] return + rts +} + // plus +// __register(A) char plus(__register(X) char a, __register(A) char b) +plus: { + // add(a, b) + // [11] add::a#0 = plus::a#2 -- vbuz1=vbuxx + stx.z add.a + // [12] add::b#0 = plus::b#2 + // [13] call add + jsr add + // [14] add::return#0 = add::return#1 + // plus::@1 + // [15] plus::return#2 = add::return#0 + // plus::@return + // } + // [16] return + rts +} + // add +// __register(A) char add(__zp(2) char a, __register(A) char b) +add: { + .label a = 2 + // a+b + // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa + clc + adc.z a + // add::@return + // } + // [18] return + rts +} + // File Data + diff --git a/src/test/ref/call-banked-phi-case-1-near-1.sym b/src/test/ref/call-banked-phi-case-1-near-1.sym new file mode 100644 index 000000000..bfa554ac0 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-1-near-1.sym @@ -0,0 +1,33 @@ +__constant char * const SCREEN = (char *) 1024 +char add(char a , char b) +char add::a +char add::a#0 // a zp[1]:2 56.0 +char add::b +char add::b#0 // reg byte a 112.0 +char add::return +char add::return#0 // reg byte a 22.0 +char add::return#1 // reg byte a 37.33333333333333 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte x 11.0 +char plus::b +char plus::b#2 // reg byte a 5.5 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte a 3.75 + +reg byte x [ plus::a#2 ] +reg byte a [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +zp[1]:2 [ add::a#0 ] +reg byte a [ add::b#0 ] +reg byte a [ add::return#0 ] +reg byte a [ plus::return#2 ] +reg byte a [ add::return#1 ] diff --git a/src/test/ref/call-banked-phi-case-2-close-0.asm b/src/test/ref/call-banked-phi-case-2-close-0.asm new file mode 100644 index 000000000..efdf5fdb6 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-2-close-0.asm @@ -0,0 +1,96 @@ +/** + * @file call-banked-phi-case-2-close-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #2. + * Implementation using the __bank() directive. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + .file [name="call-banked-phi-case-2-close-0.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + .label SCREEN = $400 +.segment Code +main: { + // plus('0', 7) + ldx #7 + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('0', 7) + txa + // SCREEN[0] = plus('0', 7) + sta SCREEN + // plus('1', 6) + ldx #6 + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('1', 6) + txa + // SCREEN[1] = plus('1', 6) + // close call + sta SCREEN+1 + // } + rts +} +.segment RAM_Bank1 +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // a+b + stx.z $ff + clc + adc.z $ff + tax + // } + rts +} diff --git a/src/test/ref/call-banked-phi-case-2-close-0.cfg b/src/test/ref/call-banked-phi-case-2-close-0.cfg new file mode 100644 index 000000000..b5f3a4af5 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-2-close-0.cfg @@ -0,0 +1,30 @@ + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] plus::return#2 = plus::a#2 + plus::b#2 + to:plus::@return +plus::@return: scope:[plus] from plus + [12] return + to:@return diff --git a/src/test/ref/call-banked-phi-case-2-close-0.log b/src/test/ref/call-banked-phi-case-2-close-0.log new file mode 100644 index 000000000..96e2fadd5 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-2-close-0.log @@ -0,0 +1,520 @@ +Loading link script "call-banked-phi.ld" + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + plus::a#0 = '0' + plus::b#0 = 7 + call plus + plus::return#0 = plus::return#3 + to:main::@1 +main::@1: scope:[main] from main + plus::return#4 = phi( main/plus::return#0 ) + main::$0 = plus::return#4 + SCREEN[0] = main::$0 + plus::a#1 = '1' + plus::b#1 = 6 + call plus + plus::return#1 = plus::return#3 + to:main::@2 +main::@2: scope:[main] from main::@1 + plus::return#5 = phi( main::@1/plus::return#1 ) + main::$1 = plus::return#5 + SCREEN[1] = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 ) + plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 ) + plus::$0 = plus::a#2 + plus::b#2 + plus::return#2 = plus::$0 + to:plus::@return +plus::@return: scope:[plus] from plus + plus::return#6 = phi( plus/plus::return#2 ) + plus::return#3 = plus::return#6 + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant char * const SCREEN = (char *)$400 +void __start() +void main() +char main::$0 +char main::$1 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::$0 +char plus::a +char plus::a#0 +char plus::a#1 +char plus::a#2 +char plus::b +char plus::b#0 +char plus::b#1 +char plus::b#2 +char plus::return +char plus::return#0 +char plus::return#1 +char plus::return#2 +char plus::return#3 +char plus::return#4 +char plus::return#5 +char plus::return#6 + +Adding number conversion cast (unumber) 7 in plus::b#0 = 7 +Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0 +Adding number conversion cast (unumber) 6 in plus::b#1 = 6 +Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast plus::b#0 = (unumber)7 +Inlining cast plus::b#1 = (unumber)6 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 7 +Simplifying constant integer cast 0 +Simplifying constant integer cast 6 +Simplifying constant integer cast 1 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 7 +Finalized unsigned number type (char) 0 +Finalized unsigned number type (char) 6 +Finalized unsigned number type (char) 1 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias plus::return#0 = plus::return#4 +Alias plus::return#1 = plus::return#5 +Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3 +Successful SSA optimization Pass2AliasElimination +Constant plus::a#0 = '0' +Constant plus::b#0 = 7 +Constant plus::a#1 = '1' +Constant plus::b#1 = 6 +Successful SSA optimization Pass2ConstantIdentification +Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0 +Successful SSA optimization PassNSimplifyExpressionWithZero +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Inlining constant with var siblings plus::a#0 +Inlining constant with var siblings plus::b#0 +Inlining constant with var siblings plus::a#1 +Inlining constant with var siblings plus::b#1 +Constant inlined plus::b#1 = 6 +Constant inlined plus::b#0 = 7 +Constant inlined plus::a#1 = '1' +Constant inlined plus::a#0 = '0' +Successful SSA optimization Pass2ConstantInlining +Consolidated array index constant in *(SCREEN+1) +Successful SSA optimization Pass2ConstantAdditionElimination +Adding NOP phi() at start of main +CALL GRAPH +Calls in [main] to plus:1 plus:5 + +Created 2 initial phi equivalence classes +Coalesced down to 2 phi equivalence classes +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] plus::return#2 = plus::a#2 + plus::b#2 + to:plus::@return +plus::@return: scope:[plus] from plus + [12] return + to:@return + + +VARIABLE REGISTER WEIGHTS +void main() +char main::$0 // 4.0 +char main::$1 // 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // 11.0 +char plus::b +char plus::b#2 // 11.0 +char plus::return +char plus::return#0 // 4.0 +char plus::return#1 // 4.0 +char plus::return#2 // 3.75 + +Initial phi equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +Added variable plus::return#0 to live range equivalence class [ plus::return#0 ] +Added variable main::$0 to live range equivalence class [ main::$0 ] +Added variable plus::return#1 to live range equivalence class [ plus::return#1 ] +Added variable main::$1 to live range equivalence class [ main::$1 ] +Added variable plus::return#2 to live range equivalence class [ plus::return#2 ] +Complete equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +[ plus::return#0 ] +[ main::$0 ] +[ plus::return#1 ] +[ main::$1 ] +[ plus::return#2 ] +Allocated zp[1]:2 [ plus::a#2 ] +Allocated zp[1]:3 [ plus::b#2 ] +Allocated zp[1]:4 [ plus::return#0 ] +Allocated zp[1]:5 [ main::$0 ] +Allocated zp[1]:6 [ plus::return#1 ] +Allocated zp[1]:7 [ main::$1 ] +Allocated zp[1]:8 [ plus::return#2 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:8 [ plus::return#2 ] +Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [11] plus::return#2 = plus::a#2 + plus::b#2 [ plus::return#2 ] ( plus:1 [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } plus:5 [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [11] plus::return#2 = plus::a#2 + plus::b#2 [ plus::return#2 ] ( plus:1 [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } plus:5 [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Potential registers zp[1]:2 [ plus::a#2 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:3 [ plus::b#2 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:4 [ plus::return#0 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:5 [ main::$0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:6 [ plus::return#1 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:7 [ main::$1 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:8 [ plus::return#2 ] : zp[1]:8 , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [plus] 11: zp[1]:2 [ plus::a#2 ] 11: zp[1]:3 [ plus::b#2 ] 4: zp[1]:4 [ plus::return#0 ] 4: zp[1]:6 [ plus::return#1 ] 3.75: zp[1]:8 [ plus::return#2 ] +Uplift Scope [main] 4: zp[1]:5 [ main::$0 ] 4: zp[1]:7 [ main::$1 ] +Uplift Scope [] + +Uplifting [plus] best 141 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:8 [ plus::return#2 ] +Limited combination testing to 100 combinations of 768 possible. +Uplifting [main] best 129 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ] +Uplifting [] best 129 combination +Attempting to uplift remaining variables inzp[1]:8 [ plus::return#2 ] +Uplifting [plus] best 126 combination reg byte x [ plus::return#2 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +/** + * @file call-banked-phi-case-2-close-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #2. + * Implementation using the __bank() directive. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-2-close-0.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + plus_from_main: + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1 + ldx #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx + txa + jmp __b1 + // main::@1 + __b1: + // [3] main::$0 = plus::return#0 + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + plus_from___b1: + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1 + ldx #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx + txa + jmp __b2 + // main::@2 + __b2: + // [7] main::$1 = plus::return#1 + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // close call + sta SCREEN+1 + jmp __breturn + // main::@return + __breturn: + // [9] return + rts +} +.segment RAM_Bank1 + // plus +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // [11] plus::return#2 = plus::a#2 + plus::b#2 -- vbuxx=vbuaa_plus_vbuxx + stx.z $ff + clc + adc.z $ff + tax + jmp __breturn + // plus::@return + __breturn: + // [12] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __b2 +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction plus_from_main: +Removing instruction __b1: +Removing instruction plus_from___b1: +Removing instruction __b2: +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte a 11.0 +char plus::b +char plus::b#2 // reg byte x 11.0 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte x 3.75 + +reg byte a [ plus::a#2 ] +reg byte x [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +reg byte x [ plus::return#2 ] + + +FINAL ASSEMBLER +Score: 114 + + // File Comments +/** + * @file call-banked-phi-case-2-close-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #2. + * Implementation using the __bank() directive. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-2-close-0.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // plus('0', 7) + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1 + ldx #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('0', 7) + // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx + txa + // main::@1 + // [3] main::$0 = plus::return#0 + // SCREEN[0] = plus('0', 7) + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // plus('1', 6) + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1 + ldx #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('1', 6) + // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx + txa + // main::@2 + // [7] main::$1 = plus::return#1 + // SCREEN[1] = plus('1', 6) + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // close call + sta SCREEN+1 + // main::@return + // } + // [9] return + rts +} +.segment RAM_Bank1 + // plus +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // a+b + // [11] plus::return#2 = plus::a#2 + plus::b#2 -- vbuxx=vbuaa_plus_vbuxx + stx.z $ff + clc + adc.z $ff + tax + // plus::@return + // } + // [12] return + rts +} + // File Data + diff --git a/src/test/ref/call-banked-phi-case-2-close-0.sym b/src/test/ref/call-banked-phi-case-2-close-0.sym new file mode 100644 index 000000000..00ee4aca5 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-2-close-0.sym @@ -0,0 +1,21 @@ +__constant char * const SCREEN = (char *) 1024 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte a 11.0 +char plus::b +char plus::b#2 // reg byte x 11.0 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte x 3.75 + +reg byte a [ plus::a#2 ] +reg byte x [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +reg byte x [ plus::return#2 ] diff --git a/src/test/ref/call-banked-phi-case-2-close-1.asm b/src/test/ref/call-banked-phi-case-2-close-1.asm new file mode 100644 index 000000000..5a15ee9a6 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-2-close-1.asm @@ -0,0 +1,96 @@ +/** + * @file call-banked-phi-case-2-close-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #2. + * Implementation using the #pragma bank and nobank directives. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + .file [name="call-banked-phi-case-2-close-1.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + .label SCREEN = $400 +.segment Code +main: { + // plus('0', 7) + ldx #7 + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('0', 7) + txa + // SCREEN[0] = plus('0', 7) + sta SCREEN + // plus('1', 6) + ldx #6 + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('1', 6) + txa + // SCREEN[1] = plus('1', 6) + // close call + sta SCREEN+1 + // } + rts +} +.segment RAM_Bank1 +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // a+b + stx.z $ff + clc + adc.z $ff + tax + // } + rts +} diff --git a/src/test/ref/call-banked-phi-case-2-close-1.cfg b/src/test/ref/call-banked-phi-case-2-close-1.cfg new file mode 100644 index 000000000..b5f3a4af5 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-2-close-1.cfg @@ -0,0 +1,30 @@ + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] plus::return#2 = plus::a#2 + plus::b#2 + to:plus::@return +plus::@return: scope:[plus] from plus + [12] return + to:@return diff --git a/src/test/ref/call-banked-phi-case-2-close-1.log b/src/test/ref/call-banked-phi-case-2-close-1.log new file mode 100644 index 000000000..9f7544ae5 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-2-close-1.log @@ -0,0 +1,520 @@ +Loading link script "call-banked-phi.ld" + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + plus::a#0 = '0' + plus::b#0 = 7 + call plus + plus::return#0 = plus::return#3 + to:main::@1 +main::@1: scope:[main] from main + plus::return#4 = phi( main/plus::return#0 ) + main::$0 = plus::return#4 + SCREEN[0] = main::$0 + plus::a#1 = '1' + plus::b#1 = 6 + call plus + plus::return#1 = plus::return#3 + to:main::@2 +main::@2: scope:[main] from main::@1 + plus::return#5 = phi( main::@1/plus::return#1 ) + main::$1 = plus::return#5 + SCREEN[1] = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 ) + plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 ) + plus::$0 = plus::a#2 + plus::b#2 + plus::return#2 = plus::$0 + to:plus::@return +plus::@return: scope:[plus] from plus + plus::return#6 = phi( plus/plus::return#2 ) + plus::return#3 = plus::return#6 + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant char * const SCREEN = (char *)$400 +void __start() +void main() +char main::$0 +char main::$1 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::$0 +char plus::a +char plus::a#0 +char plus::a#1 +char plus::a#2 +char plus::b +char plus::b#0 +char plus::b#1 +char plus::b#2 +char plus::return +char plus::return#0 +char plus::return#1 +char plus::return#2 +char plus::return#3 +char plus::return#4 +char plus::return#5 +char plus::return#6 + +Adding number conversion cast (unumber) 7 in plus::b#0 = 7 +Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0 +Adding number conversion cast (unumber) 6 in plus::b#1 = 6 +Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast plus::b#0 = (unumber)7 +Inlining cast plus::b#1 = (unumber)6 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 7 +Simplifying constant integer cast 0 +Simplifying constant integer cast 6 +Simplifying constant integer cast 1 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 7 +Finalized unsigned number type (char) 0 +Finalized unsigned number type (char) 6 +Finalized unsigned number type (char) 1 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias plus::return#0 = plus::return#4 +Alias plus::return#1 = plus::return#5 +Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3 +Successful SSA optimization Pass2AliasElimination +Constant plus::a#0 = '0' +Constant plus::b#0 = 7 +Constant plus::a#1 = '1' +Constant plus::b#1 = 6 +Successful SSA optimization Pass2ConstantIdentification +Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0 +Successful SSA optimization PassNSimplifyExpressionWithZero +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Inlining constant with var siblings plus::a#0 +Inlining constant with var siblings plus::b#0 +Inlining constant with var siblings plus::a#1 +Inlining constant with var siblings plus::b#1 +Constant inlined plus::b#1 = 6 +Constant inlined plus::b#0 = 7 +Constant inlined plus::a#1 = '1' +Constant inlined plus::a#0 = '0' +Successful SSA optimization Pass2ConstantInlining +Consolidated array index constant in *(SCREEN+1) +Successful SSA optimization Pass2ConstantAdditionElimination +Adding NOP phi() at start of main +CALL GRAPH +Calls in [main] to plus:1 plus:5 + +Created 2 initial phi equivalence classes +Coalesced down to 2 phi equivalence classes +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] plus::return#2 = plus::a#2 + plus::b#2 + to:plus::@return +plus::@return: scope:[plus] from plus + [12] return + to:@return + + +VARIABLE REGISTER WEIGHTS +void main() +char main::$0 // 4.0 +char main::$1 // 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // 11.0 +char plus::b +char plus::b#2 // 11.0 +char plus::return +char plus::return#0 // 4.0 +char plus::return#1 // 4.0 +char plus::return#2 // 3.75 + +Initial phi equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +Added variable plus::return#0 to live range equivalence class [ plus::return#0 ] +Added variable main::$0 to live range equivalence class [ main::$0 ] +Added variable plus::return#1 to live range equivalence class [ plus::return#1 ] +Added variable main::$1 to live range equivalence class [ main::$1 ] +Added variable plus::return#2 to live range equivalence class [ plus::return#2 ] +Complete equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +[ plus::return#0 ] +[ main::$0 ] +[ plus::return#1 ] +[ main::$1 ] +[ plus::return#2 ] +Allocated zp[1]:2 [ plus::a#2 ] +Allocated zp[1]:3 [ plus::b#2 ] +Allocated zp[1]:4 [ plus::return#0 ] +Allocated zp[1]:5 [ main::$0 ] +Allocated zp[1]:6 [ plus::return#1 ] +Allocated zp[1]:7 [ main::$1 ] +Allocated zp[1]:8 [ plus::return#2 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:8 [ plus::return#2 ] +Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [11] plus::return#2 = plus::a#2 + plus::b#2 [ plus::return#2 ] ( plus:1 [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } plus:5 [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [11] plus::return#2 = plus::a#2 + plus::b#2 [ plus::return#2 ] ( plus:1 [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } plus:5 [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Potential registers zp[1]:2 [ plus::a#2 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:3 [ plus::b#2 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:4 [ plus::return#0 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:5 [ main::$0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:6 [ plus::return#1 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:7 [ main::$1 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:8 [ plus::return#2 ] : zp[1]:8 , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [plus] 11: zp[1]:2 [ plus::a#2 ] 11: zp[1]:3 [ plus::b#2 ] 4: zp[1]:4 [ plus::return#0 ] 4: zp[1]:6 [ plus::return#1 ] 3.75: zp[1]:8 [ plus::return#2 ] +Uplift Scope [main] 4: zp[1]:5 [ main::$0 ] 4: zp[1]:7 [ main::$1 ] +Uplift Scope [] + +Uplifting [plus] best 141 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:8 [ plus::return#2 ] +Limited combination testing to 100 combinations of 768 possible. +Uplifting [main] best 129 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ] +Uplifting [] best 129 combination +Attempting to uplift remaining variables inzp[1]:8 [ plus::return#2 ] +Uplifting [plus] best 126 combination reg byte x [ plus::return#2 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +/** + * @file call-banked-phi-case-2-close-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #2. + * Implementation using the #pragma bank and nobank directives. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-2-close-1.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + plus_from_main: + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1 + ldx #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx + txa + jmp __b1 + // main::@1 + __b1: + // [3] main::$0 = plus::return#0 + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + plus_from___b1: + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1 + ldx #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx + txa + jmp __b2 + // main::@2 + __b2: + // [7] main::$1 = plus::return#1 + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // close call + sta SCREEN+1 + jmp __breturn + // main::@return + __breturn: + // [9] return + rts +} +.segment RAM_Bank1 + // plus +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // [11] plus::return#2 = plus::a#2 + plus::b#2 -- vbuxx=vbuaa_plus_vbuxx + stx.z $ff + clc + adc.z $ff + tax + jmp __breturn + // plus::@return + __breturn: + // [12] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __b2 +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction plus_from_main: +Removing instruction __b1: +Removing instruction plus_from___b1: +Removing instruction __b2: +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte a 11.0 +char plus::b +char plus::b#2 // reg byte x 11.0 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte x 3.75 + +reg byte a [ plus::a#2 ] +reg byte x [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +reg byte x [ plus::return#2 ] + + +FINAL ASSEMBLER +Score: 114 + + // File Comments +/** + * @file call-banked-phi-case-2-close-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #2. + * Implementation using the #pragma bank and nobank directives. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-2-close-1.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // plus('0', 7) + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1 + ldx #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('0', 7) + // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx + txa + // main::@1 + // [3] main::$0 = plus::return#0 + // SCREEN[0] = plus('0', 7) + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // plus('1', 6) + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1 + ldx #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('1', 6) + // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx + txa + // main::@2 + // [7] main::$1 = plus::return#1 + // SCREEN[1] = plus('1', 6) + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // close call + sta SCREEN+1 + // main::@return + // } + // [9] return + rts +} +.segment RAM_Bank1 + // plus +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // a+b + // [11] plus::return#2 = plus::a#2 + plus::b#2 -- vbuxx=vbuaa_plus_vbuxx + stx.z $ff + clc + adc.z $ff + tax + // plus::@return + // } + // [12] return + rts +} + // File Data + diff --git a/src/test/ref/call-banked-phi-case-2-close-1.sym b/src/test/ref/call-banked-phi-case-2-close-1.sym new file mode 100644 index 000000000..00ee4aca5 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-2-close-1.sym @@ -0,0 +1,21 @@ +__constant char * const SCREEN = (char *) 1024 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte a 11.0 +char plus::b +char plus::b#2 // reg byte x 11.0 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte x 3.75 + +reg byte a [ plus::a#2 ] +reg byte x [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +reg byte x [ plus::return#2 ] diff --git a/src/test/ref/call-banked-phi-case-3-near-0.asm b/src/test/ref/call-banked-phi-case-3-near-0.asm new file mode 100644 index 000000000..60fa654fa --- /dev/null +++ b/src/test/ref/call-banked-phi-case-3-near-0.asm @@ -0,0 +1,106 @@ +/** + * @file call-banked-phi-case-3-near-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #3. + * Implementation using the __bank() directive. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + .file [name="call-banked-phi-case-3-near-0.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + .label SCREEN = $400 +.segment Code +main: { + // plus('0', 7) + ldx #7 + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('0', 7) + txa + // SCREEN[0] = plus('0', 7) + sta SCREEN + // plus('1', 6) + ldx #6 + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('1', 6) + txa + // SCREEN[1] = plus('1', 6) + // close call + sta SCREEN+1 + // } + rts +} +.segment RAM_Bank1 +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // add(a, b) + sta.z add.a + txa + jsr add + tax + // } + rts +} +.segment Code +// __register(A) char add(__zp(2) char a, __register(A) char b) +add: { + .label a = 2 + // a+b + clc + adc.z a + // } + rts +} diff --git a/src/test/ref/call-banked-phi-case-3-near-0.cfg b/src/test/ref/call-banked-phi-case-3-near-0.cfg new file mode 100644 index 000000000..f7ec35c09 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-3-near-0.cfg @@ -0,0 +1,44 @@ + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] add::a#0 = plus::a#2 + [12] add::b#0 = plus::b#2 + [13] call add + [14] add::return#0 = add::return#1 + to:plus::@1 +plus::@1: scope:[plus] from plus + [15] plus::return#2 = add::return#0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + [16] return + to:@return + +char add(char a , char b) +add: scope:[add] from plus + [17] add::return#1 = add::a#0 + add::b#0 + to:add::@return +add::@return: scope:[add] from add + [18] return + to:@return diff --git a/src/test/ref/call-banked-phi-case-3-near-0.log b/src/test/ref/call-banked-phi-case-3-near-0.log new file mode 100644 index 000000000..63c05360e --- /dev/null +++ b/src/test/ref/call-banked-phi-case-3-near-0.log @@ -0,0 +1,660 @@ +Loading link script "call-banked-phi.ld" + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + plus::a#0 = '0' + plus::b#0 = 7 + call plus + plus::return#0 = plus::return#3 + to:main::@1 +main::@1: scope:[main] from main + plus::return#4 = phi( main/plus::return#0 ) + main::$0 = plus::return#4 + SCREEN[0] = main::$0 + plus::a#1 = '1' + plus::b#1 = 6 + call plus + plus::return#1 = plus::return#3 + to:main::@2 +main::@2: scope:[main] from main::@1 + plus::return#5 = phi( main::@1/plus::return#1 ) + main::$1 = plus::return#5 + SCREEN[1] = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 ) + plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 ) + add::a#0 = plus::a#2 + add::b#0 = plus::b#2 + call add + add::return#0 = add::return#2 + to:plus::@1 +plus::@1: scope:[plus] from plus + add::return#3 = phi( plus/add::return#0 ) + plus::$0 = add::return#3 + plus::return#2 = plus::$0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + plus::return#6 = phi( plus::@1/plus::return#2 ) + plus::return#3 = plus::return#6 + return + to:@return + +char add(char a , char b) +add: scope:[add] from plus + add::b#1 = phi( plus/add::b#0 ) + add::a#1 = phi( plus/add::a#0 ) + add::$0 = add::a#1 + add::b#1 + add::return#1 = add::$0 + to:add::@return +add::@return: scope:[add] from add + add::return#4 = phi( add/add::return#1 ) + add::return#2 = add::return#4 + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant char * const SCREEN = (char *)$400 +void __start() +char add(char a , char b) +char add::$0 +char add::a +char add::a#0 +char add::a#1 +char add::b +char add::b#0 +char add::b#1 +char add::return +char add::return#0 +char add::return#1 +char add::return#2 +char add::return#3 +char add::return#4 +void main() +char main::$0 +char main::$1 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::$0 +char plus::a +char plus::a#0 +char plus::a#1 +char plus::a#2 +char plus::b +char plus::b#0 +char plus::b#1 +char plus::b#2 +char plus::return +char plus::return#0 +char plus::return#1 +char plus::return#2 +char plus::return#3 +char plus::return#4 +char plus::return#5 +char plus::return#6 + +Adding number conversion cast (unumber) 7 in plus::b#0 = 7 +Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0 +Adding number conversion cast (unumber) 6 in plus::b#1 = 6 +Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast plus::b#0 = (unumber)7 +Inlining cast plus::b#1 = (unumber)6 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 7 +Simplifying constant integer cast 0 +Simplifying constant integer cast 6 +Simplifying constant integer cast 1 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 7 +Finalized unsigned number type (char) 0 +Finalized unsigned number type (char) 6 +Finalized unsigned number type (char) 1 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias plus::return#0 = plus::return#4 +Alias plus::return#1 = plus::return#5 +Alias add::return#0 = add::return#3 +Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3 +Alias add::return#1 = add::$0 add::return#4 add::return#2 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values add::a#1 add::a#0 +Identical Phi Values add::b#1 add::b#0 +Successful SSA optimization Pass2IdenticalPhiElimination +Constant plus::a#0 = '0' +Constant plus::b#0 = 7 +Constant plus::a#1 = '1' +Constant plus::b#1 = 6 +Successful SSA optimization Pass2ConstantIdentification +Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0 +Successful SSA optimization PassNSimplifyExpressionWithZero +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Inlining constant with var siblings plus::a#0 +Inlining constant with var siblings plus::b#0 +Inlining constant with var siblings plus::a#1 +Inlining constant with var siblings plus::b#1 +Constant inlined plus::b#1 = 6 +Constant inlined plus::b#0 = 7 +Constant inlined plus::a#1 = '1' +Constant inlined plus::a#0 = '0' +Successful SSA optimization Pass2ConstantInlining +Consolidated array index constant in *(SCREEN+1) +Successful SSA optimization Pass2ConstantAdditionElimination +Adding NOP phi() at start of main +CALL GRAPH +Calls in [main] to plus:1 plus:5 +Calls in [plus] to add:13 + +Created 2 initial phi equivalence classes +Coalesced down to 2 phi equivalence classes +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] add::a#0 = plus::a#2 + [12] add::b#0 = plus::b#2 + [13] call add + [14] add::return#0 = add::return#1 + to:plus::@1 +plus::@1: scope:[plus] from plus + [15] plus::return#2 = add::return#0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + [16] return + to:@return + +char add(char a , char b) +add: scope:[add] from plus + [17] add::return#1 = add::a#0 + add::b#0 + to:add::@return +add::@return: scope:[add] from add + [18] return + to:@return + + +VARIABLE REGISTER WEIGHTS +char add(char a , char b) +char add::a +char add::a#0 // 56.0 +char add::b +char add::b#0 // 112.0 +char add::return +char add::return#0 // 22.0 +char add::return#1 // 37.33333333333333 +void main() +char main::$0 // 4.0 +char main::$1 // 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // 11.0 +char plus::b +char plus::b#2 // 5.5 +char plus::return +char plus::return#0 // 4.0 +char plus::return#1 // 4.0 +char plus::return#2 // 3.75 + +Initial phi equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +Added variable plus::return#0 to live range equivalence class [ plus::return#0 ] +Added variable main::$0 to live range equivalence class [ main::$0 ] +Added variable plus::return#1 to live range equivalence class [ plus::return#1 ] +Added variable main::$1 to live range equivalence class [ main::$1 ] +Added variable add::a#0 to live range equivalence class [ add::a#0 ] +Added variable add::b#0 to live range equivalence class [ add::b#0 ] +Added variable add::return#0 to live range equivalence class [ add::return#0 ] +Added variable plus::return#2 to live range equivalence class [ plus::return#2 ] +Added variable add::return#1 to live range equivalence class [ add::return#1 ] +Complete equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +[ plus::return#0 ] +[ main::$0 ] +[ plus::return#1 ] +[ main::$1 ] +[ add::a#0 ] +[ add::b#0 ] +[ add::return#0 ] +[ plus::return#2 ] +[ add::return#1 ] +Allocated zp[1]:2 [ add::b#0 ] +Allocated zp[1]:3 [ add::a#0 ] +Allocated zp[1]:4 [ add::return#1 ] +Allocated zp[1]:5 [ add::return#0 ] +Allocated zp[1]:6 [ plus::a#2 ] +Allocated zp[1]:7 [ plus::b#2 ] +Allocated zp[1]:8 [ plus::return#0 ] +Allocated zp[1]:9 [ main::$0 ] +Allocated zp[1]:10 [ plus::return#1 ] +Allocated zp[1]:11 [ main::$1 ] +Allocated zp[1]:12 [ plus::return#2 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ] +Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a +Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a +Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y , +Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ] +Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ] +Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ] +Uplift Scope [] + +Uplifting [add] best 189 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte a [ add::return#1 ] reg byte a [ add::return#0 ] +Limited combination testing to 100 combinations of 256 possible. +Uplifting [plus] best 161 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ] +Limited combination testing to 100 combinations of 768 possible. +Uplifting [main] best 149 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ] +Uplifting [] best 149 combination +Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ] +Uplifting [add] best 149 combination zp[1]:3 [ add::a#0 ] +Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ] +Uplifting [plus] best 146 combination reg byte x [ plus::return#2 ] +Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +/** + * @file call-banked-phi-case-3-near-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #3. + * Implementation using the __bank() directive. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-3-near-0.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + plus_from_main: + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1 + ldx #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx + txa + jmp __b1 + // main::@1 + __b1: + // [3] main::$0 = plus::return#0 + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + plus_from___b1: + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1 + ldx #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx + txa + jmp __b2 + // main::@2 + __b2: + // [7] main::$1 = plus::return#1 + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // close call + sta SCREEN+1 + jmp __breturn + // main::@return + __breturn: + // [9] return + rts +} +.segment RAM_Bank1 + // plus +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa + sta.z add.a + // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx + txa + // [13] call add + jsr add + // [14] add::return#0 = add::return#1 + jmp __b1 + // plus::@1 + __b1: + // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa + tax + jmp __breturn + // plus::@return + __breturn: + // [16] return + rts +} +.segment Code + // add +// __register(A) char add(__zp(2) char a, __register(A) char b) +add: { + .label a = 2 + // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa + clc + adc.z a + jmp __breturn + // add::@return + __breturn: + // [18] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __b2 +Removing instruction jmp __breturn +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction plus_from_main: +Removing instruction __b1: +Removing instruction plus_from___b1: +Removing instruction __b2: +Removing instruction __breturn: +Removing instruction __b1: +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +char add(char a , char b) +char add::a +char add::a#0 // a zp[1]:2 56.0 +char add::b +char add::b#0 // reg byte a 112.0 +char add::return +char add::return#0 // reg byte a 22.0 +char add::return#1 // reg byte a 37.33333333333333 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte a 11.0 +char plus::b +char plus::b#2 // reg byte x 5.5 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte x 3.75 + +reg byte a [ plus::a#2 ] +reg byte x [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +zp[1]:2 [ add::a#0 ] +reg byte a [ add::b#0 ] +reg byte a [ add::return#0 ] +reg byte x [ plus::return#2 ] +reg byte a [ add::return#1 ] + + +FINAL ASSEMBLER +Score: 128 + + // File Comments +/** + * @file call-banked-phi-case-3-near-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #3. + * Implementation using the __bank() directive. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-3-near-0.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // plus('0', 7) + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1 + ldx #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('0', 7) + // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx + txa + // main::@1 + // [3] main::$0 = plus::return#0 + // SCREEN[0] = plus('0', 7) + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // plus('1', 6) + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1 + ldx #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('1', 6) + // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx + txa + // main::@2 + // [7] main::$1 = plus::return#1 + // SCREEN[1] = plus('1', 6) + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // close call + sta SCREEN+1 + // main::@return + // } + // [9] return + rts +} +.segment RAM_Bank1 + // plus +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // add(a, b) + // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa + sta.z add.a + // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx + txa + // [13] call add + jsr add + // [14] add::return#0 = add::return#1 + // plus::@1 + // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa + tax + // plus::@return + // } + // [16] return + rts +} +.segment Code + // add +// __register(A) char add(__zp(2) char a, __register(A) char b) +add: { + .label a = 2 + // a+b + // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa + clc + adc.z a + // add::@return + // } + // [18] return + rts +} + // File Data + diff --git a/src/test/ref/call-banked-phi-case-3-near-0.sym b/src/test/ref/call-banked-phi-case-3-near-0.sym new file mode 100644 index 000000000..658eb6563 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-3-near-0.sym @@ -0,0 +1,33 @@ +__constant char * const SCREEN = (char *) 1024 +char add(char a , char b) +char add::a +char add::a#0 // a zp[1]:2 56.0 +char add::b +char add::b#0 // reg byte a 112.0 +char add::return +char add::return#0 // reg byte a 22.0 +char add::return#1 // reg byte a 37.33333333333333 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte a 11.0 +char plus::b +char plus::b#2 // reg byte x 5.5 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte x 3.75 + +reg byte a [ plus::a#2 ] +reg byte x [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +zp[1]:2 [ add::a#0 ] +reg byte a [ add::b#0 ] +reg byte a [ add::return#0 ] +reg byte x [ plus::return#2 ] +reg byte a [ add::return#1 ] diff --git a/src/test/ref/call-banked-phi-case-3-near-1.asm b/src/test/ref/call-banked-phi-case-3-near-1.asm new file mode 100644 index 000000000..9ffca6b4a --- /dev/null +++ b/src/test/ref/call-banked-phi-case-3-near-1.asm @@ -0,0 +1,106 @@ +/** + * @file call-banked-phi-case-3-near-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #3. + * Implementation using the #pragma bank and nobank directives. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + .file [name="call-banked-phi-case-3-near-1.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + .label SCREEN = $400 +.segment Code +main: { + // plus('0', 7) + ldx #7 + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('0', 7) + txa + // SCREEN[0] = plus('0', 7) + sta SCREEN + // plus('1', 6) + ldx #6 + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('1', 6) + txa + // SCREEN[1] = plus('1', 6) + // close call + sta SCREEN+1 + // } + rts +} +.segment RAM_Bank1 +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // add(a, b) + sta.z add.a + txa + jsr add + tax + // } + rts +} +.segment Code +// __register(A) char add(__zp(2) char a, __register(A) char b) +add: { + .label a = 2 + // a+b + clc + adc.z a + // } + rts +} diff --git a/src/test/ref/call-banked-phi-case-3-near-1.cfg b/src/test/ref/call-banked-phi-case-3-near-1.cfg new file mode 100644 index 000000000..f7ec35c09 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-3-near-1.cfg @@ -0,0 +1,44 @@ + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] add::a#0 = plus::a#2 + [12] add::b#0 = plus::b#2 + [13] call add + [14] add::return#0 = add::return#1 + to:plus::@1 +plus::@1: scope:[plus] from plus + [15] plus::return#2 = add::return#0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + [16] return + to:@return + +char add(char a , char b) +add: scope:[add] from plus + [17] add::return#1 = add::a#0 + add::b#0 + to:add::@return +add::@return: scope:[add] from add + [18] return + to:@return diff --git a/src/test/ref/call-banked-phi-case-3-near-1.log b/src/test/ref/call-banked-phi-case-3-near-1.log new file mode 100644 index 000000000..7b34ab84a --- /dev/null +++ b/src/test/ref/call-banked-phi-case-3-near-1.log @@ -0,0 +1,660 @@ +Loading link script "call-banked-phi.ld" + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + plus::a#0 = '0' + plus::b#0 = 7 + call plus + plus::return#0 = plus::return#3 + to:main::@1 +main::@1: scope:[main] from main + plus::return#4 = phi( main/plus::return#0 ) + main::$0 = plus::return#4 + SCREEN[0] = main::$0 + plus::a#1 = '1' + plus::b#1 = 6 + call plus + plus::return#1 = plus::return#3 + to:main::@2 +main::@2: scope:[main] from main::@1 + plus::return#5 = phi( main::@1/plus::return#1 ) + main::$1 = plus::return#5 + SCREEN[1] = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 ) + plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 ) + add::a#0 = plus::a#2 + add::b#0 = plus::b#2 + call add + add::return#0 = add::return#2 + to:plus::@1 +plus::@1: scope:[plus] from plus + add::return#3 = phi( plus/add::return#0 ) + plus::$0 = add::return#3 + plus::return#2 = plus::$0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + plus::return#6 = phi( plus::@1/plus::return#2 ) + plus::return#3 = plus::return#6 + return + to:@return + +char add(char a , char b) +add: scope:[add] from plus + add::b#1 = phi( plus/add::b#0 ) + add::a#1 = phi( plus/add::a#0 ) + add::$0 = add::a#1 + add::b#1 + add::return#1 = add::$0 + to:add::@return +add::@return: scope:[add] from add + add::return#4 = phi( add/add::return#1 ) + add::return#2 = add::return#4 + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant char * const SCREEN = (char *)$400 +void __start() +char add(char a , char b) +char add::$0 +char add::a +char add::a#0 +char add::a#1 +char add::b +char add::b#0 +char add::b#1 +char add::return +char add::return#0 +char add::return#1 +char add::return#2 +char add::return#3 +char add::return#4 +void main() +char main::$0 +char main::$1 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::$0 +char plus::a +char plus::a#0 +char plus::a#1 +char plus::a#2 +char plus::b +char plus::b#0 +char plus::b#1 +char plus::b#2 +char plus::return +char plus::return#0 +char plus::return#1 +char plus::return#2 +char plus::return#3 +char plus::return#4 +char plus::return#5 +char plus::return#6 + +Adding number conversion cast (unumber) 7 in plus::b#0 = 7 +Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0 +Adding number conversion cast (unumber) 6 in plus::b#1 = 6 +Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast plus::b#0 = (unumber)7 +Inlining cast plus::b#1 = (unumber)6 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 7 +Simplifying constant integer cast 0 +Simplifying constant integer cast 6 +Simplifying constant integer cast 1 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 7 +Finalized unsigned number type (char) 0 +Finalized unsigned number type (char) 6 +Finalized unsigned number type (char) 1 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias plus::return#0 = plus::return#4 +Alias plus::return#1 = plus::return#5 +Alias add::return#0 = add::return#3 +Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3 +Alias add::return#1 = add::$0 add::return#4 add::return#2 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values add::a#1 add::a#0 +Identical Phi Values add::b#1 add::b#0 +Successful SSA optimization Pass2IdenticalPhiElimination +Constant plus::a#0 = '0' +Constant plus::b#0 = 7 +Constant plus::a#1 = '1' +Constant plus::b#1 = 6 +Successful SSA optimization Pass2ConstantIdentification +Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0 +Successful SSA optimization PassNSimplifyExpressionWithZero +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Inlining constant with var siblings plus::a#0 +Inlining constant with var siblings plus::b#0 +Inlining constant with var siblings plus::a#1 +Inlining constant with var siblings plus::b#1 +Constant inlined plus::b#1 = 6 +Constant inlined plus::b#0 = 7 +Constant inlined plus::a#1 = '1' +Constant inlined plus::a#0 = '0' +Successful SSA optimization Pass2ConstantInlining +Consolidated array index constant in *(SCREEN+1) +Successful SSA optimization Pass2ConstantAdditionElimination +Adding NOP phi() at start of main +CALL GRAPH +Calls in [main] to plus:1 plus:5 +Calls in [plus] to add:13 + +Created 2 initial phi equivalence classes +Coalesced down to 2 phi equivalence classes +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] add::a#0 = plus::a#2 + [12] add::b#0 = plus::b#2 + [13] call add + [14] add::return#0 = add::return#1 + to:plus::@1 +plus::@1: scope:[plus] from plus + [15] plus::return#2 = add::return#0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + [16] return + to:@return + +char add(char a , char b) +add: scope:[add] from plus + [17] add::return#1 = add::a#0 + add::b#0 + to:add::@return +add::@return: scope:[add] from add + [18] return + to:@return + + +VARIABLE REGISTER WEIGHTS +char add(char a , char b) +char add::a +char add::a#0 // 56.0 +char add::b +char add::b#0 // 112.0 +char add::return +char add::return#0 // 22.0 +char add::return#1 // 37.33333333333333 +void main() +char main::$0 // 4.0 +char main::$1 // 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // 11.0 +char plus::b +char plus::b#2 // 5.5 +char plus::return +char plus::return#0 // 4.0 +char plus::return#1 // 4.0 +char plus::return#2 // 3.75 + +Initial phi equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +Added variable plus::return#0 to live range equivalence class [ plus::return#0 ] +Added variable main::$0 to live range equivalence class [ main::$0 ] +Added variable plus::return#1 to live range equivalence class [ plus::return#1 ] +Added variable main::$1 to live range equivalence class [ main::$1 ] +Added variable add::a#0 to live range equivalence class [ add::a#0 ] +Added variable add::b#0 to live range equivalence class [ add::b#0 ] +Added variable add::return#0 to live range equivalence class [ add::return#0 ] +Added variable plus::return#2 to live range equivalence class [ plus::return#2 ] +Added variable add::return#1 to live range equivalence class [ add::return#1 ] +Complete equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +[ plus::return#0 ] +[ main::$0 ] +[ plus::return#1 ] +[ main::$1 ] +[ add::a#0 ] +[ add::b#0 ] +[ add::return#0 ] +[ plus::return#2 ] +[ add::return#1 ] +Allocated zp[1]:2 [ add::b#0 ] +Allocated zp[1]:3 [ add::a#0 ] +Allocated zp[1]:4 [ add::return#1 ] +Allocated zp[1]:5 [ add::return#0 ] +Allocated zp[1]:6 [ plus::a#2 ] +Allocated zp[1]:7 [ plus::b#2 ] +Allocated zp[1]:8 [ plus::return#0 ] +Allocated zp[1]:9 [ main::$0 ] +Allocated zp[1]:10 [ plus::return#1 ] +Allocated zp[1]:11 [ main::$1 ] +Allocated zp[1]:12 [ plus::return#2 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ] +Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a +Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a +Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y , +Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ] +Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ] +Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ] +Uplift Scope [] + +Uplifting [add] best 189 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte a [ add::return#1 ] reg byte a [ add::return#0 ] +Limited combination testing to 100 combinations of 256 possible. +Uplifting [plus] best 161 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ] +Limited combination testing to 100 combinations of 768 possible. +Uplifting [main] best 149 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ] +Uplifting [] best 149 combination +Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ] +Uplifting [add] best 149 combination zp[1]:3 [ add::a#0 ] +Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ] +Uplifting [plus] best 146 combination reg byte x [ plus::return#2 ] +Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +/** + * @file call-banked-phi-case-3-near-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #3. + * Implementation using the #pragma bank and nobank directives. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-3-near-1.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + plus_from_main: + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1 + ldx #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx + txa + jmp __b1 + // main::@1 + __b1: + // [3] main::$0 = plus::return#0 + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + plus_from___b1: + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1 + ldx #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx + txa + jmp __b2 + // main::@2 + __b2: + // [7] main::$1 = plus::return#1 + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // close call + sta SCREEN+1 + jmp __breturn + // main::@return + __breturn: + // [9] return + rts +} +.segment RAM_Bank1 + // plus +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa + sta.z add.a + // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx + txa + // [13] call add + jsr add + // [14] add::return#0 = add::return#1 + jmp __b1 + // plus::@1 + __b1: + // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa + tax + jmp __breturn + // plus::@return + __breturn: + // [16] return + rts +} +.segment Code + // add +// __register(A) char add(__zp(2) char a, __register(A) char b) +add: { + .label a = 2 + // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa + clc + adc.z a + jmp __breturn + // add::@return + __breturn: + // [18] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __b2 +Removing instruction jmp __breturn +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction plus_from_main: +Removing instruction __b1: +Removing instruction plus_from___b1: +Removing instruction __b2: +Removing instruction __breturn: +Removing instruction __b1: +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +char add(char a , char b) +char add::a +char add::a#0 // a zp[1]:2 56.0 +char add::b +char add::b#0 // reg byte a 112.0 +char add::return +char add::return#0 // reg byte a 22.0 +char add::return#1 // reg byte a 37.33333333333333 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte a 11.0 +char plus::b +char plus::b#2 // reg byte x 5.5 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte x 3.75 + +reg byte a [ plus::a#2 ] +reg byte x [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +zp[1]:2 [ add::a#0 ] +reg byte a [ add::b#0 ] +reg byte a [ add::return#0 ] +reg byte x [ plus::return#2 ] +reg byte a [ add::return#1 ] + + +FINAL ASSEMBLER +Score: 128 + + // File Comments +/** + * @file call-banked-phi-case-3-near-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #3. + * Implementation using the #pragma bank and nobank directives. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-3-near-1.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // plus('0', 7) + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1 + ldx #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('0', 7) + // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx + txa + // main::@1 + // [3] main::$0 = plus::return#0 + // SCREEN[0] = plus('0', 7) + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // plus('1', 6) + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1 + ldx #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('1', 6) + // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx + txa + // main::@2 + // [7] main::$1 = plus::return#1 + // SCREEN[1] = plus('1', 6) + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // close call + sta SCREEN+1 + // main::@return + // } + // [9] return + rts +} +.segment RAM_Bank1 + // plus +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // add(a, b) + // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa + sta.z add.a + // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx + txa + // [13] call add + jsr add + // [14] add::return#0 = add::return#1 + // plus::@1 + // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa + tax + // plus::@return + // } + // [16] return + rts +} +.segment Code + // add +// __register(A) char add(__zp(2) char a, __register(A) char b) +add: { + .label a = 2 + // a+b + // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa + clc + adc.z a + // add::@return + // } + // [18] return + rts +} + // File Data + diff --git a/src/test/ref/call-banked-phi-case-3-near-1.sym b/src/test/ref/call-banked-phi-case-3-near-1.sym new file mode 100644 index 000000000..658eb6563 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-3-near-1.sym @@ -0,0 +1,33 @@ +__constant char * const SCREEN = (char *) 1024 +char add(char a , char b) +char add::a +char add::a#0 // a zp[1]:2 56.0 +char add::b +char add::b#0 // reg byte a 112.0 +char add::return +char add::return#0 // reg byte a 22.0 +char add::return#1 // reg byte a 37.33333333333333 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte a 11.0 +char plus::b +char plus::b#2 // reg byte x 5.5 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte x 3.75 + +reg byte a [ plus::a#2 ] +reg byte x [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +zp[1]:2 [ add::a#0 ] +reg byte a [ add::b#0 ] +reg byte a [ add::return#0 ] +reg byte x [ plus::return#2 ] +reg byte a [ add::return#1 ] diff --git a/src/test/ref/call-banked-phi-case-4-near-0.asm b/src/test/ref/call-banked-phi-case-4-near-0.asm new file mode 100644 index 000000000..a7a1cbbf4 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-4-near-0.asm @@ -0,0 +1,106 @@ +/** + * @file call-banked-phi-case-4-near-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #4. + * Implementation using the __bank() directive. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + .file [name="call-banked-phi-case-4-near-0.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + .label SCREEN = $400 +.segment Code +main: { + // plus('0', 7) + ldx #7 + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('0', 7) + txa + // SCREEN[0] = plus('0', 7) + sta SCREEN + // plus('1', 6) + ldx #6 + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('1', 6) + txa + // SCREEN[1] = plus('1', 6) + // close call + sta SCREEN+1 + // } + rts +} +.segment RAM_Bank1 +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // add(a, b) + sta.z add.a + txa + jsr add + tax + // } + rts +} +// __register(A) char add(__zp(2) char a, __register(A) char b) +// __bank(cx16_ram, 1) +add: { + .label a = 2 + // a+b + clc + adc.z a + // } + rts +} diff --git a/src/test/ref/call-banked-phi-case-4-near-0.cfg b/src/test/ref/call-banked-phi-case-4-near-0.cfg new file mode 100644 index 000000000..bb224fbfe --- /dev/null +++ b/src/test/ref/call-banked-phi-case-4-near-0.cfg @@ -0,0 +1,44 @@ + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] add::a#0 = plus::a#2 + [12] add::b#0 = plus::b#2 + [13] call add + [14] add::return#0 = add::return#1 + to:plus::@1 +plus::@1: scope:[plus] from plus + [15] plus::return#2 = add::return#0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + [16] return + to:@return + +__bank(cx16_ram, 1) char add(char a , char b) +add: scope:[add] from plus + [17] add::return#1 = add::a#0 + add::b#0 + to:add::@return +add::@return: scope:[add] from add + [18] return + to:@return diff --git a/src/test/ref/call-banked-phi-case-4-near-0.log b/src/test/ref/call-banked-phi-case-4-near-0.log new file mode 100644 index 000000000..5532553ee --- /dev/null +++ b/src/test/ref/call-banked-phi-case-4-near-0.log @@ -0,0 +1,660 @@ +Loading link script "call-banked-phi.ld" + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + plus::a#0 = '0' + plus::b#0 = 7 + call plus + plus::return#0 = plus::return#3 + to:main::@1 +main::@1: scope:[main] from main + plus::return#4 = phi( main/plus::return#0 ) + main::$0 = plus::return#4 + SCREEN[0] = main::$0 + plus::a#1 = '1' + plus::b#1 = 6 + call plus + plus::return#1 = plus::return#3 + to:main::@2 +main::@2: scope:[main] from main::@1 + plus::return#5 = phi( main::@1/plus::return#1 ) + main::$1 = plus::return#5 + SCREEN[1] = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 ) + plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 ) + add::a#0 = plus::a#2 + add::b#0 = plus::b#2 + call add + add::return#0 = add::return#2 + to:plus::@1 +plus::@1: scope:[plus] from plus + add::return#3 = phi( plus/add::return#0 ) + plus::$0 = add::return#3 + plus::return#2 = plus::$0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + plus::return#6 = phi( plus::@1/plus::return#2 ) + plus::return#3 = plus::return#6 + return + to:@return + +__bank(cx16_ram, 1) char add(char a , char b) +add: scope:[add] from plus + add::b#1 = phi( plus/add::b#0 ) + add::a#1 = phi( plus/add::a#0 ) + add::$0 = add::a#1 + add::b#1 + add::return#1 = add::$0 + to:add::@return +add::@return: scope:[add] from add + add::return#4 = phi( add/add::return#1 ) + add::return#2 = add::return#4 + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant char * const SCREEN = (char *)$400 +void __start() +__bank(cx16_ram, 1) char add(char a , char b) +char add::$0 +char add::a +char add::a#0 +char add::a#1 +char add::b +char add::b#0 +char add::b#1 +char add::return +char add::return#0 +char add::return#1 +char add::return#2 +char add::return#3 +char add::return#4 +void main() +char main::$0 +char main::$1 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::$0 +char plus::a +char plus::a#0 +char plus::a#1 +char plus::a#2 +char plus::b +char plus::b#0 +char plus::b#1 +char plus::b#2 +char plus::return +char plus::return#0 +char plus::return#1 +char plus::return#2 +char plus::return#3 +char plus::return#4 +char plus::return#5 +char plus::return#6 + +Adding number conversion cast (unumber) 7 in plus::b#0 = 7 +Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0 +Adding number conversion cast (unumber) 6 in plus::b#1 = 6 +Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast plus::b#0 = (unumber)7 +Inlining cast plus::b#1 = (unumber)6 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 7 +Simplifying constant integer cast 0 +Simplifying constant integer cast 6 +Simplifying constant integer cast 1 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 7 +Finalized unsigned number type (char) 0 +Finalized unsigned number type (char) 6 +Finalized unsigned number type (char) 1 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias plus::return#0 = plus::return#4 +Alias plus::return#1 = plus::return#5 +Alias add::return#0 = add::return#3 +Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3 +Alias add::return#1 = add::$0 add::return#4 add::return#2 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values add::a#1 add::a#0 +Identical Phi Values add::b#1 add::b#0 +Successful SSA optimization Pass2IdenticalPhiElimination +Constant plus::a#0 = '0' +Constant plus::b#0 = 7 +Constant plus::a#1 = '1' +Constant plus::b#1 = 6 +Successful SSA optimization Pass2ConstantIdentification +Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0 +Successful SSA optimization PassNSimplifyExpressionWithZero +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Inlining constant with var siblings plus::a#0 +Inlining constant with var siblings plus::b#0 +Inlining constant with var siblings plus::a#1 +Inlining constant with var siblings plus::b#1 +Constant inlined plus::b#1 = 6 +Constant inlined plus::b#0 = 7 +Constant inlined plus::a#1 = '1' +Constant inlined plus::a#0 = '0' +Successful SSA optimization Pass2ConstantInlining +Consolidated array index constant in *(SCREEN+1) +Successful SSA optimization Pass2ConstantAdditionElimination +Adding NOP phi() at start of main +CALL GRAPH +Calls in [main] to plus:1 plus:5 +Calls in [plus] to add:13 + +Created 2 initial phi equivalence classes +Coalesced down to 2 phi equivalence classes +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] add::a#0 = plus::a#2 + [12] add::b#0 = plus::b#2 + [13] call add + [14] add::return#0 = add::return#1 + to:plus::@1 +plus::@1: scope:[plus] from plus + [15] plus::return#2 = add::return#0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + [16] return + to:@return + +__bank(cx16_ram, 1) char add(char a , char b) +add: scope:[add] from plus + [17] add::return#1 = add::a#0 + add::b#0 + to:add::@return +add::@return: scope:[add] from add + [18] return + to:@return + + +VARIABLE REGISTER WEIGHTS +__bank(cx16_ram, 1) char add(char a , char b) +char add::a +char add::a#0 // 56.0 +char add::b +char add::b#0 // 112.0 +char add::return +char add::return#0 // 22.0 +char add::return#1 // 37.33333333333333 +void main() +char main::$0 // 4.0 +char main::$1 // 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // 11.0 +char plus::b +char plus::b#2 // 5.5 +char plus::return +char plus::return#0 // 4.0 +char plus::return#1 // 4.0 +char plus::return#2 // 3.75 + +Initial phi equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +Added variable plus::return#0 to live range equivalence class [ plus::return#0 ] +Added variable main::$0 to live range equivalence class [ main::$0 ] +Added variable plus::return#1 to live range equivalence class [ plus::return#1 ] +Added variable main::$1 to live range equivalence class [ main::$1 ] +Added variable add::a#0 to live range equivalence class [ add::a#0 ] +Added variable add::b#0 to live range equivalence class [ add::b#0 ] +Added variable add::return#0 to live range equivalence class [ add::return#0 ] +Added variable plus::return#2 to live range equivalence class [ plus::return#2 ] +Added variable add::return#1 to live range equivalence class [ add::return#1 ] +Complete equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +[ plus::return#0 ] +[ main::$0 ] +[ plus::return#1 ] +[ main::$1 ] +[ add::a#0 ] +[ add::b#0 ] +[ add::return#0 ] +[ plus::return#2 ] +[ add::return#1 ] +Allocated zp[1]:2 [ add::b#0 ] +Allocated zp[1]:3 [ add::a#0 ] +Allocated zp[1]:4 [ add::return#1 ] +Allocated zp[1]:5 [ add::return#0 ] +Allocated zp[1]:6 [ plus::a#2 ] +Allocated zp[1]:7 [ plus::b#2 ] +Allocated zp[1]:8 [ plus::return#0 ] +Allocated zp[1]:9 [ main::$0 ] +Allocated zp[1]:10 [ plus::return#1 ] +Allocated zp[1]:11 [ main::$1 ] +Allocated zp[1]:12 [ plus::return#2 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ] +Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a +Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a +Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y , +Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ] +Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ] +Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ] +Uplift Scope [] + +Uplifting [add] best 189 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte a [ add::return#1 ] reg byte a [ add::return#0 ] +Limited combination testing to 100 combinations of 256 possible. +Uplifting [plus] best 161 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ] +Limited combination testing to 100 combinations of 768 possible. +Uplifting [main] best 149 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ] +Uplifting [] best 149 combination +Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ] +Uplifting [add] best 149 combination zp[1]:3 [ add::a#0 ] +Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ] +Uplifting [plus] best 146 combination reg byte x [ plus::return#2 ] +Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +/** + * @file call-banked-phi-case-4-near-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #4. + * Implementation using the __bank() directive. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-4-near-0.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + plus_from_main: + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1 + ldx #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx + txa + jmp __b1 + // main::@1 + __b1: + // [3] main::$0 = plus::return#0 + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + plus_from___b1: + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1 + ldx #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx + txa + jmp __b2 + // main::@2 + __b2: + // [7] main::$1 = plus::return#1 + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // close call + sta SCREEN+1 + jmp __breturn + // main::@return + __breturn: + // [9] return + rts +} +.segment RAM_Bank1 + // plus +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa + sta.z add.a + // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx + txa + // [13] call add + jsr add + // [14] add::return#0 = add::return#1 + jmp __b1 + // plus::@1 + __b1: + // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa + tax + jmp __breturn + // plus::@return + __breturn: + // [16] return + rts +} + // add +// __register(A) char add(__zp(2) char a, __register(A) char b) +// __bank(cx16_ram, 1) +add: { + .label a = 2 + // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa + clc + adc.z a + jmp __breturn + // add::@return + __breturn: + // [18] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __b2 +Removing instruction jmp __breturn +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction plus_from_main: +Removing instruction __b1: +Removing instruction plus_from___b1: +Removing instruction __b2: +Removing instruction __breturn: +Removing instruction __b1: +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +__bank(cx16_ram, 1) char add(char a , char b) +char add::a +char add::a#0 // a zp[1]:2 56.0 +char add::b +char add::b#0 // reg byte a 112.0 +char add::return +char add::return#0 // reg byte a 22.0 +char add::return#1 // reg byte a 37.33333333333333 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte a 11.0 +char plus::b +char plus::b#2 // reg byte x 5.5 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte x 3.75 + +reg byte a [ plus::a#2 ] +reg byte x [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +zp[1]:2 [ add::a#0 ] +reg byte a [ add::b#0 ] +reg byte a [ add::return#0 ] +reg byte x [ plus::return#2 ] +reg byte a [ add::return#1 ] + + +FINAL ASSEMBLER +Score: 128 + + // File Comments +/** + * @file call-banked-phi-case-4-near-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #4. + * Implementation using the __bank() directive. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-4-near-0.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // plus('0', 7) + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1 + ldx #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('0', 7) + // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx + txa + // main::@1 + // [3] main::$0 = plus::return#0 + // SCREEN[0] = plus('0', 7) + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // plus('1', 6) + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1 + ldx #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('1', 6) + // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx + txa + // main::@2 + // [7] main::$1 = plus::return#1 + // SCREEN[1] = plus('1', 6) + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // close call + sta SCREEN+1 + // main::@return + // } + // [9] return + rts +} +.segment RAM_Bank1 + // plus +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // add(a, b) + // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa + sta.z add.a + // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx + txa + // [13] call add + jsr add + // [14] add::return#0 = add::return#1 + // plus::@1 + // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa + tax + // plus::@return + // } + // [16] return + rts +} + // add +// __register(A) char add(__zp(2) char a, __register(A) char b) +// __bank(cx16_ram, 1) +add: { + .label a = 2 + // a+b + // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa + clc + adc.z a + // add::@return + // } + // [18] return + rts +} + // File Data + diff --git a/src/test/ref/call-banked-phi-case-4-near-0.sym b/src/test/ref/call-banked-phi-case-4-near-0.sym new file mode 100644 index 000000000..4267f6a35 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-4-near-0.sym @@ -0,0 +1,33 @@ +__constant char * const SCREEN = (char *) 1024 +__bank(cx16_ram, 1) char add(char a , char b) +char add::a +char add::a#0 // a zp[1]:2 56.0 +char add::b +char add::b#0 // reg byte a 112.0 +char add::return +char add::return#0 // reg byte a 22.0 +char add::return#1 // reg byte a 37.33333333333333 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte a 11.0 +char plus::b +char plus::b#2 // reg byte x 5.5 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte x 3.75 + +reg byte a [ plus::a#2 ] +reg byte x [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +zp[1]:2 [ add::a#0 ] +reg byte a [ add::b#0 ] +reg byte a [ add::return#0 ] +reg byte x [ plus::return#2 ] +reg byte a [ add::return#1 ] diff --git a/src/test/ref/call-banked-phi-case-4-near-1.asm b/src/test/ref/call-banked-phi-case-4-near-1.asm new file mode 100644 index 000000000..97f10c7d0 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-4-near-1.asm @@ -0,0 +1,106 @@ +/** + * @file call-banked-phi-case-4-near-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #4. + * Implementation using the #pragma bank and nobank directives. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + .file [name="call-banked-phi-case-4-near-1.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + .label SCREEN = $400 +.segment Code +main: { + // plus('0', 7) + ldx #7 + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('0', 7) + txa + // SCREEN[0] = plus('0', 7) + sta SCREEN + // plus('1', 6) + ldx #6 + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('1', 6) + txa + // SCREEN[1] = plus('1', 6) + // close call + sta SCREEN+1 + // } + rts +} +.segment RAM_Bank1 +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // add(a, b) + sta.z add.a + txa + jsr add + tax + // } + rts +} +// __register(A) char add(__zp(2) char a, __register(A) char b) +// __bank(cx16_ram, 1) +add: { + .label a = 2 + // a+b + clc + adc.z a + // } + rts +} diff --git a/src/test/ref/call-banked-phi-case-4-near-1.cfg b/src/test/ref/call-banked-phi-case-4-near-1.cfg new file mode 100644 index 000000000..bb224fbfe --- /dev/null +++ b/src/test/ref/call-banked-phi-case-4-near-1.cfg @@ -0,0 +1,44 @@ + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] add::a#0 = plus::a#2 + [12] add::b#0 = plus::b#2 + [13] call add + [14] add::return#0 = add::return#1 + to:plus::@1 +plus::@1: scope:[plus] from plus + [15] plus::return#2 = add::return#0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + [16] return + to:@return + +__bank(cx16_ram, 1) char add(char a , char b) +add: scope:[add] from plus + [17] add::return#1 = add::a#0 + add::b#0 + to:add::@return +add::@return: scope:[add] from add + [18] return + to:@return diff --git a/src/test/ref/call-banked-phi-case-4-near-1.log b/src/test/ref/call-banked-phi-case-4-near-1.log new file mode 100644 index 000000000..4205fd77c --- /dev/null +++ b/src/test/ref/call-banked-phi-case-4-near-1.log @@ -0,0 +1,660 @@ +Loading link script "call-banked-phi.ld" + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + plus::a#0 = '0' + plus::b#0 = 7 + call plus + plus::return#0 = plus::return#3 + to:main::@1 +main::@1: scope:[main] from main + plus::return#4 = phi( main/plus::return#0 ) + main::$0 = plus::return#4 + SCREEN[0] = main::$0 + plus::a#1 = '1' + plus::b#1 = 6 + call plus + plus::return#1 = plus::return#3 + to:main::@2 +main::@2: scope:[main] from main::@1 + plus::return#5 = phi( main::@1/plus::return#1 ) + main::$1 = plus::return#5 + SCREEN[1] = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 ) + plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 ) + add::a#0 = plus::a#2 + add::b#0 = plus::b#2 + call add + add::return#0 = add::return#2 + to:plus::@1 +plus::@1: scope:[plus] from plus + add::return#3 = phi( plus/add::return#0 ) + plus::$0 = add::return#3 + plus::return#2 = plus::$0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + plus::return#6 = phi( plus::@1/plus::return#2 ) + plus::return#3 = plus::return#6 + return + to:@return + +__bank(cx16_ram, 1) char add(char a , char b) +add: scope:[add] from plus + add::b#1 = phi( plus/add::b#0 ) + add::a#1 = phi( plus/add::a#0 ) + add::$0 = add::a#1 + add::b#1 + add::return#1 = add::$0 + to:add::@return +add::@return: scope:[add] from add + add::return#4 = phi( add/add::return#1 ) + add::return#2 = add::return#4 + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant char * const SCREEN = (char *)$400 +void __start() +__bank(cx16_ram, 1) char add(char a , char b) +char add::$0 +char add::a +char add::a#0 +char add::a#1 +char add::b +char add::b#0 +char add::b#1 +char add::return +char add::return#0 +char add::return#1 +char add::return#2 +char add::return#3 +char add::return#4 +void main() +char main::$0 +char main::$1 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::$0 +char plus::a +char plus::a#0 +char plus::a#1 +char plus::a#2 +char plus::b +char plus::b#0 +char plus::b#1 +char plus::b#2 +char plus::return +char plus::return#0 +char plus::return#1 +char plus::return#2 +char plus::return#3 +char plus::return#4 +char plus::return#5 +char plus::return#6 + +Adding number conversion cast (unumber) 7 in plus::b#0 = 7 +Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0 +Adding number conversion cast (unumber) 6 in plus::b#1 = 6 +Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast plus::b#0 = (unumber)7 +Inlining cast plus::b#1 = (unumber)6 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 7 +Simplifying constant integer cast 0 +Simplifying constant integer cast 6 +Simplifying constant integer cast 1 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 7 +Finalized unsigned number type (char) 0 +Finalized unsigned number type (char) 6 +Finalized unsigned number type (char) 1 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias plus::return#0 = plus::return#4 +Alias plus::return#1 = plus::return#5 +Alias add::return#0 = add::return#3 +Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3 +Alias add::return#1 = add::$0 add::return#4 add::return#2 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values add::a#1 add::a#0 +Identical Phi Values add::b#1 add::b#0 +Successful SSA optimization Pass2IdenticalPhiElimination +Constant plus::a#0 = '0' +Constant plus::b#0 = 7 +Constant plus::a#1 = '1' +Constant plus::b#1 = 6 +Successful SSA optimization Pass2ConstantIdentification +Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0 +Successful SSA optimization PassNSimplifyExpressionWithZero +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Inlining constant with var siblings plus::a#0 +Inlining constant with var siblings plus::b#0 +Inlining constant with var siblings plus::a#1 +Inlining constant with var siblings plus::b#1 +Constant inlined plus::b#1 = 6 +Constant inlined plus::b#0 = 7 +Constant inlined plus::a#1 = '1' +Constant inlined plus::a#0 = '0' +Successful SSA optimization Pass2ConstantInlining +Consolidated array index constant in *(SCREEN+1) +Successful SSA optimization Pass2ConstantAdditionElimination +Adding NOP phi() at start of main +CALL GRAPH +Calls in [main] to plus:1 plus:5 +Calls in [plus] to add:13 + +Created 2 initial phi equivalence classes +Coalesced down to 2 phi equivalence classes +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] add::a#0 = plus::a#2 + [12] add::b#0 = plus::b#2 + [13] call add + [14] add::return#0 = add::return#1 + to:plus::@1 +plus::@1: scope:[plus] from plus + [15] plus::return#2 = add::return#0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + [16] return + to:@return + +__bank(cx16_ram, 1) char add(char a , char b) +add: scope:[add] from plus + [17] add::return#1 = add::a#0 + add::b#0 + to:add::@return +add::@return: scope:[add] from add + [18] return + to:@return + + +VARIABLE REGISTER WEIGHTS +__bank(cx16_ram, 1) char add(char a , char b) +char add::a +char add::a#0 // 56.0 +char add::b +char add::b#0 // 112.0 +char add::return +char add::return#0 // 22.0 +char add::return#1 // 37.33333333333333 +void main() +char main::$0 // 4.0 +char main::$1 // 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // 11.0 +char plus::b +char plus::b#2 // 5.5 +char plus::return +char plus::return#0 // 4.0 +char plus::return#1 // 4.0 +char plus::return#2 // 3.75 + +Initial phi equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +Added variable plus::return#0 to live range equivalence class [ plus::return#0 ] +Added variable main::$0 to live range equivalence class [ main::$0 ] +Added variable plus::return#1 to live range equivalence class [ plus::return#1 ] +Added variable main::$1 to live range equivalence class [ main::$1 ] +Added variable add::a#0 to live range equivalence class [ add::a#0 ] +Added variable add::b#0 to live range equivalence class [ add::b#0 ] +Added variable add::return#0 to live range equivalence class [ add::return#0 ] +Added variable plus::return#2 to live range equivalence class [ plus::return#2 ] +Added variable add::return#1 to live range equivalence class [ add::return#1 ] +Complete equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +[ plus::return#0 ] +[ main::$0 ] +[ plus::return#1 ] +[ main::$1 ] +[ add::a#0 ] +[ add::b#0 ] +[ add::return#0 ] +[ plus::return#2 ] +[ add::return#1 ] +Allocated zp[1]:2 [ add::b#0 ] +Allocated zp[1]:3 [ add::a#0 ] +Allocated zp[1]:4 [ add::return#1 ] +Allocated zp[1]:5 [ add::return#0 ] +Allocated zp[1]:6 [ plus::a#2 ] +Allocated zp[1]:7 [ plus::b#2 ] +Allocated zp[1]:8 [ plus::return#0 ] +Allocated zp[1]:9 [ main::$0 ] +Allocated zp[1]:10 [ plus::return#1 ] +Allocated zp[1]:11 [ main::$1 ] +Allocated zp[1]:12 [ plus::return#2 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ] +Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a +Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a +Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y , +Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ] +Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ] +Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ] +Uplift Scope [] + +Uplifting [add] best 189 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte a [ add::return#1 ] reg byte a [ add::return#0 ] +Limited combination testing to 100 combinations of 256 possible. +Uplifting [plus] best 161 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ] +Limited combination testing to 100 combinations of 768 possible. +Uplifting [main] best 149 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ] +Uplifting [] best 149 combination +Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ] +Uplifting [add] best 149 combination zp[1]:3 [ add::a#0 ] +Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ] +Uplifting [plus] best 146 combination reg byte x [ plus::return#2 ] +Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +/** + * @file call-banked-phi-case-4-near-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #4. + * Implementation using the #pragma bank and nobank directives. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-4-near-1.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + plus_from_main: + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1 + ldx #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx + txa + jmp __b1 + // main::@1 + __b1: + // [3] main::$0 = plus::return#0 + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + plus_from___b1: + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1 + ldx #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx + txa + jmp __b2 + // main::@2 + __b2: + // [7] main::$1 = plus::return#1 + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // close call + sta SCREEN+1 + jmp __breturn + // main::@return + __breturn: + // [9] return + rts +} +.segment RAM_Bank1 + // plus +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa + sta.z add.a + // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx + txa + // [13] call add + jsr add + // [14] add::return#0 = add::return#1 + jmp __b1 + // plus::@1 + __b1: + // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa + tax + jmp __breturn + // plus::@return + __breturn: + // [16] return + rts +} + // add +// __register(A) char add(__zp(2) char a, __register(A) char b) +// __bank(cx16_ram, 1) +add: { + .label a = 2 + // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa + clc + adc.z a + jmp __breturn + // add::@return + __breturn: + // [18] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __b2 +Removing instruction jmp __breturn +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction plus_from_main: +Removing instruction __b1: +Removing instruction plus_from___b1: +Removing instruction __b2: +Removing instruction __breturn: +Removing instruction __b1: +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +__bank(cx16_ram, 1) char add(char a , char b) +char add::a +char add::a#0 // a zp[1]:2 56.0 +char add::b +char add::b#0 // reg byte a 112.0 +char add::return +char add::return#0 // reg byte a 22.0 +char add::return#1 // reg byte a 37.33333333333333 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte a 11.0 +char plus::b +char plus::b#2 // reg byte x 5.5 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte x 3.75 + +reg byte a [ plus::a#2 ] +reg byte x [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +zp[1]:2 [ add::a#0 ] +reg byte a [ add::b#0 ] +reg byte a [ add::return#0 ] +reg byte x [ plus::return#2 ] +reg byte a [ add::return#1 ] + + +FINAL ASSEMBLER +Score: 128 + + // File Comments +/** + * @file call-banked-phi-case-4-near-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #4. + * Implementation using the #pragma bank and nobank directives. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-4-near-1.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // plus('0', 7) + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1 + ldx #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('0', 7) + // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx + txa + // main::@1 + // [3] main::$0 = plus::return#0 + // SCREEN[0] = plus('0', 7) + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // plus('1', 6) + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1 + ldx #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('1', 6) + // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx + txa + // main::@2 + // [7] main::$1 = plus::return#1 + // SCREEN[1] = plus('1', 6) + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // close call + sta SCREEN+1 + // main::@return + // } + // [9] return + rts +} +.segment RAM_Bank1 + // plus +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // add(a, b) + // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa + sta.z add.a + // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx + txa + // [13] call add + jsr add + // [14] add::return#0 = add::return#1 + // plus::@1 + // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa + tax + // plus::@return + // } + // [16] return + rts +} + // add +// __register(A) char add(__zp(2) char a, __register(A) char b) +// __bank(cx16_ram, 1) +add: { + .label a = 2 + // a+b + // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa + clc + adc.z a + // add::@return + // } + // [18] return + rts +} + // File Data + diff --git a/src/test/ref/call-banked-phi-case-4-near-1.sym b/src/test/ref/call-banked-phi-case-4-near-1.sym new file mode 100644 index 000000000..4267f6a35 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-4-near-1.sym @@ -0,0 +1,33 @@ +__constant char * const SCREEN = (char *) 1024 +__bank(cx16_ram, 1) char add(char a , char b) +char add::a +char add::a#0 // a zp[1]:2 56.0 +char add::b +char add::b#0 // reg byte a 112.0 +char add::return +char add::return#0 // reg byte a 22.0 +char add::return#1 // reg byte a 37.33333333333333 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte a 11.0 +char plus::b +char plus::b#2 // reg byte x 5.5 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte x 3.75 + +reg byte a [ plus::a#2 ] +reg byte x [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +zp[1]:2 [ add::a#0 ] +reg byte a [ add::b#0 ] +reg byte a [ add::return#0 ] +reg byte x [ plus::return#2 ] +reg byte a [ add::return#1 ] diff --git a/src/test/ref/call-banked-phi-case-5-far-0.asm b/src/test/ref/call-banked-phi-case-5-far-0.asm new file mode 100644 index 000000000..c8603afdc --- /dev/null +++ b/src/test/ref/call-banked-phi-case-5-far-0.asm @@ -0,0 +1,110 @@ +/** + * @file call-banked-phi-case-5-far-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #5. + * Implementation using the __bank() directive. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + .file [name="call-banked-phi-case-5-far-0.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + .label SCREEN = $400 +.segment Code +main: { + // plus('0', 7) + ldx #7 + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('0', 7) + txa + // SCREEN[0] = plus('0', 7) + sta SCREEN + // plus('1', 6) + ldx #6 + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('1', 6) + txa + // SCREEN[1] = plus('1', 6) + // close call + sta SCREEN+1 + // } + rts +} +.segment RAM_Bank1 +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // add(a, b) + sta.z add.a + txa + jsr $ff6e + .byte add + .byte 2 + tax + // } + rts +} +.segment RAM_Bank2 +// __register(A) char add(__zp(2) char a, __register(A) char b) +// __bank(cx16_ram, 2) +add: { + .label a = 2 + // a+b + clc + adc.z a + // } + rts +} diff --git a/src/test/ref/call-banked-phi-case-5-far-0.cfg b/src/test/ref/call-banked-phi-case-5-far-0.cfg new file mode 100644 index 000000000..03dfaf36c --- /dev/null +++ b/src/test/ref/call-banked-phi-case-5-far-0.cfg @@ -0,0 +1,44 @@ + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] add::a#0 = plus::a#2 + [12] add::b#0 = plus::b#2 + [13] call add + [14] add::return#0 = add::return#1 + to:plus::@1 +plus::@1: scope:[plus] from plus + [15] plus::return#2 = add::return#0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + [16] return + to:@return + +__bank(cx16_ram, 2) char add(char a , char b) +add: scope:[add] from plus + [17] add::return#1 = add::a#0 + add::b#0 + to:add::@return +add::@return: scope:[add] from add + [18] return + to:@return diff --git a/src/test/ref/call-banked-phi-case-5-far-0.log b/src/test/ref/call-banked-phi-case-5-far-0.log new file mode 100644 index 000000000..d1d83e6e3 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-5-far-0.log @@ -0,0 +1,668 @@ +Loading link script "call-banked-phi.ld" + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + plus::a#0 = '0' + plus::b#0 = 7 + call plus + plus::return#0 = plus::return#3 + to:main::@1 +main::@1: scope:[main] from main + plus::return#4 = phi( main/plus::return#0 ) + main::$0 = plus::return#4 + SCREEN[0] = main::$0 + plus::a#1 = '1' + plus::b#1 = 6 + call plus + plus::return#1 = plus::return#3 + to:main::@2 +main::@2: scope:[main] from main::@1 + plus::return#5 = phi( main::@1/plus::return#1 ) + main::$1 = plus::return#5 + SCREEN[1] = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 ) + plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 ) + add::a#0 = plus::a#2 + add::b#0 = plus::b#2 + call add + add::return#0 = add::return#2 + to:plus::@1 +plus::@1: scope:[plus] from plus + add::return#3 = phi( plus/add::return#0 ) + plus::$0 = add::return#3 + plus::return#2 = plus::$0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + plus::return#6 = phi( plus::@1/plus::return#2 ) + plus::return#3 = plus::return#6 + return + to:@return + +__bank(cx16_ram, 2) char add(char a , char b) +add: scope:[add] from plus + add::b#1 = phi( plus/add::b#0 ) + add::a#1 = phi( plus/add::a#0 ) + add::$0 = add::a#1 + add::b#1 + add::return#1 = add::$0 + to:add::@return +add::@return: scope:[add] from add + add::return#4 = phi( add/add::return#1 ) + add::return#2 = add::return#4 + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant char * const SCREEN = (char *)$400 +void __start() +__bank(cx16_ram, 2) char add(char a , char b) +char add::$0 +char add::a +char add::a#0 +char add::a#1 +char add::b +char add::b#0 +char add::b#1 +char add::return +char add::return#0 +char add::return#1 +char add::return#2 +char add::return#3 +char add::return#4 +void main() +char main::$0 +char main::$1 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::$0 +char plus::a +char plus::a#0 +char plus::a#1 +char plus::a#2 +char plus::b +char plus::b#0 +char plus::b#1 +char plus::b#2 +char plus::return +char plus::return#0 +char plus::return#1 +char plus::return#2 +char plus::return#3 +char plus::return#4 +char plus::return#5 +char plus::return#6 + +Adding number conversion cast (unumber) 7 in plus::b#0 = 7 +Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0 +Adding number conversion cast (unumber) 6 in plus::b#1 = 6 +Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast plus::b#0 = (unumber)7 +Inlining cast plus::b#1 = (unumber)6 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 7 +Simplifying constant integer cast 0 +Simplifying constant integer cast 6 +Simplifying constant integer cast 1 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 7 +Finalized unsigned number type (char) 0 +Finalized unsigned number type (char) 6 +Finalized unsigned number type (char) 1 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias plus::return#0 = plus::return#4 +Alias plus::return#1 = plus::return#5 +Alias add::return#0 = add::return#3 +Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3 +Alias add::return#1 = add::$0 add::return#4 add::return#2 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values add::a#1 add::a#0 +Identical Phi Values add::b#1 add::b#0 +Successful SSA optimization Pass2IdenticalPhiElimination +Constant plus::a#0 = '0' +Constant plus::b#0 = 7 +Constant plus::a#1 = '1' +Constant plus::b#1 = 6 +Successful SSA optimization Pass2ConstantIdentification +Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0 +Successful SSA optimization PassNSimplifyExpressionWithZero +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Inlining constant with var siblings plus::a#0 +Inlining constant with var siblings plus::b#0 +Inlining constant with var siblings plus::a#1 +Inlining constant with var siblings plus::b#1 +Constant inlined plus::b#1 = 6 +Constant inlined plus::b#0 = 7 +Constant inlined plus::a#1 = '1' +Constant inlined plus::a#0 = '0' +Successful SSA optimization Pass2ConstantInlining +Consolidated array index constant in *(SCREEN+1) +Successful SSA optimization Pass2ConstantAdditionElimination +Adding NOP phi() at start of main +CALL GRAPH +Calls in [main] to plus:1 plus:5 +Calls in [plus] to add:13 + +Created 2 initial phi equivalence classes +Coalesced down to 2 phi equivalence classes +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] add::a#0 = plus::a#2 + [12] add::b#0 = plus::b#2 + [13] call add + [14] add::return#0 = add::return#1 + to:plus::@1 +plus::@1: scope:[plus] from plus + [15] plus::return#2 = add::return#0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + [16] return + to:@return + +__bank(cx16_ram, 2) char add(char a , char b) +add: scope:[add] from plus + [17] add::return#1 = add::a#0 + add::b#0 + to:add::@return +add::@return: scope:[add] from add + [18] return + to:@return + + +VARIABLE REGISTER WEIGHTS +__bank(cx16_ram, 2) char add(char a , char b) +char add::a +char add::a#0 // 56.0 +char add::b +char add::b#0 // 112.0 +char add::return +char add::return#0 // 22.0 +char add::return#1 // 37.33333333333333 +void main() +char main::$0 // 4.0 +char main::$1 // 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // 11.0 +char plus::b +char plus::b#2 // 5.5 +char plus::return +char plus::return#0 // 4.0 +char plus::return#1 // 4.0 +char plus::return#2 // 3.75 + +Initial phi equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +Added variable plus::return#0 to live range equivalence class [ plus::return#0 ] +Added variable main::$0 to live range equivalence class [ main::$0 ] +Added variable plus::return#1 to live range equivalence class [ plus::return#1 ] +Added variable main::$1 to live range equivalence class [ main::$1 ] +Added variable add::a#0 to live range equivalence class [ add::a#0 ] +Added variable add::b#0 to live range equivalence class [ add::b#0 ] +Added variable add::return#0 to live range equivalence class [ add::return#0 ] +Added variable plus::return#2 to live range equivalence class [ plus::return#2 ] +Added variable add::return#1 to live range equivalence class [ add::return#1 ] +Complete equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +[ plus::return#0 ] +[ main::$0 ] +[ plus::return#1 ] +[ main::$1 ] +[ add::a#0 ] +[ add::b#0 ] +[ add::return#0 ] +[ plus::return#2 ] +[ add::return#1 ] +Allocated zp[1]:2 [ add::b#0 ] +Allocated zp[1]:3 [ add::a#0 ] +Allocated zp[1]:4 [ add::return#1 ] +Allocated zp[1]:5 [ add::return#0 ] +Allocated zp[1]:6 [ plus::a#2 ] +Allocated zp[1]:7 [ plus::b#2 ] +Allocated zp[1]:8 [ plus::return#0 ] +Allocated zp[1]:9 [ main::$0 ] +Allocated zp[1]:10 [ plus::return#1 ] +Allocated zp[1]:11 [ main::$1 ] +Allocated zp[1]:12 [ plus::return#2 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ] +Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a +Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a +Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y , +Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ] +Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ] +Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ] +Uplift Scope [] + +Uplifting [add] best 189 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte a [ add::return#1 ] reg byte a [ add::return#0 ] +Limited combination testing to 100 combinations of 256 possible. +Uplifting [plus] best 161 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ] +Limited combination testing to 100 combinations of 768 possible. +Uplifting [main] best 149 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ] +Uplifting [] best 149 combination +Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ] +Uplifting [add] best 149 combination zp[1]:3 [ add::a#0 ] +Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ] +Uplifting [plus] best 146 combination reg byte x [ plus::return#2 ] +Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +/** + * @file call-banked-phi-case-5-far-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #5. + * Implementation using the __bank() directive. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-5-far-0.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + plus_from_main: + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1 + ldx #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx + txa + jmp __b1 + // main::@1 + __b1: + // [3] main::$0 = plus::return#0 + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + plus_from___b1: + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1 + ldx #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx + txa + jmp __b2 + // main::@2 + __b2: + // [7] main::$1 = plus::return#1 + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // close call + sta SCREEN+1 + jmp __breturn + // main::@return + __breturn: + // [9] return + rts +} +.segment RAM_Bank1 + // plus +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa + sta.z add.a + // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx + txa + // [13] call add -- call_phi_far_cx16_ram + jsr $ff6e + .byte add + .byte 2 + // [14] add::return#0 = add::return#1 + jmp __b1 + // plus::@1 + __b1: + // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa + tax + jmp __breturn + // plus::@return + __breturn: + // [16] return + rts +} +.segment RAM_Bank2 + // add +// __register(A) char add(__zp(2) char a, __register(A) char b) +// __bank(cx16_ram, 2) +add: { + .label a = 2 + // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa + clc + adc.z a + jmp __breturn + // add::@return + __breturn: + // [18] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __b2 +Removing instruction jmp __breturn +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction plus_from_main: +Removing instruction __b1: +Removing instruction plus_from___b1: +Removing instruction __b2: +Removing instruction __breturn: +Removing instruction __b1: +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +__bank(cx16_ram, 2) char add(char a , char b) +char add::a +char add::a#0 // a zp[1]:2 56.0 +char add::b +char add::b#0 // reg byte a 112.0 +char add::return +char add::return#0 // reg byte a 22.0 +char add::return#1 // reg byte a 37.33333333333333 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte a 11.0 +char plus::b +char plus::b#2 // reg byte x 5.5 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte x 3.75 + +reg byte a [ plus::a#2 ] +reg byte x [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +zp[1]:2 [ add::a#0 ] +reg byte a [ add::b#0 ] +reg byte a [ add::return#0 ] +reg byte x [ plus::return#2 ] +reg byte a [ add::return#1 ] + + +FINAL ASSEMBLER +Score: 128 + + // File Comments +/** + * @file call-banked-phi-case-5-far-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #5. + * Implementation using the __bank() directive. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-5-far-0.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // plus('0', 7) + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1 + ldx #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('0', 7) + // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx + txa + // main::@1 + // [3] main::$0 = plus::return#0 + // SCREEN[0] = plus('0', 7) + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // plus('1', 6) + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1 + ldx #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('1', 6) + // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx + txa + // main::@2 + // [7] main::$1 = plus::return#1 + // SCREEN[1] = plus('1', 6) + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // close call + sta SCREEN+1 + // main::@return + // } + // [9] return + rts +} +.segment RAM_Bank1 + // plus +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // add(a, b) + // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa + sta.z add.a + // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx + txa + // [13] call add -- call_phi_far_cx16_ram + jsr $ff6e + .byte add + .byte 2 + // [14] add::return#0 = add::return#1 + // plus::@1 + // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa + tax + // plus::@return + // } + // [16] return + rts +} +.segment RAM_Bank2 + // add +// __register(A) char add(__zp(2) char a, __register(A) char b) +// __bank(cx16_ram, 2) +add: { + .label a = 2 + // a+b + // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa + clc + adc.z a + // add::@return + // } + // [18] return + rts +} + // File Data + diff --git a/src/test/ref/call-banked-phi-case-5-far-0.sym b/src/test/ref/call-banked-phi-case-5-far-0.sym new file mode 100644 index 000000000..f777cf8ef --- /dev/null +++ b/src/test/ref/call-banked-phi-case-5-far-0.sym @@ -0,0 +1,33 @@ +__constant char * const SCREEN = (char *) 1024 +__bank(cx16_ram, 2) char add(char a , char b) +char add::a +char add::a#0 // a zp[1]:2 56.0 +char add::b +char add::b#0 // reg byte a 112.0 +char add::return +char add::return#0 // reg byte a 22.0 +char add::return#1 // reg byte a 37.33333333333333 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte a 11.0 +char plus::b +char plus::b#2 // reg byte x 5.5 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte x 3.75 + +reg byte a [ plus::a#2 ] +reg byte x [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +zp[1]:2 [ add::a#0 ] +reg byte a [ add::b#0 ] +reg byte a [ add::return#0 ] +reg byte x [ plus::return#2 ] +reg byte a [ add::return#1 ] diff --git a/src/test/ref/call-banked-phi-case-5-far-1.asm b/src/test/ref/call-banked-phi-case-5-far-1.asm new file mode 100644 index 000000000..93f4f13c5 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-5-far-1.asm @@ -0,0 +1,110 @@ +/** + * @file call-banked-phi-case-5-far-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #5. + * Implementation using the #pragma bank and nobank directives. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + .file [name="call-banked-phi-case-5-far-1.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + .label SCREEN = $400 +.segment Code +main: { + // plus('0', 7) + ldx #7 + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('0', 7) + txa + // SCREEN[0] = plus('0', 7) + sta SCREEN + // plus('1', 6) + ldx #6 + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('1', 6) + txa + // SCREEN[1] = plus('1', 6) + // close call + sta SCREEN+1 + // } + rts +} +.segment RAM_Bank1 +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // add(a, b) + sta.z add.a + txa + jsr $ff6e + .byte add + .byte 2 + tax + // } + rts +} +.segment RAM_Bank2 +// __register(A) char add(__zp(2) char a, __register(A) char b) +// __bank(cx16_ram, 2) +add: { + .label a = 2 + // a+b + clc + adc.z a + // } + rts +} diff --git a/src/test/ref/call-banked-phi-case-5-far-1.cfg b/src/test/ref/call-banked-phi-case-5-far-1.cfg new file mode 100644 index 000000000..03dfaf36c --- /dev/null +++ b/src/test/ref/call-banked-phi-case-5-far-1.cfg @@ -0,0 +1,44 @@ + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] add::a#0 = plus::a#2 + [12] add::b#0 = plus::b#2 + [13] call add + [14] add::return#0 = add::return#1 + to:plus::@1 +plus::@1: scope:[plus] from plus + [15] plus::return#2 = add::return#0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + [16] return + to:@return + +__bank(cx16_ram, 2) char add(char a , char b) +add: scope:[add] from plus + [17] add::return#1 = add::a#0 + add::b#0 + to:add::@return +add::@return: scope:[add] from add + [18] return + to:@return diff --git a/src/test/ref/call-banked-phi-case-5-far-1.log b/src/test/ref/call-banked-phi-case-5-far-1.log new file mode 100644 index 000000000..8844e959f --- /dev/null +++ b/src/test/ref/call-banked-phi-case-5-far-1.log @@ -0,0 +1,668 @@ +Loading link script "call-banked-phi.ld" + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + plus::a#0 = '0' + plus::b#0 = 7 + call plus + plus::return#0 = plus::return#3 + to:main::@1 +main::@1: scope:[main] from main + plus::return#4 = phi( main/plus::return#0 ) + main::$0 = plus::return#4 + SCREEN[0] = main::$0 + plus::a#1 = '1' + plus::b#1 = 6 + call plus + plus::return#1 = plus::return#3 + to:main::@2 +main::@2: scope:[main] from main::@1 + plus::return#5 = phi( main::@1/plus::return#1 ) + main::$1 = plus::return#5 + SCREEN[1] = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 ) + plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 ) + add::a#0 = plus::a#2 + add::b#0 = plus::b#2 + call add + add::return#0 = add::return#2 + to:plus::@1 +plus::@1: scope:[plus] from plus + add::return#3 = phi( plus/add::return#0 ) + plus::$0 = add::return#3 + plus::return#2 = plus::$0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + plus::return#6 = phi( plus::@1/plus::return#2 ) + plus::return#3 = plus::return#6 + return + to:@return + +__bank(cx16_ram, 2) char add(char a , char b) +add: scope:[add] from plus + add::b#1 = phi( plus/add::b#0 ) + add::a#1 = phi( plus/add::a#0 ) + add::$0 = add::a#1 + add::b#1 + add::return#1 = add::$0 + to:add::@return +add::@return: scope:[add] from add + add::return#4 = phi( add/add::return#1 ) + add::return#2 = add::return#4 + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant char * const SCREEN = (char *)$400 +void __start() +__bank(cx16_ram, 2) char add(char a , char b) +char add::$0 +char add::a +char add::a#0 +char add::a#1 +char add::b +char add::b#0 +char add::b#1 +char add::return +char add::return#0 +char add::return#1 +char add::return#2 +char add::return#3 +char add::return#4 +void main() +char main::$0 +char main::$1 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::$0 +char plus::a +char plus::a#0 +char plus::a#1 +char plus::a#2 +char plus::b +char plus::b#0 +char plus::b#1 +char plus::b#2 +char plus::return +char plus::return#0 +char plus::return#1 +char plus::return#2 +char plus::return#3 +char plus::return#4 +char plus::return#5 +char plus::return#6 + +Adding number conversion cast (unumber) 7 in plus::b#0 = 7 +Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0 +Adding number conversion cast (unumber) 6 in plus::b#1 = 6 +Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast plus::b#0 = (unumber)7 +Inlining cast plus::b#1 = (unumber)6 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 7 +Simplifying constant integer cast 0 +Simplifying constant integer cast 6 +Simplifying constant integer cast 1 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 7 +Finalized unsigned number type (char) 0 +Finalized unsigned number type (char) 6 +Finalized unsigned number type (char) 1 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias plus::return#0 = plus::return#4 +Alias plus::return#1 = plus::return#5 +Alias add::return#0 = add::return#3 +Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3 +Alias add::return#1 = add::$0 add::return#4 add::return#2 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values add::a#1 add::a#0 +Identical Phi Values add::b#1 add::b#0 +Successful SSA optimization Pass2IdenticalPhiElimination +Constant plus::a#0 = '0' +Constant plus::b#0 = 7 +Constant plus::a#1 = '1' +Constant plus::b#1 = 6 +Successful SSA optimization Pass2ConstantIdentification +Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0 +Successful SSA optimization PassNSimplifyExpressionWithZero +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Inlining constant with var siblings plus::a#0 +Inlining constant with var siblings plus::b#0 +Inlining constant with var siblings plus::a#1 +Inlining constant with var siblings plus::b#1 +Constant inlined plus::b#1 = 6 +Constant inlined plus::b#0 = 7 +Constant inlined plus::a#1 = '1' +Constant inlined plus::a#0 = '0' +Successful SSA optimization Pass2ConstantInlining +Consolidated array index constant in *(SCREEN+1) +Successful SSA optimization Pass2ConstantAdditionElimination +Adding NOP phi() at start of main +CALL GRAPH +Calls in [main] to plus:1 plus:5 +Calls in [plus] to add:13 + +Created 2 initial phi equivalence classes +Coalesced down to 2 phi equivalence classes +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] add::a#0 = plus::a#2 + [12] add::b#0 = plus::b#2 + [13] call add + [14] add::return#0 = add::return#1 + to:plus::@1 +plus::@1: scope:[plus] from plus + [15] plus::return#2 = add::return#0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + [16] return + to:@return + +__bank(cx16_ram, 2) char add(char a , char b) +add: scope:[add] from plus + [17] add::return#1 = add::a#0 + add::b#0 + to:add::@return +add::@return: scope:[add] from add + [18] return + to:@return + + +VARIABLE REGISTER WEIGHTS +__bank(cx16_ram, 2) char add(char a , char b) +char add::a +char add::a#0 // 56.0 +char add::b +char add::b#0 // 112.0 +char add::return +char add::return#0 // 22.0 +char add::return#1 // 37.33333333333333 +void main() +char main::$0 // 4.0 +char main::$1 // 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // 11.0 +char plus::b +char plus::b#2 // 5.5 +char plus::return +char plus::return#0 // 4.0 +char plus::return#1 // 4.0 +char plus::return#2 // 3.75 + +Initial phi equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +Added variable plus::return#0 to live range equivalence class [ plus::return#0 ] +Added variable main::$0 to live range equivalence class [ main::$0 ] +Added variable plus::return#1 to live range equivalence class [ plus::return#1 ] +Added variable main::$1 to live range equivalence class [ main::$1 ] +Added variable add::a#0 to live range equivalence class [ add::a#0 ] +Added variable add::b#0 to live range equivalence class [ add::b#0 ] +Added variable add::return#0 to live range equivalence class [ add::return#0 ] +Added variable plus::return#2 to live range equivalence class [ plus::return#2 ] +Added variable add::return#1 to live range equivalence class [ add::return#1 ] +Complete equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +[ plus::return#0 ] +[ main::$0 ] +[ plus::return#1 ] +[ main::$1 ] +[ add::a#0 ] +[ add::b#0 ] +[ add::return#0 ] +[ plus::return#2 ] +[ add::return#1 ] +Allocated zp[1]:2 [ add::b#0 ] +Allocated zp[1]:3 [ add::a#0 ] +Allocated zp[1]:4 [ add::return#1 ] +Allocated zp[1]:5 [ add::return#0 ] +Allocated zp[1]:6 [ plus::a#2 ] +Allocated zp[1]:7 [ plus::b#2 ] +Allocated zp[1]:8 [ plus::return#0 ] +Allocated zp[1]:9 [ main::$0 ] +Allocated zp[1]:10 [ plus::return#1 ] +Allocated zp[1]:11 [ main::$1 ] +Allocated zp[1]:12 [ plus::return#2 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ] +Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a +Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a +Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y , +Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ] +Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ] +Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ] +Uplift Scope [] + +Uplifting [add] best 189 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte a [ add::return#1 ] reg byte a [ add::return#0 ] +Limited combination testing to 100 combinations of 256 possible. +Uplifting [plus] best 161 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ] +Limited combination testing to 100 combinations of 768 possible. +Uplifting [main] best 149 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ] +Uplifting [] best 149 combination +Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ] +Uplifting [add] best 149 combination zp[1]:3 [ add::a#0 ] +Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ] +Uplifting [plus] best 146 combination reg byte x [ plus::return#2 ] +Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +/** + * @file call-banked-phi-case-5-far-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #5. + * Implementation using the #pragma bank and nobank directives. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-5-far-1.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + plus_from_main: + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1 + ldx #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx + txa + jmp __b1 + // main::@1 + __b1: + // [3] main::$0 = plus::return#0 + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + plus_from___b1: + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1 + ldx #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx + txa + jmp __b2 + // main::@2 + __b2: + // [7] main::$1 = plus::return#1 + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // close call + sta SCREEN+1 + jmp __breturn + // main::@return + __breturn: + // [9] return + rts +} +.segment RAM_Bank1 + // plus +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa + sta.z add.a + // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx + txa + // [13] call add -- call_phi_far_cx16_ram + jsr $ff6e + .byte add + .byte 2 + // [14] add::return#0 = add::return#1 + jmp __b1 + // plus::@1 + __b1: + // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa + tax + jmp __breturn + // plus::@return + __breturn: + // [16] return + rts +} +.segment RAM_Bank2 + // add +// __register(A) char add(__zp(2) char a, __register(A) char b) +// __bank(cx16_ram, 2) +add: { + .label a = 2 + // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa + clc + adc.z a + jmp __breturn + // add::@return + __breturn: + // [18] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __b2 +Removing instruction jmp __breturn +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction plus_from_main: +Removing instruction __b1: +Removing instruction plus_from___b1: +Removing instruction __b2: +Removing instruction __breturn: +Removing instruction __b1: +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +__bank(cx16_ram, 2) char add(char a , char b) +char add::a +char add::a#0 // a zp[1]:2 56.0 +char add::b +char add::b#0 // reg byte a 112.0 +char add::return +char add::return#0 // reg byte a 22.0 +char add::return#1 // reg byte a 37.33333333333333 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte a 11.0 +char plus::b +char plus::b#2 // reg byte x 5.5 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte x 3.75 + +reg byte a [ plus::a#2 ] +reg byte x [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +zp[1]:2 [ add::a#0 ] +reg byte a [ add::b#0 ] +reg byte a [ add::return#0 ] +reg byte x [ plus::return#2 ] +reg byte a [ add::return#1 ] + + +FINAL ASSEMBLER +Score: 128 + + // File Comments +/** + * @file call-banked-phi-case-5-far-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #5. + * Implementation using the #pragma bank and nobank directives. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-5-far-1.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // plus('0', 7) + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1 + ldx #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('0', 7) + // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx + txa + // main::@1 + // [3] main::$0 = plus::return#0 + // SCREEN[0] = plus('0', 7) + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // plus('1', 6) + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1 + ldx #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('1', 6) + // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx + txa + // main::@2 + // [7] main::$1 = plus::return#1 + // SCREEN[1] = plus('1', 6) + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // close call + sta SCREEN+1 + // main::@return + // } + // [9] return + rts +} +.segment RAM_Bank1 + // plus +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // add(a, b) + // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa + sta.z add.a + // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx + txa + // [13] call add -- call_phi_far_cx16_ram + jsr $ff6e + .byte add + .byte 2 + // [14] add::return#0 = add::return#1 + // plus::@1 + // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa + tax + // plus::@return + // } + // [16] return + rts +} +.segment RAM_Bank2 + // add +// __register(A) char add(__zp(2) char a, __register(A) char b) +// __bank(cx16_ram, 2) +add: { + .label a = 2 + // a+b + // [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa + clc + adc.z a + // add::@return + // } + // [18] return + rts +} + // File Data + diff --git a/src/test/ref/call-banked-phi-case-5-far-1.sym b/src/test/ref/call-banked-phi-case-5-far-1.sym new file mode 100644 index 000000000..f777cf8ef --- /dev/null +++ b/src/test/ref/call-banked-phi-case-5-far-1.sym @@ -0,0 +1,33 @@ +__constant char * const SCREEN = (char *) 1024 +__bank(cx16_ram, 2) char add(char a , char b) +char add::a +char add::a#0 // a zp[1]:2 56.0 +char add::b +char add::b#0 // reg byte a 112.0 +char add::return +char add::return#0 // reg byte a 22.0 +char add::return#1 // reg byte a 37.33333333333333 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte a 11.0 +char plus::b +char plus::b#2 // reg byte x 5.5 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte x 3.75 + +reg byte a [ plus::a#2 ] +reg byte x [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +zp[1]:2 [ add::a#0 ] +reg byte a [ add::b#0 ] +reg byte a [ add::return#0 ] +reg byte x [ plus::return#2 ] +reg byte a [ add::return#1 ] diff --git a/src/test/ref/call-banked-phi-case-6-close-0.asm b/src/test/ref/call-banked-phi-case-6-close-0.asm new file mode 100644 index 000000000..160ff45fa --- /dev/null +++ b/src/test/ref/call-banked-phi-case-6-close-0.asm @@ -0,0 +1,119 @@ +/** + * @file call-banked-phi-case-6-close-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #6. + * Implementation using the __bank() directive. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + .file [name="call-banked-phi-case-6-close-0.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + .label SCREEN = $400 +.segment Code +main: { + // plus('0', 7) + ldx #7 + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('0', 7) + txa + // SCREEN[0] = plus('0', 7) + sta SCREEN + // plus('1', 6) + ldx #6 + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('1', 6) + txa + // SCREEN[1] = plus('1', 6) + // close call + sta SCREEN+1 + // } + rts +} +.segment RAM_Bank1 +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // add(a, b) + sta.z add.a + txa + sta.z $ff + lda.z 1 + pha + lda #1 + sta.z 1 + lda.z $ff + jsr add + sta.z $ff + pla + sta.z 1 + lda.z $ff + txa + tax + // } + rts +} +.segment ROM_Bank1 +// __register(X) char add(__zp(2) char a, __register(A) char b) +// __bank(cx16_rom, 1) +add: { + .label a = 2 + // a+b + clc + adc.z a + tax + // } + rts +} diff --git a/src/test/ref/call-banked-phi-case-6-close-0.cfg b/src/test/ref/call-banked-phi-case-6-close-0.cfg new file mode 100644 index 000000000..c10c5c2f7 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-6-close-0.cfg @@ -0,0 +1,44 @@ + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] add::a#0 = plus::a#2 + [12] add::b#0 = plus::b#2 + [13] call add + [14] add::return#0 = add::return#1 + to:plus::@1 +plus::@1: scope:[plus] from plus + [15] plus::return#2 = add::return#0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + [16] return + to:@return + +__bank(cx16_rom, 1) char add(char a , char b) +add: scope:[add] from plus + [17] add::return#1 = add::a#0 + add::b#0 + to:add::@return +add::@return: scope:[add] from add + [18] return + to:@return diff --git a/src/test/ref/call-banked-phi-case-6-close-0.log b/src/test/ref/call-banked-phi-case-6-close-0.log new file mode 100644 index 000000000..b81bf7d9a --- /dev/null +++ b/src/test/ref/call-banked-phi-case-6-close-0.log @@ -0,0 +1,689 @@ +Loading link script "call-banked-phi.ld" + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + plus::a#0 = '0' + plus::b#0 = 7 + call plus + plus::return#0 = plus::return#3 + to:main::@1 +main::@1: scope:[main] from main + plus::return#4 = phi( main/plus::return#0 ) + main::$0 = plus::return#4 + SCREEN[0] = main::$0 + plus::a#1 = '1' + plus::b#1 = 6 + call plus + plus::return#1 = plus::return#3 + to:main::@2 +main::@2: scope:[main] from main::@1 + plus::return#5 = phi( main::@1/plus::return#1 ) + main::$1 = plus::return#5 + SCREEN[1] = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 ) + plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 ) + add::a#0 = plus::a#2 + add::b#0 = plus::b#2 + call add + add::return#0 = add::return#2 + to:plus::@1 +plus::@1: scope:[plus] from plus + add::return#3 = phi( plus/add::return#0 ) + plus::$0 = add::return#3 + plus::return#2 = plus::$0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + plus::return#6 = phi( plus::@1/plus::return#2 ) + plus::return#3 = plus::return#6 + return + to:@return + +__bank(cx16_rom, 1) char add(char a , char b) +add: scope:[add] from plus + add::b#1 = phi( plus/add::b#0 ) + add::a#1 = phi( plus/add::a#0 ) + add::$0 = add::a#1 + add::b#1 + add::return#1 = add::$0 + to:add::@return +add::@return: scope:[add] from add + add::return#4 = phi( add/add::return#1 ) + add::return#2 = add::return#4 + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant char * const SCREEN = (char *)$400 +void __start() +__bank(cx16_rom, 1) char add(char a , char b) +char add::$0 +char add::a +char add::a#0 +char add::a#1 +char add::b +char add::b#0 +char add::b#1 +char add::return +char add::return#0 +char add::return#1 +char add::return#2 +char add::return#3 +char add::return#4 +void main() +char main::$0 +char main::$1 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::$0 +char plus::a +char plus::a#0 +char plus::a#1 +char plus::a#2 +char plus::b +char plus::b#0 +char plus::b#1 +char plus::b#2 +char plus::return +char plus::return#0 +char plus::return#1 +char plus::return#2 +char plus::return#3 +char plus::return#4 +char plus::return#5 +char plus::return#6 + +Adding number conversion cast (unumber) 7 in plus::b#0 = 7 +Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0 +Adding number conversion cast (unumber) 6 in plus::b#1 = 6 +Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast plus::b#0 = (unumber)7 +Inlining cast plus::b#1 = (unumber)6 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 7 +Simplifying constant integer cast 0 +Simplifying constant integer cast 6 +Simplifying constant integer cast 1 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 7 +Finalized unsigned number type (char) 0 +Finalized unsigned number type (char) 6 +Finalized unsigned number type (char) 1 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias plus::return#0 = plus::return#4 +Alias plus::return#1 = plus::return#5 +Alias add::return#0 = add::return#3 +Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3 +Alias add::return#1 = add::$0 add::return#4 add::return#2 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values add::a#1 add::a#0 +Identical Phi Values add::b#1 add::b#0 +Successful SSA optimization Pass2IdenticalPhiElimination +Constant plus::a#0 = '0' +Constant plus::b#0 = 7 +Constant plus::a#1 = '1' +Constant plus::b#1 = 6 +Successful SSA optimization Pass2ConstantIdentification +Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0 +Successful SSA optimization PassNSimplifyExpressionWithZero +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Inlining constant with var siblings plus::a#0 +Inlining constant with var siblings plus::b#0 +Inlining constant with var siblings plus::a#1 +Inlining constant with var siblings plus::b#1 +Constant inlined plus::b#1 = 6 +Constant inlined plus::b#0 = 7 +Constant inlined plus::a#1 = '1' +Constant inlined plus::a#0 = '0' +Successful SSA optimization Pass2ConstantInlining +Consolidated array index constant in *(SCREEN+1) +Successful SSA optimization Pass2ConstantAdditionElimination +Adding NOP phi() at start of main +CALL GRAPH +Calls in [main] to plus:1 plus:5 +Calls in [plus] to add:13 + +Created 2 initial phi equivalence classes +Coalesced down to 2 phi equivalence classes +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] add::a#0 = plus::a#2 + [12] add::b#0 = plus::b#2 + [13] call add + [14] add::return#0 = add::return#1 + to:plus::@1 +plus::@1: scope:[plus] from plus + [15] plus::return#2 = add::return#0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + [16] return + to:@return + +__bank(cx16_rom, 1) char add(char a , char b) +add: scope:[add] from plus + [17] add::return#1 = add::a#0 + add::b#0 + to:add::@return +add::@return: scope:[add] from add + [18] return + to:@return + + +VARIABLE REGISTER WEIGHTS +__bank(cx16_rom, 1) char add(char a , char b) +char add::a +char add::a#0 // 56.0 +char add::b +char add::b#0 // 112.0 +char add::return +char add::return#0 // 22.0 +char add::return#1 // 37.33333333333333 +void main() +char main::$0 // 4.0 +char main::$1 // 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // 11.0 +char plus::b +char plus::b#2 // 5.5 +char plus::return +char plus::return#0 // 4.0 +char plus::return#1 // 4.0 +char plus::return#2 // 3.75 + +Initial phi equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +Added variable plus::return#0 to live range equivalence class [ plus::return#0 ] +Added variable main::$0 to live range equivalence class [ main::$0 ] +Added variable plus::return#1 to live range equivalence class [ plus::return#1 ] +Added variable main::$1 to live range equivalence class [ main::$1 ] +Added variable add::a#0 to live range equivalence class [ add::a#0 ] +Added variable add::b#0 to live range equivalence class [ add::b#0 ] +Added variable add::return#0 to live range equivalence class [ add::return#0 ] +Added variable plus::return#2 to live range equivalence class [ plus::return#2 ] +Added variable add::return#1 to live range equivalence class [ add::return#1 ] +Complete equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +[ plus::return#0 ] +[ main::$0 ] +[ plus::return#1 ] +[ main::$1 ] +[ add::a#0 ] +[ add::b#0 ] +[ add::return#0 ] +[ plus::return#2 ] +[ add::return#1 ] +Allocated zp[1]:2 [ add::b#0 ] +Allocated zp[1]:3 [ add::a#0 ] +Allocated zp[1]:4 [ add::return#1 ] +Allocated zp[1]:5 [ add::return#0 ] +Allocated zp[1]:6 [ plus::a#2 ] +Allocated zp[1]:7 [ plus::b#2 ] +Allocated zp[1]:8 [ plus::return#0 ] +Allocated zp[1]:9 [ main::$0 ] +Allocated zp[1]:10 [ plus::return#1 ] +Allocated zp[1]:11 [ main::$1 ] +Allocated zp[1]:12 [ plus::return#2 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ] +Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [13] call add [ add::return#1 ] ( plus:1 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:4 [ add::return#1 ] +Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a +Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [13] call add [ add::return#1 ] ( plus:1 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a +Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a +Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y , +Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ] +Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ] +Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ] +Uplift Scope [] + +Uplifting [add] best 223 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte x [ add::return#1 ] reg byte a [ add::return#0 ] +Limited combination testing to 100 combinations of 192 possible. +Uplifting [plus] best 195 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ] +Limited combination testing to 100 combinations of 768 possible. +Uplifting [main] best 183 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ] +Uplifting [] best 183 combination +Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ] +Uplifting [add] best 183 combination zp[1]:3 [ add::a#0 ] +Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ] +Uplifting [plus] best 180 combination reg byte x [ plus::return#2 ] +Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +/** + * @file call-banked-phi-case-6-close-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #6. + * Implementation using the __bank() directive. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-6-close-0.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + plus_from_main: + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1 + ldx #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx + txa + jmp __b1 + // main::@1 + __b1: + // [3] main::$0 = plus::return#0 + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + plus_from___b1: + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1 + ldx #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx + txa + jmp __b2 + // main::@2 + __b2: + // [7] main::$1 = plus::return#1 + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // close call + sta SCREEN+1 + jmp __breturn + // main::@return + __breturn: + // [9] return + rts +} +.segment RAM_Bank1 + // plus +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa + sta.z add.a + // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx + txa + // [13] call add -- call_phi_close_cx16_rom + sta.z $ff + lda.z 1 + pha + lda #1 + sta.z 1 + lda.z $ff + jsr add + sta.z $ff + pla + sta.z 1 + lda.z $ff + // [14] add::return#0 = add::return#1 -- vbuaa=vbuxx + txa + jmp __b1 + // plus::@1 + __b1: + // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa + tax + jmp __breturn + // plus::@return + __breturn: + // [16] return + rts +} +.segment ROM_Bank1 + // add +// __register(X) char add(__zp(2) char a, __register(A) char b) +// __bank(cx16_rom, 1) +add: { + .label a = 2 + // [17] add::return#1 = add::a#0 + add::b#0 -- vbuxx=vbuz1_plus_vbuaa + clc + adc.z a + tax + jmp __breturn + // add::@return + __breturn: + // [18] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __b2 +Removing instruction jmp __breturn +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction plus_from_main: +Removing instruction __b1: +Removing instruction plus_from___b1: +Removing instruction __b2: +Removing instruction __breturn: +Removing instruction __b1: +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +__bank(cx16_rom, 1) char add(char a , char b) +char add::a +char add::a#0 // a zp[1]:2 56.0 +char add::b +char add::b#0 // reg byte a 112.0 +char add::return +char add::return#0 // reg byte a 22.0 +char add::return#1 // reg byte x 37.33333333333333 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte a 11.0 +char plus::b +char plus::b#2 // reg byte x 5.5 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte x 3.75 + +reg byte a [ plus::a#2 ] +reg byte x [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +zp[1]:2 [ add::a#0 ] +reg byte a [ add::b#0 ] +reg byte a [ add::return#0 ] +reg byte x [ plus::return#2 ] +reg byte x [ add::return#1 ] + + +FINAL ASSEMBLER +Score: 162 + + // File Comments +/** + * @file call-banked-phi-case-6-close-0.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #6. + * Implementation using the __bank() directive. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-6-close-0.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // plus('0', 7) + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1 + ldx #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('0', 7) + // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx + txa + // main::@1 + // [3] main::$0 = plus::return#0 + // SCREEN[0] = plus('0', 7) + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // plus('1', 6) + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1 + ldx #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('1', 6) + // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx + txa + // main::@2 + // [7] main::$1 = plus::return#1 + // SCREEN[1] = plus('1', 6) + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // close call + sta SCREEN+1 + // main::@return + // } + // [9] return + rts +} +.segment RAM_Bank1 + // plus +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // add(a, b) + // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa + sta.z add.a + // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx + txa + // [13] call add -- call_phi_close_cx16_rom + sta.z $ff + lda.z 1 + pha + lda #1 + sta.z 1 + lda.z $ff + jsr add + sta.z $ff + pla + sta.z 1 + lda.z $ff + // [14] add::return#0 = add::return#1 -- vbuaa=vbuxx + txa + // plus::@1 + // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa + tax + // plus::@return + // } + // [16] return + rts +} +.segment ROM_Bank1 + // add +// __register(X) char add(__zp(2) char a, __register(A) char b) +// __bank(cx16_rom, 1) +add: { + .label a = 2 + // a+b + // [17] add::return#1 = add::a#0 + add::b#0 -- vbuxx=vbuz1_plus_vbuaa + clc + adc.z a + tax + // add::@return + // } + // [18] return + rts +} + // File Data + diff --git a/src/test/ref/call-banked-phi-case-6-close-0.sym b/src/test/ref/call-banked-phi-case-6-close-0.sym new file mode 100644 index 000000000..26eed0f1c --- /dev/null +++ b/src/test/ref/call-banked-phi-case-6-close-0.sym @@ -0,0 +1,33 @@ +__constant char * const SCREEN = (char *) 1024 +__bank(cx16_rom, 1) char add(char a , char b) +char add::a +char add::a#0 // a zp[1]:2 56.0 +char add::b +char add::b#0 // reg byte a 112.0 +char add::return +char add::return#0 // reg byte a 22.0 +char add::return#1 // reg byte x 37.33333333333333 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte a 11.0 +char plus::b +char plus::b#2 // reg byte x 5.5 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte x 3.75 + +reg byte a [ plus::a#2 ] +reg byte x [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +zp[1]:2 [ add::a#0 ] +reg byte a [ add::b#0 ] +reg byte a [ add::return#0 ] +reg byte x [ plus::return#2 ] +reg byte x [ add::return#1 ] diff --git a/src/test/ref/call-banked-phi-case-6-close-1.asm b/src/test/ref/call-banked-phi-case-6-close-1.asm new file mode 100644 index 000000000..89f99c527 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-6-close-1.asm @@ -0,0 +1,119 @@ +/** + * @file call-banked-phi-case-6-close-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #6. + * Implementation using the #pragma bank and nobank directives. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + .file [name="call-banked-phi-case-6-close-1.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + .label SCREEN = $400 +.segment Code +main: { + // plus('0', 7) + ldx #7 + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('0', 7) + txa + // SCREEN[0] = plus('0', 7) + sta SCREEN + // plus('1', 6) + ldx #6 + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('1', 6) + txa + // SCREEN[1] = plus('1', 6) + // close call + sta SCREEN+1 + // } + rts +} +.segment RAM_Bank1 +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // add(a, b) + sta.z add.a + txa + sta.z $ff + lda.z 1 + pha + lda #1 + sta.z 1 + lda.z $ff + jsr add + sta.z $ff + pla + sta.z 1 + lda.z $ff + txa + tax + // } + rts +} +.segment ROM_Bank1 +// __register(X) char add(__zp(2) char a, __register(A) char b) +// __bank(cx16_rom, 1) +add: { + .label a = 2 + // a+b + clc + adc.z a + tax + // } + rts +} diff --git a/src/test/ref/call-banked-phi-case-6-close-1.cfg b/src/test/ref/call-banked-phi-case-6-close-1.cfg new file mode 100644 index 000000000..c10c5c2f7 --- /dev/null +++ b/src/test/ref/call-banked-phi-case-6-close-1.cfg @@ -0,0 +1,44 @@ + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] add::a#0 = plus::a#2 + [12] add::b#0 = plus::b#2 + [13] call add + [14] add::return#0 = add::return#1 + to:plus::@1 +plus::@1: scope:[plus] from plus + [15] plus::return#2 = add::return#0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + [16] return + to:@return + +__bank(cx16_rom, 1) char add(char a , char b) +add: scope:[add] from plus + [17] add::return#1 = add::a#0 + add::b#0 + to:add::@return +add::@return: scope:[add] from add + [18] return + to:@return diff --git a/src/test/ref/call-banked-phi-case-6-close-1.log b/src/test/ref/call-banked-phi-case-6-close-1.log new file mode 100644 index 000000000..c64b16fba --- /dev/null +++ b/src/test/ref/call-banked-phi-case-6-close-1.log @@ -0,0 +1,689 @@ +Loading link script "call-banked-phi.ld" + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + plus::a#0 = '0' + plus::b#0 = 7 + call plus + plus::return#0 = plus::return#3 + to:main::@1 +main::@1: scope:[main] from main + plus::return#4 = phi( main/plus::return#0 ) + main::$0 = plus::return#4 + SCREEN[0] = main::$0 + plus::a#1 = '1' + plus::b#1 = 6 + call plus + plus::return#1 = plus::return#3 + to:main::@2 +main::@2: scope:[main] from main::@1 + plus::return#5 = phi( main::@1/plus::return#1 ) + main::$1 = plus::return#5 + SCREEN[1] = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 ) + plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 ) + add::a#0 = plus::a#2 + add::b#0 = plus::b#2 + call add + add::return#0 = add::return#2 + to:plus::@1 +plus::@1: scope:[plus] from plus + add::return#3 = phi( plus/add::return#0 ) + plus::$0 = add::return#3 + plus::return#2 = plus::$0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + plus::return#6 = phi( plus::@1/plus::return#2 ) + plus::return#3 = plus::return#6 + return + to:@return + +__bank(cx16_rom, 1) char add(char a , char b) +add: scope:[add] from plus + add::b#1 = phi( plus/add::b#0 ) + add::a#1 = phi( plus/add::a#0 ) + add::$0 = add::a#1 + add::b#1 + add::return#1 = add::$0 + to:add::@return +add::@return: scope:[add] from add + add::return#4 = phi( add/add::return#1 ) + add::return#2 = add::return#4 + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant char * const SCREEN = (char *)$400 +void __start() +__bank(cx16_rom, 1) char add(char a , char b) +char add::$0 +char add::a +char add::a#0 +char add::a#1 +char add::b +char add::b#0 +char add::b#1 +char add::return +char add::return#0 +char add::return#1 +char add::return#2 +char add::return#3 +char add::return#4 +void main() +char main::$0 +char main::$1 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::$0 +char plus::a +char plus::a#0 +char plus::a#1 +char plus::a#2 +char plus::b +char plus::b#0 +char plus::b#1 +char plus::b#2 +char plus::return +char plus::return#0 +char plus::return#1 +char plus::return#2 +char plus::return#3 +char plus::return#4 +char plus::return#5 +char plus::return#6 + +Adding number conversion cast (unumber) 7 in plus::b#0 = 7 +Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0 +Adding number conversion cast (unumber) 6 in plus::b#1 = 6 +Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast plus::b#0 = (unumber)7 +Inlining cast plus::b#1 = (unumber)6 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 7 +Simplifying constant integer cast 0 +Simplifying constant integer cast 6 +Simplifying constant integer cast 1 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 7 +Finalized unsigned number type (char) 0 +Finalized unsigned number type (char) 6 +Finalized unsigned number type (char) 1 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias plus::return#0 = plus::return#4 +Alias plus::return#1 = plus::return#5 +Alias add::return#0 = add::return#3 +Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3 +Alias add::return#1 = add::$0 add::return#4 add::return#2 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values add::a#1 add::a#0 +Identical Phi Values add::b#1 add::b#0 +Successful SSA optimization Pass2IdenticalPhiElimination +Constant plus::a#0 = '0' +Constant plus::b#0 = 7 +Constant plus::a#1 = '1' +Constant plus::b#1 = 6 +Successful SSA optimization Pass2ConstantIdentification +Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0 +Successful SSA optimization PassNSimplifyExpressionWithZero +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Inlining constant with var siblings plus::a#0 +Inlining constant with var siblings plus::b#0 +Inlining constant with var siblings plus::a#1 +Inlining constant with var siblings plus::b#1 +Constant inlined plus::b#1 = 6 +Constant inlined plus::b#0 = 7 +Constant inlined plus::a#1 = '1' +Constant inlined plus::a#0 = '0' +Successful SSA optimization Pass2ConstantInlining +Consolidated array index constant in *(SCREEN+1) +Successful SSA optimization Pass2ConstantAdditionElimination +Adding NOP phi() at start of main +CALL GRAPH +Calls in [main] to plus:1 plus:5 +Calls in [plus] to add:13 + +Created 2 initial phi equivalence classes +Coalesced down to 2 phi equivalence classes +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] phi() + [1] call plus + [2] plus::return#0 = plus::return#2 + to:main::@1 +main::@1: scope:[main] from main + [3] main::$0 = plus::return#0 + [4] *SCREEN = main::$0 + [5] call plus + [6] plus::return#1 = plus::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [7] main::$1 = plus::return#1 + [8] *(SCREEN+1) = main::$1 + to:main::@return +main::@return: scope:[main] from main::@2 + [9] return + to:@return + +__bank(cx16_ram, 1) char plus(char a , char b) +plus: scope:[plus] from main main::@1 + [10] plus::b#2 = phi( main/7, main::@1/6 ) + [10] plus::a#2 = phi( main/'0', main::@1/'1' ) + [11] add::a#0 = plus::a#2 + [12] add::b#0 = plus::b#2 + [13] call add + [14] add::return#0 = add::return#1 + to:plus::@1 +plus::@1: scope:[plus] from plus + [15] plus::return#2 = add::return#0 + to:plus::@return +plus::@return: scope:[plus] from plus::@1 + [16] return + to:@return + +__bank(cx16_rom, 1) char add(char a , char b) +add: scope:[add] from plus + [17] add::return#1 = add::a#0 + add::b#0 + to:add::@return +add::@return: scope:[add] from add + [18] return + to:@return + + +VARIABLE REGISTER WEIGHTS +__bank(cx16_rom, 1) char add(char a , char b) +char add::a +char add::a#0 // 56.0 +char add::b +char add::b#0 // 112.0 +char add::return +char add::return#0 // 22.0 +char add::return#1 // 37.33333333333333 +void main() +char main::$0 // 4.0 +char main::$1 // 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // 11.0 +char plus::b +char plus::b#2 // 5.5 +char plus::return +char plus::return#0 // 4.0 +char plus::return#1 // 4.0 +char plus::return#2 // 3.75 + +Initial phi equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +Added variable plus::return#0 to live range equivalence class [ plus::return#0 ] +Added variable main::$0 to live range equivalence class [ main::$0 ] +Added variable plus::return#1 to live range equivalence class [ plus::return#1 ] +Added variable main::$1 to live range equivalence class [ main::$1 ] +Added variable add::a#0 to live range equivalence class [ add::a#0 ] +Added variable add::b#0 to live range equivalence class [ add::b#0 ] +Added variable add::return#0 to live range equivalence class [ add::return#0 ] +Added variable plus::return#2 to live range equivalence class [ plus::return#2 ] +Added variable add::return#1 to live range equivalence class [ add::return#1 ] +Complete equivalence classes +[ plus::a#2 ] +[ plus::b#2 ] +[ plus::return#0 ] +[ main::$0 ] +[ plus::return#1 ] +[ main::$1 ] +[ add::a#0 ] +[ add::b#0 ] +[ add::return#0 ] +[ plus::return#2 ] +[ add::return#1 ] +Allocated zp[1]:2 [ add::b#0 ] +Allocated zp[1]:3 [ add::a#0 ] +Allocated zp[1]:4 [ add::return#1 ] +Allocated zp[1]:5 [ add::return#0 ] +Allocated zp[1]:6 [ plus::a#2 ] +Allocated zp[1]:7 [ plus::b#2 ] +Allocated zp[1]:8 [ plus::return#0 ] +Allocated zp[1]:9 [ main::$0 ] +Allocated zp[1]:10 [ plus::return#1 ] +Allocated zp[1]:11 [ main::$1 ] +Allocated zp[1]:12 [ plus::return#2 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ] +Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [13] call add [ add::return#1 ] ( plus:1 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:4 [ add::return#1 ] +Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a +Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [13] call add [ add::return#1 ] ( plus:1 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a +Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a +Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y , +Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ] +Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ] +Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ] +Uplift Scope [] + +Uplifting [add] best 223 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte x [ add::return#1 ] reg byte a [ add::return#0 ] +Limited combination testing to 100 combinations of 192 possible. +Uplifting [plus] best 195 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ] +Limited combination testing to 100 combinations of 768 possible. +Uplifting [main] best 183 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ] +Uplifting [] best 183 combination +Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ] +Uplifting [add] best 183 combination zp[1]:3 [ add::a#0 ] +Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ] +Uplifting [plus] best 180 combination reg byte x [ plus::return#2 ] +Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +/** + * @file call-banked-phi-case-6-close-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #6. + * Implementation using the #pragma bank and nobank directives. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-6-close-1.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + plus_from_main: + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1 + ldx #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx + txa + jmp __b1 + // main::@1 + __b1: + // [3] main::$0 = plus::return#0 + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + plus_from___b1: + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1 + ldx #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx + txa + jmp __b2 + // main::@2 + __b2: + // [7] main::$1 = plus::return#1 + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // close call + sta SCREEN+1 + jmp __breturn + // main::@return + __breturn: + // [9] return + rts +} +.segment RAM_Bank1 + // plus +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa + sta.z add.a + // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx + txa + // [13] call add -- call_phi_close_cx16_rom + sta.z $ff + lda.z 1 + pha + lda #1 + sta.z 1 + lda.z $ff + jsr add + sta.z $ff + pla + sta.z 1 + lda.z $ff + // [14] add::return#0 = add::return#1 -- vbuaa=vbuxx + txa + jmp __b1 + // plus::@1 + __b1: + // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa + tax + jmp __breturn + // plus::@return + __breturn: + // [16] return + rts +} +.segment ROM_Bank1 + // add +// __register(X) char add(__zp(2) char a, __register(A) char b) +// __bank(cx16_rom, 1) +add: { + .label a = 2 + // [17] add::return#1 = add::a#0 + add::b#0 -- vbuxx=vbuz1_plus_vbuaa + clc + adc.z a + tax + jmp __breturn + // add::@return + __breturn: + // [18] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __b2 +Removing instruction jmp __breturn +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction plus_from_main: +Removing instruction __b1: +Removing instruction plus_from___b1: +Removing instruction __b2: +Removing instruction __breturn: +Removing instruction __b1: +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +__bank(cx16_rom, 1) char add(char a , char b) +char add::a +char add::a#0 // a zp[1]:2 56.0 +char add::b +char add::b#0 // reg byte a 112.0 +char add::return +char add::return#0 // reg byte a 22.0 +char add::return#1 // reg byte x 37.33333333333333 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte a 11.0 +char plus::b +char plus::b#2 // reg byte x 5.5 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte x 3.75 + +reg byte a [ plus::a#2 ] +reg byte x [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +zp[1]:2 [ add::a#0 ] +reg byte a [ add::b#0 ] +reg byte a [ add::return#0 ] +reg byte x [ plus::return#2 ] +reg byte x [ add::return#1 ] + + +FINAL ASSEMBLER +Score: 162 + + // File Comments +/** + * @file call-banked-phi-case-6-close-1.c + * @author Sven Van de Velde (sven.van.de.velde@telenet.be), + * @author Jesper Gravgaard + * @brief Test a procedure with calling convention PHI - case #6. + * Implementation using the #pragma bank and nobank directives. + * @version 0.1 + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + * The following cases exist in banked calling implementations: + * + * - case #1 - unbanked to unbanked and no banking areas + * - case #2 - unbanked to banked to any bank area + * - case #3 - banked to unbanked from any bank area + * - case #4 - banked to same bank in same bank area + * - case #5 - banked to different bank in same bank area + * - case #6 - banked to any bank between different bank areas + * + * This brings us to the call types: + * + * - near = case #1, #3, #4 + * - close = case #2, #6 + * - far = case #5 + * + */ + // Upstart + .file [name="call-banked-phi-case-6-close-1.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // plus('0', 7) + // [1] call plus + // [10] phi from main to plus [phi:main->plus] + // [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1 + ldx #7 + // [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram + lda #'0' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('0', 7) + // [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx + txa + // main::@1 + // [3] main::$0 = plus::return#0 + // SCREEN[0] = plus('0', 7) + // [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa + sta SCREEN + // plus('1', 6) + // [5] call plus + // [10] phi from main::@1 to plus [phi:main::@1->plus] + // [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1 + ldx #6 + // [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram + lda #'1' + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus('1', 6) + // [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx + txa + // main::@2 + // [7] main::$1 = plus::return#1 + // SCREEN[1] = plus('1', 6) + // [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa + // close call + sta SCREEN+1 + // main::@return + // } + // [9] return + rts +} +.segment RAM_Bank1 + // plus +// __register(X) char plus(__register(A) char a, __register(X) char b) +// __bank(cx16_ram, 1) +plus: { + // add(a, b) + // [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa + sta.z add.a + // [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx + txa + // [13] call add -- call_phi_close_cx16_rom + sta.z $ff + lda.z 1 + pha + lda #1 + sta.z 1 + lda.z $ff + jsr add + sta.z $ff + pla + sta.z 1 + lda.z $ff + // [14] add::return#0 = add::return#1 -- vbuaa=vbuxx + txa + // plus::@1 + // [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa + tax + // plus::@return + // } + // [16] return + rts +} +.segment ROM_Bank1 + // add +// __register(X) char add(__zp(2) char a, __register(A) char b) +// __bank(cx16_rom, 1) +add: { + .label a = 2 + // a+b + // [17] add::return#1 = add::a#0 + add::b#0 -- vbuxx=vbuz1_plus_vbuaa + clc + adc.z a + tax + // add::@return + // } + // [18] return + rts +} + // File Data + diff --git a/src/test/ref/call-banked-phi-case-6-close-1.sym b/src/test/ref/call-banked-phi-case-6-close-1.sym new file mode 100644 index 000000000..26eed0f1c --- /dev/null +++ b/src/test/ref/call-banked-phi-case-6-close-1.sym @@ -0,0 +1,33 @@ +__constant char * const SCREEN = (char *) 1024 +__bank(cx16_rom, 1) char add(char a , char b) +char add::a +char add::a#0 // a zp[1]:2 56.0 +char add::b +char add::b#0 // reg byte a 112.0 +char add::return +char add::return#0 // reg byte a 22.0 +char add::return#1 // reg byte x 37.33333333333333 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +__bank(cx16_ram, 1) char plus(char a , char b) +char plus::a +char plus::a#2 // reg byte a 11.0 +char plus::b +char plus::b#2 // reg byte x 5.5 +char plus::return +char plus::return#0 // reg byte a 4.0 +char plus::return#1 // reg byte a 4.0 +char plus::return#2 // reg byte x 3.75 + +reg byte a [ plus::a#2 ] +reg byte x [ plus::b#2 ] +reg byte a [ plus::return#0 ] +reg byte a [ main::$0 ] +reg byte a [ plus::return#1 ] +reg byte a [ main::$1 ] +zp[1]:2 [ add::a#0 ] +reg byte a [ add::b#0 ] +reg byte a [ add::return#0 ] +reg byte x [ plus::return#2 ] +reg byte x [ add::return#1 ] diff --git a/src/test/ref/call-banked-phi-memvars.asm b/src/test/ref/call-banked-phi-memvars.asm new file mode 100644 index 000000000..bd4556180 --- /dev/null +++ b/src/test/ref/call-banked-phi-memvars.asm @@ -0,0 +1,144 @@ +/** + * Test banked calls with memory variables. + * The parameters & return should end up in the shared/common bank. + */ + .file [name="call-banked-phi-memvars.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + .label SCREEN = $400 +.segment Code +main: { + ldy #0 + __b1: + // for(char i=0;i<5; i++) + cpy #5 + bcc __b2 + // } + rts + __b2: + // plus(100, (int)i) + tya + sta plus.b + lda #0 + sta plus.b+1 + lda #<$64 + sta plus.a + lda #>$64 + sta plus.a+1 + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus(100, (int)i) + // SCREEN[i] = plus(100, (int)i) + tya + asl + tax + lda __1 + sta SCREEN,x + lda __1+1 + sta SCREEN+1,x + // 10+i + tya + tax + axs #-[$a] + // plus(200, (int)i) + tya + sta plus.b + lda #0 + sta plus.b+1 + lda #<$c8 + sta plus.a + lda #>$c8 + sta plus.a+1 + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus(200, (int)i) + // SCREEN[10+i] = plus(200, (int)i) + txa + asl + tax + lda __3 + sta SCREEN,x + lda __3+1 + sta SCREEN+1,x + // for(char i=0;i<5; i++) + iny + jmp __b1 + .segment Data + .label __1 = plus.b + .label __3 = plus.b +} +.segment RAM_Bank1 +// __mem() int plus(__mem() int a, __mem() int b) +// __bank(cx16_ram, 1) +plus: { + // r += a + clc + lda a + adc #<2 + sta r + lda a+1 + adc #>2 + sta r+1 + // r += b + clc + lda r + adc b + sta r + lda r+1 + adc b+1 + sta r+1 + // r += a + clc + lda r + adc a + sta r + lda r+1 + adc a+1 + sta r+1 + // r += b + clc + lda return + adc r + sta return + lda return+1 + adc r+1 + sta return+1 + // } + rts + .segment Data + b: .word 0 + .label return = b + .segment RAM_Bank1 + r: .word 0 + .segment Data + a: .word 0 +} diff --git a/src/test/ref/call-banked-phi-memvars.cfg b/src/test/ref/call-banked-phi-memvars.cfg new file mode 100644 index 000000000..4cb2646a1 --- /dev/null +++ b/src/test/ref/call-banked-phi-memvars.cfg @@ -0,0 +1,45 @@ + +void main() +main: scope:[main] from + [0] phi() + to:main::@1 +main::@1: scope:[main] from main main::@4 + [1] main::i#2 = phi( main/0, main::@4/main::i#1 ) + [2] if(main::i#2<5) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [3] return + to:@return +main::@2: scope:[main] from main::@1 + [4] plus::b#0 = (int)main::i#2 + [5] call plus + [6] plus::return#0 = plus::return#2 + to:main::@3 +main::@3: scope:[main] from main::@2 + [7] main::$1 = plus::return#0 + [8] main::$4 = main::i#2 << 1 + [9] SCREEN[main::$4] = main::$1 + [10] main::$2 = $a + main::i#2 + [11] plus::b#1 = (int)main::i#2 + [12] call plus + [13] plus::return#1 = plus::return#2 + to:main::@4 +main::@4: scope:[main] from main::@3 + [14] main::$3 = plus::return#1 + [15] main::$5 = main::$2 << 1 + [16] SCREEN[main::$5] = main::$3 + [17] main::i#1 = ++ main::i#2 + to:main::@1 + +__bank(cx16_ram, 1) int plus(int a , int b) +plus: scope:[plus] from main::@2 main::@3 + [18] plus::b#2 = phi( main::@2/plus::b#0, main::@3/plus::b#1 ) + [18] plus::a#2 = phi( main::@2/$64, main::@3/$c8 ) + [19] plus::r#1 = 2 + plus::a#2 + [20] plus::r#2 = plus::r#1 + plus::b#2 + [21] plus::r#3 = plus::r#2 + plus::a#2 + [22] plus::return#2 = plus::r#3 + plus::b#2 + to:plus::@return +plus::@return: scope:[plus] from plus + [23] return + to:@return diff --git a/src/test/ref/call-banked-phi-memvars.log b/src/test/ref/call-banked-phi-memvars.log new file mode 100644 index 000000000..6aea5faca --- /dev/null +++ b/src/test/ref/call-banked-phi-memvars.log @@ -0,0 +1,803 @@ +Loading link script "call-banked-phi.ld" +Updating intermediate variable memory area to MAIN_MEMORY main::$0 +Updating intermediate variable memory area to MAIN_MEMORY main::$1 +Updating intermediate variable memory area to MAIN_MEMORY main::$2 +Updating intermediate variable memory area to MAIN_MEMORY main::$3 +Fixing banked procedure parameter/return value to default segment plus::a +Fixing banked procedure parameter/return value to default segment plus::b +Fixing banked procedure parameter/return value to default segment plus::return + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + main::i#0 = 0 + to:main::@1 +main::@1: scope:[main] from main main::@4 + main::i#2 = phi( main/main::i#0, main::@4/main::i#1 ) + main::$0 = main::i#2 < 5 + if(main::$0) goto main::@2 + to:main::@return +main::@2: scope:[main] from main::@1 + main::i#3 = phi( main::@1/main::i#2 ) + plus::a#0 = $64 + plus::b#0 = (int)main::i#3 + call plus + plus::return#0 = plus::return#3 + to:main::@3 +main::@3: scope:[main] from main::@2 + main::i#4 = phi( main::@2/main::i#3 ) + plus::return#4 = phi( main::@2/plus::return#0 ) + main::$1 = plus::return#4 + main::$4 = main::i#4 * SIZEOF_INT + SCREEN[main::$4] = main::$1 + main::$2 = $a + main::i#4 + plus::a#1 = $c8 + plus::b#1 = (int)main::i#4 + call plus + plus::return#1 = plus::return#3 + to:main::@4 +main::@4: scope:[main] from main::@3 + main::i#5 = phi( main::@3/main::i#4 ) + plus::return#5 = phi( main::@3/plus::return#1 ) + main::$3 = plus::return#5 + main::$5 = main::$2 * SIZEOF_INT + SCREEN[main::$5] = main::$3 + main::i#1 = ++ main::i#5 + to:main::@1 +main::@return: scope:[main] from main::@1 + return + to:@return + +__bank(cx16_ram, 1) int plus(int a , int b) +plus: scope:[plus] from main::@2 main::@3 + plus::b#2 = phi( main::@2/plus::b#0, main::@3/plus::b#1 ) + plus::a#2 = phi( main::@2/plus::a#0, main::@3/plus::a#1 ) + plus::r#0 = 2 + plus::r#1 = plus::r#0 + plus::a#2 + plus::r#2 = plus::r#1 + plus::b#2 + plus::r#3 = plus::r#2 + plus::a#2 + plus::r#4 = plus::r#3 + plus::b#2 + plus::return#2 = plus::r#4 + to:plus::@return +plus::@return: scope:[plus] from plus + plus::return#6 = phi( plus/plus::return#2 ) + plus::return#3 = plus::return#6 + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant int * const SCREEN = (int *)$400 +__constant char SIZEOF_INT = 2 +void __start() +void main() +bool main::$0 +int main::$1 +number main::$2 +int main::$3 +char main::$4 +number main::$5 +char main::i +char main::i#0 +char main::i#1 +char main::i#2 +char main::i#3 +char main::i#4 +char main::i#5 +__bank(cx16_ram, 1) int plus(int a , int b) +int plus::a +int plus::a#0 +int plus::a#1 +int plus::a#2 +int plus::b +int plus::b#0 +int plus::b#1 +int plus::b#2 +int plus::r +int plus::r#0 +int plus::r#1 +int plus::r#2 +int plus::r#3 +int plus::r#4 +int plus::return +int plus::return#0 +int plus::return#1 +int plus::return#2 +int plus::return#3 +int plus::return#4 +int plus::return#5 +int plus::return#6 + +Adding number conversion cast (unumber) 5 in main::$0 = main::i#2 < 5 +Adding number conversion cast (snumber) $64 in plus::a#0 = $64 +Adding number conversion cast (unumber) $a in main::$2 = $a + main::i#4 +Adding number conversion cast (unumber) main::$2 in main::$2 = (unumber)$a + main::i#4 +Adding number conversion cast (snumber) $c8 in plus::a#1 = $c8 +Adding number conversion cast (unumber) main::$5 in main::$5 = main::$2 * SIZEOF_INT +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast plus::a#0 = (snumber)$64 +Inlining cast plus::a#1 = (snumber)$c8 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (int *) 1024 +Simplifying constant integer cast 5 +Simplifying constant integer cast $64 +Simplifying constant integer cast $a +Simplifying constant integer cast $c8 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 5 +Finalized signed number type (signed char) $64 +Finalized unsigned number type (char) $a +Finalized signed number type (int) $c8 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Inferred type updated to char in main::$2 = $a + main::i#4 +Inferred type updated to char in main::$5 = main::$2 * SIZEOF_INT +Alias main::i#2 = main::i#3 main::i#4 main::i#5 +Alias plus::return#0 = plus::return#4 +Alias plus::return#1 = plus::return#5 +Alias plus::return#2 = plus::r#4 plus::return#6 plus::return#3 +Successful SSA optimization Pass2AliasElimination +Simple Condition main::$0 [3] if(main::i#2<5) goto main::@2 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant main::i#0 = 0 +Constant plus::a#0 = $64 +Constant plus::a#1 = $c8 +Constant plus::r#0 = 2 +Successful SSA optimization Pass2ConstantIdentification +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Rewriting multiplication to use shift [6] main::$4 = main::i#2 * SIZEOF_INT +Rewriting multiplication to use shift [13] main::$5 = main::$2 * SIZEOF_INT +Successful SSA optimization Pass2MultiplyToShiftRewriting +Inlining constant with var siblings main::i#0 +Inlining constant with var siblings plus::a#0 +Inlining constant with var siblings plus::a#1 +Inlining constant with var siblings plus::r#0 +Constant inlined main::i#0 = 0 +Constant inlined plus::a#1 = $c8 +Constant inlined plus::r#0 = 2 +Constant inlined plus::a#0 = $64 +Successful SSA optimization Pass2ConstantInlining +Eliminating unused constant SIZEOF_INT +Successful SSA optimization PassNEliminateUnusedVars +Adding NOP phi() at start of main +CALL GRAPH +Calls in [main] to plus:6 plus:14 + +Created 3 initial phi equivalence classes +Coalesced [5] plus::b#3 = plus::b#0 +Coalesced [13] plus::b#4 = plus::b#1 +Coalesced [20] main::i#6 = main::i#1 +Coalesced down to 3 phi equivalence classes +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] phi() + to:main::@1 +main::@1: scope:[main] from main main::@4 + [1] main::i#2 = phi( main/0, main::@4/main::i#1 ) + [2] if(main::i#2<5) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [3] return + to:@return +main::@2: scope:[main] from main::@1 + [4] plus::b#0 = (int)main::i#2 + [5] call plus + [6] plus::return#0 = plus::return#2 + to:main::@3 +main::@3: scope:[main] from main::@2 + [7] main::$1 = plus::return#0 + [8] main::$4 = main::i#2 << 1 + [9] SCREEN[main::$4] = main::$1 + [10] main::$2 = $a + main::i#2 + [11] plus::b#1 = (int)main::i#2 + [12] call plus + [13] plus::return#1 = plus::return#2 + to:main::@4 +main::@4: scope:[main] from main::@3 + [14] main::$3 = plus::return#1 + [15] main::$5 = main::$2 << 1 + [16] SCREEN[main::$5] = main::$3 + [17] main::i#1 = ++ main::i#2 + to:main::@1 + +__bank(cx16_ram, 1) int plus(int a , int b) +plus: scope:[plus] from main::@2 main::@3 + [18] plus::b#2 = phi( main::@2/plus::b#0, main::@3/plus::b#1 ) + [18] plus::a#2 = phi( main::@2/$64, main::@3/$c8 ) + [19] plus::r#1 = 2 + plus::a#2 + [20] plus::r#2 = plus::r#1 + plus::b#2 + [21] plus::r#3 = plus::r#2 + plus::a#2 + [22] plus::return#2 = plus::r#3 + plus::b#2 + to:plus::@return +plus::@return: scope:[plus] from plus + [23] return + to:@return + + +VARIABLE REGISTER WEIGHTS +void main() +int main::$1 // 11.0 +char main::$2 // 4.4 +int main::$3 // 11.0 +char main::$4 // 22.0 +char main::$5 // 22.0 +char main::i +char main::i#1 // 22.0 +char main::i#2 // 3.6666666666666665 +__bank(cx16_ram, 1) int plus(int a , int b) +int plus::a +int plus::a#2 // 67.33333333333333 +int plus::b +int plus::b#0 // 22.0 +int plus::b#1 // 22.0 +int plus::b#2 // 56.0 +int plus::r +int plus::r#1 // 202.0 +int plus::r#2 // 202.0 +int plus::r#3 // 202.0 +int plus::return +int plus::return#0 // 22.0 +int plus::return#1 // 22.0 +int plus::return#2 // 30.75 + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +[ plus::a#2 ] +[ plus::b#2 plus::b#0 plus::b#1 ] +Added variable plus::return#0 to live range equivalence class [ plus::return#0 ] +Added variable main::$1 to live range equivalence class [ main::$1 ] +Added variable main::$4 to live range equivalence class [ main::$4 ] +Added variable main::$2 to live range equivalence class [ main::$2 ] +Added variable plus::return#1 to live range equivalence class [ plus::return#1 ] +Added variable main::$3 to live range equivalence class [ main::$3 ] +Added variable main::$5 to live range equivalence class [ main::$5 ] +Added variable plus::r#1 to live range equivalence class [ plus::r#1 ] +Added variable plus::r#2 to live range equivalence class [ plus::r#2 ] +Added variable plus::r#3 to live range equivalence class [ plus::r#3 ] +Added variable plus::return#2 to live range equivalence class [ plus::return#2 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +[ plus::a#2 ] +[ plus::b#2 plus::b#0 plus::b#1 ] +[ plus::return#0 ] +[ main::$1 ] +[ main::$4 ] +[ main::$2 ] +[ plus::return#1 ] +[ main::$3 ] +[ main::$5 ] +[ plus::r#1 ] +[ plus::r#2 ] +[ plus::r#3 ] +[ plus::return#2 ] +Allocated mem[2] [ plus::r#1 ] +Allocated mem[2] [ plus::r#2 ] +Allocated mem[2] [ plus::r#3 ] +Allocated mem[2] [ plus::b#2 plus::b#0 plus::b#1 ] +Allocated mem[2] [ plus::a#2 ] +Allocated mem[2] [ plus::return#2 ] +Allocated mem[1] [ main::i#2 main::i#1 ] +Allocated mem[2] [ plus::return#0 ] +Allocated mem[1] [ main::$4 ] +Allocated mem[2] [ plus::return#1 ] +Allocated mem[1] [ main::$5 ] +Allocated mem[2] [ main::$1 ] +Allocated mem[2] [ main::$3 ] +Allocated mem[1] [ main::$2 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [4] plus::b#0 = (int)main::i#2 [ main::i#2 plus::b#0 ] ( [ main::i#2 plus::b#0 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for mem[1] [ main::i#2 main::i#1 ] +Statement [5] call plus [ main::i#2 plus::return#2 ] ( [ main::i#2 plus::return#2 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Statement [6] plus::return#0 = plus::return#2 [ main::i#2 plus::return#0 ] ( [ main::i#2 plus::return#0 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Statement [7] main::$1 = plus::return#0 [ main::i#2 main::$1 ] ( [ main::i#2 main::$1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [8] main::$4 = main::i#2 << 1 [ main::i#2 main::$1 main::$4 ] ( [ main::i#2 main::$1 main::$4 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [9] SCREEN[main::$4] = main::$1 [ main::i#2 ] ( [ main::i#2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [10] main::$2 = $a + main::i#2 [ main::i#2 main::$2 ] ( [ main::i#2 main::$2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [11] plus::b#1 = (int)main::i#2 [ main::i#2 main::$2 plus::b#1 ] ( [ main::i#2 main::$2 plus::b#1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for mem[1] [ main::$2 ] +Statement [12] call plus [ main::i#2 plus::return#2 main::$2 ] ( [ main::i#2 plus::return#2 main::$2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [13] plus::return#1 = plus::return#2 [ main::i#2 main::$2 plus::return#1 ] ( [ main::i#2 main::$2 plus::return#1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [14] main::$3 = plus::return#1 [ main::i#2 main::$2 main::$3 ] ( [ main::i#2 main::$2 main::$3 ] { } ) always clobbers reg byte a +Statement [15] main::$5 = main::$2 << 1 [ main::i#2 main::$3 main::$5 ] ( [ main::i#2 main::$3 main::$5 ] { } ) always clobbers reg byte a +Statement [16] SCREEN[main::$5] = main::$3 [ main::i#2 ] ( [ main::i#2 ] { } ) always clobbers reg byte a +Statement [19] plus::r#1 = 2 + plus::a#2 [ plus::a#2 plus::b#2 plus::r#1 ] ( plus:5 [ main::i#2 plus::a#2 plus::b#2 plus::r#1 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::a#2 plus::b#2 plus::r#1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [20] plus::r#2 = plus::r#1 + plus::b#2 [ plus::a#2 plus::b#2 plus::r#2 ] ( plus:5 [ main::i#2 plus::a#2 plus::b#2 plus::r#2 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::a#2 plus::b#2 plus::r#2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [21] plus::r#3 = plus::r#2 + plus::a#2 [ plus::b#2 plus::r#3 ] ( plus:5 [ main::i#2 plus::b#2 plus::r#3 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::b#2 plus::r#3 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [22] plus::return#2 = plus::r#3 + plus::b#2 [ plus::return#2 ] ( plus:5 [ main::i#2 plus::return#2 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::return#2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [4] plus::b#0 = (int)main::i#2 [ main::i#2 plus::b#0 ] ( [ main::i#2 plus::b#0 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Statement [5] call plus [ main::i#2 plus::return#2 ] ( [ main::i#2 plus::return#2 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Statement [6] plus::return#0 = plus::return#2 [ main::i#2 plus::return#0 ] ( [ main::i#2 plus::return#0 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a +Statement [7] main::$1 = plus::return#0 [ main::i#2 main::$1 ] ( [ main::i#2 main::$1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [8] main::$4 = main::i#2 << 1 [ main::i#2 main::$1 main::$4 ] ( [ main::i#2 main::$1 main::$4 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [9] SCREEN[main::$4] = main::$1 [ main::i#2 ] ( [ main::i#2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [10] main::$2 = $a + main::i#2 [ main::i#2 main::$2 ] ( [ main::i#2 main::$2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [11] plus::b#1 = (int)main::i#2 [ main::i#2 main::$2 plus::b#1 ] ( [ main::i#2 main::$2 plus::b#1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [12] call plus [ main::i#2 plus::return#2 main::$2 ] ( [ main::i#2 plus::return#2 main::$2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [13] plus::return#1 = plus::return#2 [ main::i#2 main::$2 plus::return#1 ] ( [ main::i#2 main::$2 plus::return#1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [14] main::$3 = plus::return#1 [ main::i#2 main::$2 main::$3 ] ( [ main::i#2 main::$2 main::$3 ] { } ) always clobbers reg byte a +Statement [15] main::$5 = main::$2 << 1 [ main::i#2 main::$3 main::$5 ] ( [ main::i#2 main::$3 main::$5 ] { } ) always clobbers reg byte a +Statement [16] SCREEN[main::$5] = main::$3 [ main::i#2 ] ( [ main::i#2 ] { } ) always clobbers reg byte a +Statement [19] plus::r#1 = 2 + plus::a#2 [ plus::a#2 plus::b#2 plus::r#1 ] ( plus:5 [ main::i#2 plus::a#2 plus::b#2 plus::r#1 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::a#2 plus::b#2 plus::r#1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [20] plus::r#2 = plus::r#1 + plus::b#2 [ plus::a#2 plus::b#2 plus::r#2 ] ( plus:5 [ main::i#2 plus::a#2 plus::b#2 plus::r#2 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::a#2 plus::b#2 plus::r#2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [21] plus::r#3 = plus::r#2 + plus::a#2 [ plus::b#2 plus::r#3 ] ( plus:5 [ main::i#2 plus::b#2 plus::r#3 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::b#2 plus::r#3 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Statement [22] plus::return#2 = plus::r#3 + plus::b#2 [ plus::return#2 ] ( plus:5 [ main::i#2 plus::return#2 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::return#2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a +Potential registers mem[1] [ main::i#2 main::i#1 ] : mem[1] , reg byte x , reg byte y , +Potential registers mem[2] [ plus::a#2 ] : mem[2] , +Potential registers mem[2] [ plus::b#2 plus::b#0 plus::b#1 ] : mem[2] , +Potential registers mem[2] [ plus::return#0 ] : mem[2] , +Potential registers mem[2] [ main::$1 ] : mem[2] , +Potential registers mem[1] [ main::$4 ] : mem[1] , reg byte a , reg byte x , reg byte y , +Potential registers mem[1] [ main::$2 ] : mem[1] , reg byte x , reg byte y , +Potential registers mem[2] [ plus::return#1 ] : mem[2] , +Potential registers mem[2] [ main::$3 ] : mem[2] , +Potential registers mem[1] [ main::$5 ] : mem[1] , reg byte a , reg byte x , reg byte y , +Potential registers mem[2] [ plus::r#1 ] : mem[2] , +Potential registers mem[2] [ plus::r#2 ] : mem[2] , +Potential registers mem[2] [ plus::r#3 ] : mem[2] , +Potential registers mem[2] [ plus::return#2 ] : mem[2] , + +REGISTER UPLIFT SCOPES +Uplift Scope [plus] 202: mem[2] [ plus::r#1 ] 202: mem[2] [ plus::r#2 ] 202: mem[2] [ plus::r#3 ] 100: mem[2] [ plus::b#2 plus::b#0 plus::b#1 ] 67.33: mem[2] [ plus::a#2 ] 30.75: mem[2] [ plus::return#2 ] 22: mem[2] [ plus::return#0 ] 22: mem[2] [ plus::return#1 ] +Uplift Scope [main] 25.67: mem[1] [ main::i#2 main::i#1 ] 22: mem[1] [ main::$4 ] 22: mem[1] [ main::$5 ] 11: mem[2] [ main::$1 ] 11: mem[2] [ main::$3 ] 4.4: mem[1] [ main::$2 ] +Uplift Scope [] + +Uplifting [plus] best 2226 combination mem[2] [ plus::r#1 ] mem[2] [ plus::r#2 ] mem[2] [ plus::r#3 ] mem[2] [ plus::b#2 plus::b#0 plus::b#1 ] mem[2] [ plus::a#2 ] mem[2] [ plus::return#2 ] mem[2] [ plus::return#0 ] mem[2] [ plus::return#1 ] +Uplifting [main] best 1866 combination reg byte y [ main::i#2 main::i#1 ] reg byte x [ main::$4 ] reg byte x [ main::$5 ] mem[2] [ main::$1 ] mem[2] [ main::$3 ] reg byte x [ main::$2 ] +Limited combination testing to 100 combinations of 144 possible. +Uplifting [] best 1866 combination +Coalescing zero page register [ mem[2] [ plus::b#2 plus::b#0 plus::b#1 ] ] with [ mem[2] [ plus::return#2 ] ] - score: 1 +Coalescing zero page register [ mem[2] [ plus::return#0 ] ] with [ mem[2] [ main::$1 ] ] - score: 1 +Coalescing zero page register [ mem[2] [ plus::return#1 ] ] with [ mem[2] [ main::$3 ] ] - score: 1 +Coalescing zero page register [ mem[2] [ plus::r#1 ] ] with [ mem[2] [ plus::r#2 ] ] - score: 1 +Coalescing zero page register [ mem[2] [ plus::b#2 plus::b#0 plus::b#1 plus::return#2 ] ] with [ mem[2] [ plus::return#0 main::$1 ] ] - score: 1 +Coalescing zero page register [ mem[2] [ plus::b#2 plus::b#0 plus::b#1 plus::return#2 plus::return#0 main::$1 ] ] with [ mem[2] [ plus::return#1 main::$3 ] ] - score: 1 +Coalescing zero page register [ mem[2] [ plus::r#1 plus::r#2 ] ] with [ mem[2] [ plus::r#3 ] ] - score: 1 + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +/** + * Test banked calls with memory variables. + * The parameters & return should end up in the shared/common bank. + */ + // Upstart + .file [name="call-banked-phi-memvars.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // [1] phi from main to main::@1 [phi:main->main::@1] + __b1_from_main: + // [1] phi main::i#2 = 0 [phi:main->main::@1#0] -- vbuyy=vbuc1 + ldy #0 + jmp __b1 + // main::@1 + __b1: + // [2] if(main::i#2<5) goto main::@2 -- vbuyy_lt_vbuc1_then_la1 + cpy #5 + bcc __b2 + jmp __breturn + // main::@return + __breturn: + // [3] return + rts + // main::@2 + __b2: + // [4] plus::b#0 = (int)main::i#2 -- vwsm1=_sword_vbuyy + tya + sta plus.b + lda #0 + sta plus.b+1 + // [5] call plus + // [18] phi from main::@2 to plus [phi:main::@2->plus] + plus_from___b2: + // [18] phi plus::b#2 = plus::b#0 [phi:main::@2->plus#0] -- register_copy + // [18] phi plus::a#2 = $64 [phi:main::@2->plus#1] -- call_phi_close_cx16_ram + lda #<$64 + sta plus.a + lda #>$64 + sta plus.a+1 + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // [6] plus::return#0 = plus::return#2 + jmp __b3 + // main::@3 + __b3: + // [7] main::$1 = plus::return#0 + // [8] main::$4 = main::i#2 << 1 -- vbuxx=vbuyy_rol_1 + tya + asl + tax + // [9] SCREEN[main::$4] = main::$1 -- pwsc1_derefidx_vbuxx=vwsm1 + lda __1 + sta SCREEN,x + lda __1+1 + sta SCREEN+1,x + // [10] main::$2 = $a + main::i#2 -- vbuxx=vbuc1_plus_vbuyy + tya + tax + axs #-[$a] + // [11] plus::b#1 = (int)main::i#2 -- vwsm1=_sword_vbuyy + tya + sta plus.b + lda #0 + sta plus.b+1 + // [12] call plus + // [18] phi from main::@3 to plus [phi:main::@3->plus] + plus_from___b3: + // [18] phi plus::b#2 = plus::b#1 [phi:main::@3->plus#0] -- register_copy + // [18] phi plus::a#2 = $c8 [phi:main::@3->plus#1] -- call_phi_close_cx16_ram + lda #<$c8 + sta plus.a + lda #>$c8 + sta plus.a+1 + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // [13] plus::return#1 = plus::return#2 + jmp __b4 + // main::@4 + __b4: + // [14] main::$3 = plus::return#1 + // [15] main::$5 = main::$2 << 1 -- vbuxx=vbuxx_rol_1 + txa + asl + tax + // [16] SCREEN[main::$5] = main::$3 -- pwsc1_derefidx_vbuxx=vwsm1 + lda __3 + sta SCREEN,x + lda __3+1 + sta SCREEN+1,x + // [17] main::i#1 = ++ main::i#2 -- vbuyy=_inc_vbuyy + iny + // [1] phi from main::@4 to main::@1 [phi:main::@4->main::@1] + __b1_from___b4: + // [1] phi main::i#2 = main::i#1 [phi:main::@4->main::@1#0] -- register_copy + jmp __b1 + .segment Data + .label __1 = plus.b + .label __3 = plus.b +} +.segment RAM_Bank1 + // plus +// __mem() int plus(__mem() int a, __mem() int b) +// __bank(cx16_ram, 1) +plus: { + // [19] plus::r#1 = 2 + plus::a#2 -- vwsm1=vwsc1_plus_vwsm2 + clc + lda a + adc #<2 + sta r + lda a+1 + adc #>2 + sta r+1 + // [20] plus::r#2 = plus::r#1 + plus::b#2 -- vwsm1=vwsm1_plus_vwsm2 + clc + lda r + adc b + sta r + lda r+1 + adc b+1 + sta r+1 + // [21] plus::r#3 = plus::r#2 + plus::a#2 -- vwsm1=vwsm1_plus_vwsm2 + clc + lda r + adc a + sta r + lda r+1 + adc a+1 + sta r+1 + // [22] plus::return#2 = plus::r#3 + plus::b#2 -- vwsm1=vwsm2_plus_vwsm1 + clc + lda return + adc r + sta return + lda return+1 + adc r+1 + sta return+1 + jmp __breturn + // plus::@return + __breturn: + // [23] return + rts + .segment Data + b: .word 0 + .label return = b + .segment RAM_Bank1 + r: .word 0 + .segment Data + a: .word 0 +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Removing instruction jmp __b3 +Removing instruction jmp __b4 +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __b1_from_main: +Removing instruction __breturn: +Removing instruction plus_from___b2: +Removing instruction __b3: +Removing instruction plus_from___b3: +Removing instruction __b4: +Removing instruction __b1_from___b4: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant int * const SCREEN = (int *) 1024 +void main() +int main::$1 // mem[2] 11.0 +char main::$2 // reg byte x 4.4 +int main::$3 // mem[2] 11.0 +char main::$4 // reg byte x 22.0 +char main::$5 // reg byte x 22.0 +char main::i +char main::i#1 // reg byte y 22.0 +char main::i#2 // reg byte y 3.6666666666666665 +__bank(cx16_ram, 1) int plus(int a , int b) +int plus::a +int plus::a#2 // a mem[2] 67.33333333333333 +int plus::b +int plus::b#0 // b mem[2] 22.0 +int plus::b#1 // b mem[2] 22.0 +int plus::b#2 // b mem[2] 56.0 +int plus::r +int plus::r#1 // r mem[2] 202.0 +int plus::r#2 // r mem[2] 202.0 +int plus::r#3 // r mem[2] 202.0 +int plus::return +int plus::return#0 // return mem[2] 22.0 +int plus::return#1 // return mem[2] 22.0 +int plus::return#2 // return mem[2] 30.75 + +reg byte y [ main::i#2 main::i#1 ] +mem[2] [ plus::a#2 ] +mem[2] [ plus::b#2 plus::b#0 plus::b#1 plus::return#2 plus::return#0 main::$1 plus::return#1 main::$3 ] +reg byte x [ main::$4 ] +reg byte x [ main::$2 ] +reg byte x [ main::$5 ] +mem[2] [ plus::r#1 plus::r#2 plus::r#3 ] + + +FINAL ASSEMBLER +Score: 1103 + + // File Comments +/** + * Test banked calls with memory variables. + * The parameters & return should end up in the shared/common bank. + */ + // Upstart + .file [name="call-banked-phi-memvars.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100] +.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100] +.segment Basic +:BasicUpstart(main) +.segment Code +.segment Data + + + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // [1] phi from main to main::@1 [phi:main->main::@1] + // [1] phi main::i#2 = 0 [phi:main->main::@1#0] -- vbuyy=vbuc1 + ldy #0 + // main::@1 + __b1: + // for(char i=0;i<5; i++) + // [2] if(main::i#2<5) goto main::@2 -- vbuyy_lt_vbuc1_then_la1 + cpy #5 + bcc __b2 + // main::@return + // } + // [3] return + rts + // main::@2 + __b2: + // plus(100, (int)i) + // [4] plus::b#0 = (int)main::i#2 -- vwsm1=_sword_vbuyy + tya + sta plus.b + lda #0 + sta plus.b+1 + // [5] call plus + // [18] phi from main::@2 to plus [phi:main::@2->plus] + // [18] phi plus::b#2 = plus::b#0 [phi:main::@2->plus#0] -- register_copy + // [18] phi plus::a#2 = $64 [phi:main::@2->plus#1] -- call_phi_close_cx16_ram + lda #<$64 + sta plus.a + lda #>$64 + sta plus.a+1 + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus(100, (int)i) + // [6] plus::return#0 = plus::return#2 + // main::@3 + // [7] main::$1 = plus::return#0 + // SCREEN[i] = plus(100, (int)i) + // [8] main::$4 = main::i#2 << 1 -- vbuxx=vbuyy_rol_1 + tya + asl + tax + // [9] SCREEN[main::$4] = main::$1 -- pwsc1_derefidx_vbuxx=vwsm1 + lda __1 + sta SCREEN,x + lda __1+1 + sta SCREEN+1,x + // 10+i + // [10] main::$2 = $a + main::i#2 -- vbuxx=vbuc1_plus_vbuyy + tya + tax + axs #-[$a] + // plus(200, (int)i) + // [11] plus::b#1 = (int)main::i#2 -- vwsm1=_sword_vbuyy + tya + sta plus.b + lda #0 + sta plus.b+1 + // [12] call plus + // [18] phi from main::@3 to plus [phi:main::@3->plus] + // [18] phi plus::b#2 = plus::b#1 [phi:main::@3->plus#0] -- register_copy + // [18] phi plus::a#2 = $c8 [phi:main::@3->plus#1] -- call_phi_close_cx16_ram + lda #<$c8 + sta plus.a + lda #>$c8 + sta plus.a+1 + sta.z $ff + lda.z 0 + pha + lda #1 + sta.z 0 + lda.z $ff + jsr plus + sta.z $ff + pla + sta.z 0 + lda.z $ff + // plus(200, (int)i) + // [13] plus::return#1 = plus::return#2 + // main::@4 + // [14] main::$3 = plus::return#1 + // SCREEN[10+i] = plus(200, (int)i) + // [15] main::$5 = main::$2 << 1 -- vbuxx=vbuxx_rol_1 + txa + asl + tax + // [16] SCREEN[main::$5] = main::$3 -- pwsc1_derefidx_vbuxx=vwsm1 + lda __3 + sta SCREEN,x + lda __3+1 + sta SCREEN+1,x + // for(char i=0;i<5; i++) + // [17] main::i#1 = ++ main::i#2 -- vbuyy=_inc_vbuyy + iny + // [1] phi from main::@4 to main::@1 [phi:main::@4->main::@1] + // [1] phi main::i#2 = main::i#1 [phi:main::@4->main::@1#0] -- register_copy + jmp __b1 + .segment Data + .label __1 = plus.b + .label __3 = plus.b +} +.segment RAM_Bank1 + // plus +// __mem() int plus(__mem() int a, __mem() int b) +// __bank(cx16_ram, 1) +plus: { + // r += a + // [19] plus::r#1 = 2 + plus::a#2 -- vwsm1=vwsc1_plus_vwsm2 + clc + lda a + adc #<2 + sta r + lda a+1 + adc #>2 + sta r+1 + // r += b + // [20] plus::r#2 = plus::r#1 + plus::b#2 -- vwsm1=vwsm1_plus_vwsm2 + clc + lda r + adc b + sta r + lda r+1 + adc b+1 + sta r+1 + // r += a + // [21] plus::r#3 = plus::r#2 + plus::a#2 -- vwsm1=vwsm1_plus_vwsm2 + clc + lda r + adc a + sta r + lda r+1 + adc a+1 + sta r+1 + // r += b + // [22] plus::return#2 = plus::r#3 + plus::b#2 -- vwsm1=vwsm2_plus_vwsm1 + clc + lda return + adc r + sta return + lda return+1 + adc r+1 + sta return+1 + // plus::@return + // } + // [23] return + rts + .segment Data + b: .word 0 + .label return = b + .segment RAM_Bank1 + r: .word 0 + .segment Data + a: .word 0 +} + // File Data + diff --git a/src/test/ref/call-banked-phi-memvars.sym b/src/test/ref/call-banked-phi-memvars.sym new file mode 100644 index 000000000..4300fc0bd --- /dev/null +++ b/src/test/ref/call-banked-phi-memvars.sym @@ -0,0 +1,33 @@ +__constant int * const SCREEN = (int *) 1024 +void main() +int main::$1 // mem[2] 11.0 +char main::$2 // reg byte x 4.4 +int main::$3 // mem[2] 11.0 +char main::$4 // reg byte x 22.0 +char main::$5 // reg byte x 22.0 +char main::i +char main::i#1 // reg byte y 22.0 +char main::i#2 // reg byte y 3.6666666666666665 +__bank(cx16_ram, 1) int plus(int a , int b) +int plus::a +int plus::a#2 // a mem[2] 67.33333333333333 +int plus::b +int plus::b#0 // b mem[2] 22.0 +int plus::b#1 // b mem[2] 22.0 +int plus::b#2 // b mem[2] 56.0 +int plus::r +int plus::r#1 // r mem[2] 202.0 +int plus::r#2 // r mem[2] 202.0 +int plus::r#3 // r mem[2] 202.0 +int plus::return +int plus::return#0 // return mem[2] 22.0 +int plus::return#1 // return mem[2] 22.0 +int plus::return#2 // return mem[2] 30.75 + +reg byte y [ main::i#2 main::i#1 ] +mem[2] [ plus::a#2 ] +mem[2] [ plus::b#2 plus::b#0 plus::b#1 plus::return#2 plus::return#0 main::$1 plus::return#1 main::$3 ] +reg byte x [ main::$4 ] +reg byte x [ main::$2 ] +reg byte x [ main::$5 ] +mem[2] [ plus::r#1 plus::r#2 plus::r#3 ] diff --git a/src/test/ref/inline-asm-ref-scoped.log b/src/test/ref/inline-asm-ref-scoped.log index 0fe922e7f..87605b4e1 100644 --- a/src/test/ref/inline-asm-ref-scoped.log +++ b/src/test/ref/inline-asm-ref-scoped.log @@ -4,7 +4,7 @@ CONTROL FLOW GRAPH SSA void main() main: scope:[main] from __start asm { lda#'c' stasub.ll+1 } - call sub + call sub to:main::@1 main::@1: scope:[main] from main to:main::@return @@ -22,7 +22,7 @@ sub::@return: scope:[sub] from sub void __start() __start: scope:[__start] from - call main + call main to:__start::@1 __start::@1: scope:[__start] from __start to:__start::@return @@ -53,7 +53,7 @@ FINAL CONTROL FLOW GRAPH void main() main: scope:[main] from asm { lda#'c' stasub.ll+1 } - [1] call sub + [1] call sub to:main::@return main::@return: scope:[main] from main [2] return @@ -106,7 +106,7 @@ main: { // asm { lda#'c' stasub.ll+1 } lda #'c' sta sub.ll+1 - // [1] call sub + // [1] call sub jsr sub jmp __breturn // main::@return @@ -165,7 +165,7 @@ main: { lda #'c' sta sub.ll+1 // sub() - // [1] call sub + // [1] call sub jsr sub // main::@return // } diff --git a/src/test/ref/pragma-noparam-noparen.log b/src/test/ref/pragma-noparam-noparen.log index 68ccb3b9d..ec255c1a4 100644 --- a/src/test/ref/pragma-noparam-noparen.log +++ b/src/test/ref/pragma-noparam-noparen.log @@ -1,4 +1,3 @@ -Warning! Unknown #pragma nobank CONTROL FLOW GRAPH SSA