From 9ccc5d828f409a8198fdbbd79243ea3bba8ce77b Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Fri, 5 Apr 2019 01:05:15 +0200 Subject: [PATCH] Improved pointer-to-pointer functionality significantly. --- src/main/fragment/_deref_pptc1=pbuc2.asm | 4 + src/main/fragment/_deref_pptz1=pbuc1.asm | 6 + src/main/fragment/pptz1=pptc1.asm | 4 + .../fragment/vbuaa=_deref__deref_pptc1.asm | 2 + .../java/dk/camelot64/kickc/CompileLog.java | 21 + .../passes/Pass2ConstantIdentification.java | 169 ++-- .../kickc/passes/Pass4CodeGeneration.java | 23 +- ...ass4RegisterUpliftPotentialInitialize.java | 19 +- .../dk/camelot64/kickc/test/TestPrograms.java | 16 +- src/test/kc/pointer-pointer-1.kc | 11 + src/test/kc/pointer-pointer-2.kc | 30 + src/test/ref/pointer-pointer-1.asm | 20 + src/test/ref/pointer-pointer-1.cfg | 17 + src/test/ref/pointer-pointer-1.log | 286 ++++++ src/test/ref/pointer-pointer-1.sym | 16 + src/test/ref/pointer-pointer-2.asm | 65 ++ src/test/ref/pointer-pointer-2.cfg | 50 + src/test/ref/pointer-pointer-2.log | 866 ++++++++++++++++++ src/test/ref/pointer-pointer-2.sym | 40 + src/test/ref/test-address-of-param.log | 16 +- 20 files changed, 1575 insertions(+), 106 deletions(-) create mode 100644 src/main/fragment/_deref_pptc1=pbuc2.asm create mode 100644 src/main/fragment/_deref_pptz1=pbuc1.asm create mode 100644 src/main/fragment/pptz1=pptc1.asm create mode 100644 src/main/fragment/vbuaa=_deref__deref_pptc1.asm create mode 100644 src/test/kc/pointer-pointer-1.kc create mode 100644 src/test/kc/pointer-pointer-2.kc create mode 100644 src/test/ref/pointer-pointer-1.asm create mode 100644 src/test/ref/pointer-pointer-1.cfg create mode 100644 src/test/ref/pointer-pointer-1.log create mode 100644 src/test/ref/pointer-pointer-1.sym create mode 100644 src/test/ref/pointer-pointer-2.asm create mode 100644 src/test/ref/pointer-pointer-2.cfg create mode 100644 src/test/ref/pointer-pointer-2.log create mode 100644 src/test/ref/pointer-pointer-2.sym diff --git a/src/main/fragment/_deref_pptc1=pbuc2.asm b/src/main/fragment/_deref_pptc1=pbuc2.asm new file mode 100644 index 000000000..e8b915e3e --- /dev/null +++ b/src/main/fragment/_deref_pptc1=pbuc2.asm @@ -0,0 +1,4 @@ +lda #<{c2} +sta {c1} +lda #>{c2} +sta {c1}+1 \ No newline at end of file diff --git a/src/main/fragment/_deref_pptz1=pbuc1.asm b/src/main/fragment/_deref_pptz1=pbuc1.asm new file mode 100644 index 000000000..b687a9d5a --- /dev/null +++ b/src/main/fragment/_deref_pptz1=pbuc1.asm @@ -0,0 +1,6 @@ +ldy #0 +lda #<{c1} +sta ({z1}),y +iny +lda #>{c1} +sta ({z1}),y \ No newline at end of file diff --git a/src/main/fragment/pptz1=pptc1.asm b/src/main/fragment/pptz1=pptc1.asm new file mode 100644 index 000000000..4c71afd48 --- /dev/null +++ b/src/main/fragment/pptz1=pptc1.asm @@ -0,0 +1,4 @@ +lda #<{c1} +sta {z1} +lda #>{c1} +sta {z1}+1 \ No newline at end of file diff --git a/src/main/fragment/vbuaa=_deref__deref_pptc1.asm b/src/main/fragment/vbuaa=_deref__deref_pptc1.asm new file mode 100644 index 000000000..b2b71be97 --- /dev/null +++ b/src/main/fragment/vbuaa=_deref__deref_pptc1.asm @@ -0,0 +1,2 @@ +ldy #0 +lda ({c1}),y diff --git a/src/main/java/dk/camelot64/kickc/CompileLog.java b/src/main/java/dk/camelot64/kickc/CompileLog.java index 5f6ec3768..8b0276a2d 100644 --- a/src/main/java/dk/camelot64/kickc/CompileLog.java +++ b/src/main/java/dk/camelot64/kickc/CompileLog.java @@ -123,10 +123,20 @@ public class CompileLog { this.verboseSequencePlan = verboseSequencePlan; } + public CompileLog verboseParse() { + setVerboseParse(true); + return this; + } + public void setVerboseParse(boolean verboseParse) { this.verboseParse = verboseParse; } + public CompileLog verboseCreateSsa() { + setVerboseCreateSsa(true); + return this; + } + public void setVerboseCreateSsa(boolean verboseCreateSsa) { this.verboseCreateSsa = verboseCreateSsa; } @@ -135,6 +145,11 @@ public class CompileLog { return verboseUplift; } + public CompileLog verboseUplift() { + setVerboseUplift(true); + return this; + } + public void setVerboseUplift(boolean verboseUplift) { this.verboseUplift = verboseUplift; } @@ -167,6 +182,12 @@ public class CompileLog { return verboseSSAOptimize; } + public CompileLog setVerboseSSAOptimize() { + setVerboseSSAOptimize(true); + return this; + } + + public void setVerboseSSAOptimize(boolean verboseSSAOptimize) { this.verboseSSAOptimize = verboseSSAOptimize; } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java index 2d983582e..38055da94 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java @@ -45,13 +45,26 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization { for(VariableRef constRef : constVars) { Variable variable = getProgram().getScope().getVariable(constRef); + ConstantVariableValue constVarVal = constants.get(constRef); + // Weed out all variables that are affected by the address-of operator if(isAddressOfUsed(constRef, getProgram())) { + + // If the assignment has an operator then replace it with the single constant value + if(constVarVal.getAssignment() instanceof StatementAssignment) { + StatementAssignment assignment = (StatementAssignment) constVarVal.getAssignment(); + if(assignment.getOperator()!=null) { + getLog().append("Constant right-side identified " + assignment.toString(getProgram(), false)); + assignment.setOperator(null); + assignment.setrValue1(null); + assignment.setrValue2(constVarVal.getConstantValue()); + } + } + + // But do not remove the variable constants.remove(constRef); continue; } - - ConstantVariableValue constVarVal = constants.get(constRef); Scope constScope = variable.getScope(); ConstantValue constVal = constVarVal.getConstantValue(); @@ -80,7 +93,7 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization { constVal); constantVar.setDeclaredAlignment(variable.getDeclaredAlignment()); constantVar.setDeclaredRegister(variable.getDeclaredRegister()); - if(variable.getComments().size()>0) { + if(variable.getComments().size() > 0) { constantVar.setComments(variable.getComments()); } else { constantVar.setComments(constVarVal.getAssignment().getComments()); @@ -111,7 +124,7 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization { /** * The statement that assigns the variable its value (the assignment will be removed at the end). * Either a {@link StatementAssignment} or a {@link StatementPhiBlock}. - * */ + */ private Statement assignment; public ConstantVariableValue(VariableRef variableRef, ConstantValue constantValue, Statement assignment) { @@ -182,83 +195,85 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization { // Volatile variables cannot be constant return; } - if(assignment.getrValue1() == null && getConstant(assignment.getrValue2()) != null) { - if(assignment.getOperator() == null) { - // Constant assignment - ConstantValue constant = getConstant(assignment.getrValue2()); - if(constant != null) { - constants.put(variable, new ConstantVariableValue(variable, constant, assignment)); - } - } else { - // Constant unary expression - ConstantValue constant = createUnary( - (OperatorUnary) assignment.getOperator(), - getConstant(assignment.getrValue2()) - ); - if(constant != null) { - constants.put(variable, new ConstantVariableValue(variable, constant, assignment)); - } - } - } else if(getConstant(assignment.getrValue1()) != null && getConstant(assignment.getrValue2()) != null) { - // Constant binary expression - ConstantValue constant = createBinary( - getConstant(assignment.getrValue1()), - (OperatorBinary) assignment.getOperator(), - getConstant(assignment.getrValue2()), - getScope()); - if(constant != null) { - constants.put(variable, new ConstantVariableValue(variable, constant, assignment)); - } - } else if(assignment.getrValue2() instanceof ValueList && assignment.getOperator() == null && assignment.getrValue1() == null) { - // A candidate for a constant list - examine to confirm - Variable lVariable = getScope().getVariable((VariableRef) lValue); - if(lVariable.getType() instanceof SymbolTypeArray) { - ValueList valueList = (ValueList) assignment.getrValue2(); - List values = valueList.getList(); - boolean allConstant = true; - // Type of the elements of the list (deducted from the type of all elements) - SymbolType listType = null; - List elements = new ArrayList<>(); - for(RValue elmValue : values) { - if(elmValue instanceof ConstantValue) { - ConstantValue constantValue = (ConstantValue) elmValue; - SymbolType elmType = constantValue.getType(getScope()); - if(listType == null) { - listType = elmType; - } else { - if(!SymbolTypeInference.typeMatch(listType, elmType)) { - SymbolType intersectType = SymbolTypeInference.intersectTypes(listType, elmType); - if(intersectType == null) { - // No overlap between list type and element type - throw new RuntimeException("Array type " + listType + " does not match element type" + elmType + ". Array: " + valueList.toString(getProgram())); - } else { - listType = intersectType; - } - } - } - elements.add(constantValue); - } else { - allConstant = false; - listType = null; - break; - } - } - if(allConstant && listType != null) { - // Constant list confirmed! - ConstantValue constant = new ConstantArrayList(elements, listType); - constants.put(variable, new ConstantVariableValue(variable, constant, assignment)); - } - } - } else if(Operators.ADDRESS_OF.equals(assignment.getOperator()) && assignment.getrValue1() == null) { - // Constant address-of variable - if(assignment.getrValue2() instanceof SymbolRef) { - ConstantSymbolPointer constantSymbolPointer = new ConstantSymbolPointer((SymbolRef) assignment.getrValue2()); - constants.put(variable, new ConstantVariableValue(variable, constantSymbolPointer, assignment)); - } + ConstantValue constant = getConstantAssignmentValue(assignment, var.getType()); + if(constant != null) { + constants.put(variable, new ConstantVariableValue(variable, constant, assignment)); } } } + /** + * Examine the right side of an assignment and if it is constant then return the constant value. + * @param assignment The assignment to examine + * @param lValueType The type of the lvalue + * @return The constant value if the right side is constant + */ + private ConstantValue getConstantAssignmentValue(StatementAssignment assignment, SymbolType lValueType) { + if(assignment.getrValue1() == null && getConstant(assignment.getrValue2()) != null) { + if(assignment.getOperator() == null) { + // Constant assignment + return getConstant(assignment.getrValue2()); + } else { + // Constant unary expression + return createUnary( + (OperatorUnary) assignment.getOperator(), + getConstant(assignment.getrValue2()) + ); + } + } else if(getConstant(assignment.getrValue1()) != null && getConstant(assignment.getrValue2()) != null) { + // Constant binary expression + return createBinary( + getConstant(assignment.getrValue1()), + (OperatorBinary) assignment.getOperator(), + getConstant(assignment.getrValue2()), + getScope()); + } else if(assignment.getrValue2() instanceof ValueList && assignment.getOperator() == null && assignment.getrValue1() == null) { + // A candidate for a constant list - examine to confirm + if(lValueType instanceof SymbolTypeArray) { + ValueList valueList = (ValueList) assignment.getrValue2(); + List values = valueList.getList(); + boolean allConstant = true; + // Type of the elements of the list (deducted from the type of all elements) + SymbolType listType = null; + List elements = new ArrayList<>(); + for(RValue elmValue : values) { + if(elmValue instanceof ConstantValue) { + ConstantValue constantValue = (ConstantValue) elmValue; + SymbolType elmType = constantValue.getType(getScope()); + if(listType == null) { + listType = elmType; + } else { + if(!SymbolTypeInference.typeMatch(listType, elmType)) { + SymbolType intersectType = SymbolTypeInference.intersectTypes(listType, elmType); + if(intersectType == null) { + // No overlap between list type and element type + throw new RuntimeException("Array type " + listType + " does not match element type" + elmType + ". Array: " + valueList.toString(getProgram())); + } else { + listType = intersectType; + } + } + } + elements.add(constantValue); + } else { + allConstant = false; + listType = null; + break; + } + } + if(allConstant && listType != null) { + // Constant list confirmed! + return new ConstantArrayList(elements, listType); + } + } + } else if(Operators.ADDRESS_OF.equals(assignment.getOperator()) && assignment.getrValue1() == null) { + // Constant address-of variable + if(assignment.getrValue2() instanceof SymbolRef) { + return new ConstantSymbolPointer((SymbolRef) assignment.getrValue2()); + } + } + return null; + } + /** * If the rValue is a known constant return the constant value. * diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index 4b468a027..429cc7f7f 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -154,6 +154,7 @@ public class Pass4CodeGeneration { */ private void generateScopeEnding(AsmProgram asm, ScopeRef currentScope) { if(!ScopeRef.ROOT.equals(currentScope)) { + // Generate any indirect calls pending for(String indirectCallAsmName : indirectCallAsmNames) { asm.addLabel("bi_"+indirectCallAsmName); asm.addInstruction("jmp", AsmAddressingMode.IND, indirectCallAsmName, false); @@ -164,6 +165,20 @@ public class Pass4CodeGeneration { } } + /** + * Add an indirect call to the assembler program. Also queues ASM for the indirect jump to be added at the end of the block. + * @param asm The ASM program being built + * @param procedureVariable The variable containing the function pointer + * @param codeScopeRef The scope containing the code being generated. Used for adding scope to the name when needed (eg. line.x1 when referencing x1 variable inside line scope from outside line scope). + */ + private void generateIndirectCall(AsmProgram asm, Variable procedureVariable, ScopeRef codeScopeRef) { + String varAsmName = AsmFormat.getAsmParamName(procedureVariable, codeScopeRef); + indirectCallAsmNames.add(varAsmName); + asm.addInstruction("jsr", AsmAddressingMode.ABS, "bi_" + varAsmName, false); + } + + + /** * Generate a comment that describes the procedure signature and parameter transfer * @param asm The assembler program being generated @@ -601,9 +616,7 @@ public class Pass4CodeGeneration { supported = true; } else if(pointer instanceof VariableRef) { Variable variable = getScope().getVariable((VariableRef) pointer); - String varAsmName = AsmFormat.getAsmParamName(variable, block.getScope()); - indirectCallAsmNames.add(varAsmName); - asm.addInstruction("jsr", AsmAddressingMode.ABS, "bi_"+varAsmName,false); + generateIndirectCall(asm, variable, block.getScope()); supported = true; } } else if(procedure instanceof VariableRef) { @@ -611,9 +624,7 @@ public class Pass4CodeGeneration { SymbolType procedureVariableType = procedureVariable.getType(); if(procedureVariableType instanceof SymbolTypePointer) { if(((SymbolTypePointer) procedureVariableType).getElementType() instanceof SymbolTypeProcedure) { - String varAsmName = AsmFormat.getAsmParamName(procedureVariable, block.getScope()); - indirectCallAsmNames.add(varAsmName); - asm.addInstruction("jsr", AsmAddressingMode.ABS, "bi_"+varAsmName,false); + generateIndirectCall(asm, procedureVariable, block.getScope()); supported = true; } } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialInitialize.java b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialInitialize.java index 115f3637f..a99167211 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialInitialize.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialInitialize.java @@ -1,10 +1,7 @@ package dk.camelot64.kickc.passes; import dk.camelot64.kickc.model.*; -import dk.camelot64.kickc.model.values.ConstantSymbolPointer; -import dk.camelot64.kickc.model.values.SymbolRef; import dk.camelot64.kickc.model.values.VariableRef; -import dk.camelot64.kickc.model.symbols.ConstantVar; import dk.camelot64.kickc.model.symbols.Variable; import java.util.ArrayList; @@ -49,12 +46,12 @@ public class Pass4RegisterUpliftPotentialInitialize extends Pass2Base { Registers.RegisterType registerType = defaultRegister.getType(); List potentials = new ArrayList<>(); potentials.add(defaultRegister); - if(registerType.equals(Registers.RegisterType.ZP_BYTE) && !varRefExtracted(equivalenceClass) &&!varVolatile(equivalenceClass)) { + if(registerType.equals(Registers.RegisterType.ZP_BYTE) && !isAddressOfUsed(equivalenceClass) &&!varVolatile(equivalenceClass)) { potentials.add(Registers.getRegisterA()); potentials.add(Registers.getRegisterX()); potentials.add(Registers.getRegisterY()); } - if(registerType.equals(Registers.RegisterType.ZP_BOOL) && !varRefExtracted(equivalenceClass) &&!varVolatile(equivalenceClass)) { + if(registerType.equals(Registers.RegisterType.ZP_BOOL) && !isAddressOfUsed(equivalenceClass) &&!varVolatile(equivalenceClass)) { potentials.add(Registers.getRegisterA()); } registerPotentials.setPotentialRegisters(equivalenceClass, potentials); @@ -83,14 +80,10 @@ public class Pass4RegisterUpliftPotentialInitialize extends Pass2Base { * @param equivalenceClass The equivalence class * @return true if a variable reference is extracted */ - private boolean varRefExtracted(LiveRangeEquivalenceClass equivalenceClass) { - Collection allConstants = getProgram().getScope().getAllConstants(true); - for(ConstantVar allConstant : allConstants) { - if(allConstant.getValue() instanceof ConstantSymbolPointer) { - SymbolRef toSym = ((ConstantSymbolPointer) allConstant.getValue()).getToSymbol(); - if(equivalenceClass.getVariables().contains(toSym)) { - return true; - } + private boolean isAddressOfUsed(LiveRangeEquivalenceClass equivalenceClass) { + for(VariableRef variableRef : equivalenceClass.getVariables()) { + if(Pass2ConstantIdentification.isAddressOfUsed(variableRef, getProgram())) { + return true; } } return false; diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 503f6914e..a6ab00add 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -32,6 +32,16 @@ public class TestPrograms { public TestPrograms() { } + @Test + public void testPointerPointer2() throws IOException, URISyntaxException { + compileAndCompare("pointer-pointer-2"); + } + + @Test + public void testPointerPointer1() throws IOException, URISyntaxException { + compileAndCompare("pointer-pointer-1" ); + } + @Test public void testFunctionPointerNoargCall6() throws IOException, URISyntaxException { compileAndCompare("function-pointer-noarg-call-6"); @@ -109,7 +119,7 @@ public class TestPrograms { @Test public void testLocalScopeLoops() throws IOException, URISyntaxException { - compileAndCompare("localscope-loops", getLogSysout()); + compileAndCompare("localscope-loops"); } @Test @@ -1470,11 +1480,11 @@ public class TestPrograms { @AfterClass public static void tearDown() { - CompileLog log = getLogSysout(); + CompileLog log = log(); AsmFragmentTemplateUsages.logUsages(log, false, false, false, false, false, false); } - private static CompileLog getLogSysout() { + private static CompileLog log() { CompileLog log = new CompileLog(); log.setSysOut(true); return log; diff --git a/src/test/kc/pointer-pointer-1.kc b/src/test/kc/pointer-pointer-1.kc new file mode 100644 index 000000000..e260f3902 --- /dev/null +++ b/src/test/kc/pointer-pointer-1.kc @@ -0,0 +1,11 @@ +// Tests a simple pointer to a pointer + +void main() { + const byte* SCREEN = $400; + + byte b = 'a'; + byte* pb = &b; + byte** ppb = &pb; + *SCREEN = **ppb; + +} \ No newline at end of file diff --git a/src/test/kc/pointer-pointer-2.kc b/src/test/kc/pointer-pointer-2.kc new file mode 100644 index 000000000..9559953fd --- /dev/null +++ b/src/test/kc/pointer-pointer-2.kc @@ -0,0 +1,30 @@ +// Tests pointer to pointer in a more complex setup + +void main() { + + byte* screen = $400; + + byte* text; + for(byte i: 0..20) { + nexttext(&text); + while(*text!='@') { + *screen++ = *text++; + } + } + +} + +byte[] text1 = "camelot @"; +byte[] text2 = "rex @"; + +byte textid = 0; + +// Choose the next text to show - by updating the text pointer pointed to by the passed pointer to a pointer +void nexttext(byte** textp) { + if((textid++&1)==0) { + *textp = text1; + } else { + *textp = text2; + } +} + diff --git a/src/test/ref/pointer-pointer-1.asm b/src/test/ref/pointer-pointer-1.asm new file mode 100644 index 000000000..b0b95c7bb --- /dev/null +++ b/src/test/ref/pointer-pointer-1.asm @@ -0,0 +1,20 @@ +// Tests a simple pointer to a pointer +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +main: { + .label SCREEN = $400 + .label ppb = pb + .label b = 2 + .label pb = 3 + lda #'a' + sta b + lda #b + sta pb+1 + ldy #0 + lda (ppb),y + sta SCREEN + rts +} diff --git a/src/test/ref/pointer-pointer-1.cfg b/src/test/ref/pointer-pointer-1.cfg new file mode 100644 index 000000000..f66c6d203 --- /dev/null +++ b/src/test/ref/pointer-pointer-1.cfg @@ -0,0 +1,17 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] (byte) main::b#0 ← (byte) 'a' + [5] (byte*) main::pb#0 ← &(byte) main::b#0 + [6] *((const byte*) main::SCREEN#0) ← *(*((const byte**) main::ppb#0)) + to:main::@return +main::@return: scope:[main] from main + [7] return + to:@return diff --git a/src/test/ref/pointer-pointer-1.log b/src/test/ref/pointer-pointer-1.log new file mode 100644 index 000000000..a040e19a1 --- /dev/null +++ b/src/test/ref/pointer-pointer-1.log @@ -0,0 +1,286 @@ + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + to:@1 +main: scope:[main] from @1 + (byte*) main::SCREEN#0 ← ((byte*)) (word/signed word/dword/signed dword) $400 + (byte) main::b#0 ← (byte) 'a' + (byte*~) main::$0 ← & (byte) main::b#0 + (byte*) main::pb#0 ← (byte*~) main::$0 + (byte**~) main::$1 ← & (byte*) main::pb#0 + (byte**) main::ppb#0 ← (byte**~) main::$1 + *((byte*) main::SCREEN#0) ← *(*((byte**) main::ppb#0)) + to:main::@return +main::@return: scope:[main] from main + return + to:@return +@1: scope:[] from @begin + call main + to:@2 +@2: scope:[] from @1 + to:@end +@end: scope:[] from @2 + +SYMBOL TABLE SSA +(label) @1 +(label) @2 +(label) @begin +(label) @end +(void()) main() +(byte*~) main::$0 +(byte**~) main::$1 +(label) main::@return +(byte*) main::SCREEN +(byte*) main::SCREEN#0 +(byte) main::b +(byte) main::b#0 +(byte*) main::pb +(byte*) main::pb#0 +(byte**) main::ppb +(byte**) main::ppb#0 + +Culled Empty Block (label) @2 +Successful SSA optimization Pass2CullEmptyBlocks +Alias (byte*) main::pb#0 = (byte*~) main::$0 +Alias (byte**) main::ppb#0 = (byte**~) main::$1 +Successful SSA optimization Pass2AliasElimination +Constant (const byte*) main::SCREEN#0 = ((byte*))$400 +Constant right-side identified [2] (byte*) main::pb#0 ← & (byte) main::b#0 +Constant (const byte**) main::ppb#0 = &main::pb#0 +Successful SSA optimization Pass2ConstantIdentification +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end +CALL GRAPH +Calls in [] to main:2 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] (byte) main::b#0 ← (byte) 'a' + [5] (byte*) main::pb#0 ← &(byte) main::b#0 + [6] *((const byte*) main::SCREEN#0) ← *(*((const byte**) main::ppb#0)) + to:main::@return +main::@return: scope:[main] from main + [7] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(void()) main() +(byte*) main::SCREEN +(byte) main::b +(byte) main::b#0 2.0 +(byte*) main::pb +(byte*) main::pb#0 20.0 +(byte**) main::ppb + +Initial phi equivalence classes +Added variable main::b#0 to zero page equivalence class [ main::b#0 ] +Added variable main::pb#0 to zero page equivalence class [ main::pb#0 ] +Complete equivalence classes +[ main::b#0 ] +[ main::pb#0 ] +Allocated zp ZP_BYTE:2 [ main::b#0 ] +Allocated zp ZP_WORD:3 [ main::pb#0 ] + +INITIAL ASM +//SEG0 File Comments +// Tests a simple pointer to a pointer +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels +//SEG3 @begin +bbegin: +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 +//SEG5 @1 +b1: +//SEG6 [2] call main + jsr main +//SEG7 [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend +//SEG8 @end +bend: +//SEG9 main +main: { + .label SCREEN = $400 + .label ppb = pb + .label b = 2 + .label pb = 3 + //SEG10 [4] (byte) main::b#0 ← (byte) 'a' -- vbuz1=vbuc1 + lda #'a' + sta b + //SEG11 [5] (byte*) main::pb#0 ← &(byte) main::b#0 -- pbuz1=pbuc1 + lda #b + sta pb+1 + //SEG12 [6] *((const byte*) main::SCREEN#0) ← *(*((const byte**) main::ppb#0)) -- _deref_pbuc1=_deref__deref_pptc2 + ldy #0 + lda (ppb),y + sta SCREEN + jmp breturn + //SEG13 main::@return + breturn: + //SEG14 [7] return + rts +} + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [4] (byte) main::b#0 ← (byte) 'a' [ main::b#0 ] ( main:2 [ main::b#0 ] ) always clobbers reg byte a +Statement [5] (byte*) main::pb#0 ← &(byte) main::b#0 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [6] *((const byte*) main::SCREEN#0) ← *(*((const byte**) main::ppb#0)) [ ] ( main:2 [ ] ) always clobbers reg byte a reg byte y +Potential registers zp ZP_BYTE:2 [ main::b#0 ] : zp ZP_BYTE:2 , +Potential registers zp ZP_WORD:3 [ main::pb#0 ] : zp ZP_WORD:3 , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 20: zp ZP_WORD:3 [ main::pb#0 ] 2: zp ZP_BYTE:2 [ main::b#0 ] +Uplift Scope [] + +Uplifting [main] best 47 combination zp ZP_WORD:3 [ main::pb#0 ] zp ZP_BYTE:2 [ main::b#0 ] +Uplifting [] best 47 combination +Attempting to uplift remaining variables inzp ZP_BYTE:2 [ main::b#0 ] +Uplifting [main] best 47 combination zp ZP_BYTE:2 [ main::b#0 ] + +ASSEMBLER BEFORE OPTIMIZATION +//SEG0 File Comments +// Tests a simple pointer to a pointer +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels +//SEG3 @begin +bbegin: +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 +//SEG5 @1 +b1: +//SEG6 [2] call main + jsr main +//SEG7 [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend +//SEG8 @end +bend: +//SEG9 main +main: { + .label SCREEN = $400 + .label ppb = pb + .label b = 2 + .label pb = 3 + //SEG10 [4] (byte) main::b#0 ← (byte) 'a' -- vbuz1=vbuc1 + lda #'a' + sta b + //SEG11 [5] (byte*) main::pb#0 ← &(byte) main::b#0 -- pbuz1=pbuc1 + lda #b + sta pb+1 + //SEG12 [6] *((const byte*) main::SCREEN#0) ← *(*((const byte**) main::ppb#0)) -- _deref_pbuc1=_deref__deref_pptc2 + ldy #0 + lda (ppb),y + sta SCREEN + jmp breturn + //SEG13 main::@return + breturn: + //SEG14 [7] return + rts +} + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction bend_from_b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction bbegin: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(void()) main() +(label) main::@return +(byte*) main::SCREEN +(const byte*) main::SCREEN#0 SCREEN = ((byte*))(word/signed word/dword/signed dword) $400 +(byte) main::b +(byte) main::b#0 b zp ZP_BYTE:2 2.0 +(byte*) main::pb +(byte*) main::pb#0 pb zp ZP_WORD:3 20.0 +(byte**) main::ppb +(const byte**) main::ppb#0 ppb = &(byte*) main::pb#0 + +zp ZP_BYTE:2 [ main::b#0 ] +zp ZP_WORD:3 [ main::pb#0 ] + + +FINAL ASSEMBLER +Score: 32 + +//SEG0 File Comments +// Tests a simple pointer to a pointer +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG2 Global Constants & labels +//SEG3 @begin +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +//SEG5 @1 +//SEG6 [2] call main +//SEG7 [3] phi from @1 to @end [phi:@1->@end] +//SEG8 @end +//SEG9 main +main: { + .label SCREEN = $400 + .label ppb = pb + .label b = 2 + .label pb = 3 + //SEG10 [4] (byte) main::b#0 ← (byte) 'a' -- vbuz1=vbuc1 + lda #'a' + sta b + //SEG11 [5] (byte*) main::pb#0 ← &(byte) main::b#0 -- pbuz1=pbuc1 + lda #b + sta pb+1 + //SEG12 [6] *((const byte*) main::SCREEN#0) ← *(*((const byte**) main::ppb#0)) -- _deref_pbuc1=_deref__deref_pptc2 + ldy #0 + lda (ppb),y + sta SCREEN + //SEG13 main::@return + //SEG14 [7] return + rts +} + diff --git a/src/test/ref/pointer-pointer-1.sym b/src/test/ref/pointer-pointer-1.sym new file mode 100644 index 000000000..b3d1701f1 --- /dev/null +++ b/src/test/ref/pointer-pointer-1.sym @@ -0,0 +1,16 @@ +(label) @1 +(label) @begin +(label) @end +(void()) main() +(label) main::@return +(byte*) main::SCREEN +(const byte*) main::SCREEN#0 SCREEN = ((byte*))(word/signed word/dword/signed dword) $400 +(byte) main::b +(byte) main::b#0 b zp ZP_BYTE:2 2.0 +(byte*) main::pb +(byte*) main::pb#0 pb zp ZP_WORD:3 20.0 +(byte**) main::ppb +(const byte**) main::ppb#0 ppb = &(byte*) main::pb#0 + +zp ZP_BYTE:2 [ main::b#0 ] +zp ZP_WORD:3 [ main::pb#0 ] diff --git a/src/test/ref/pointer-pointer-2.asm b/src/test/ref/pointer-pointer-2.asm new file mode 100644 index 000000000..71d2899ea --- /dev/null +++ b/src/test/ref/pointer-pointer-2.asm @@ -0,0 +1,65 @@ +// Tests pointer to pointer in a more complex setup +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label textid = 4 +main: { + .label screen = 5 + .label text = 2 + ldx #0 + lda #<$400 + sta screen + lda #>$400 + sta screen+1 + txa + sta textid + sta text + sta text+1 + b1: + jsr nexttext + b2: + ldy #0 + lda (text),y + cmp #'@' + bne b3 + inx + cpx #$15 + bne b1 + rts + b3: + ldy #0 + lda (text),y + sta (screen),y + inc screen + bne !+ + inc screen+1 + !: + inc text + bne !+ + inc text+1 + !: + jmp b2 +} +// Choose the next text to show - by updating the text pointer pointed to by the passed pointer to a pointer +nexttext: { + .label textp = main.text + lda #1 + and textid + inc textid + cmp #0 + beq b1 + lda #text2 + sta textp+1 + breturn: + rts + b1: + lda #text1 + sta textp+1 + jmp breturn +} + text1: .text "camelot @" + text2: .text "rex @" diff --git a/src/test/ref/pointer-pointer-2.cfg b/src/test/ref/pointer-pointer-2.cfg new file mode 100644 index 000000000..4aa8f483d --- /dev/null +++ b/src/test/ref/pointer-pointer-2.cfg @@ -0,0 +1,50 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] phi() + to:main::@1 +main::@1: scope:[main] from main main::@4 + [5] (byte) main::i#5 ← phi( main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@4/(byte) main::i#1 ) + [5] (byte*) main::screen#4 ← phi( main/((byte*))(word/signed word/dword/signed dword) $400 main::@4/(byte*) main::screen#2 ) + [5] (byte) textid#11 ← phi( main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@4/(byte) textid#13 ) + [5] (byte*) main::text#2 ← phi( main/(byte*) 0 main::@4/(byte*) main::text#3 ) + [6] call nexttext + to:main::@2 +main::@2: scope:[main] from main::@1 main::@3 + [7] (byte*) main::screen#2 ← phi( main::@3/(byte*) main::screen#1 main::@1/(byte*) main::screen#4 ) + [7] (byte*) main::text#3 ← phi( main::@3/(byte*) main::text#1 main::@1/(byte*) main::text#2 ) + [8] if(*((byte*) main::text#3)!=(byte) '@') goto main::@3 + to:main::@4 +main::@4: scope:[main] from main::@2 + [9] (byte) main::i#1 ← ++ (byte) main::i#5 + [10] if((byte) main::i#1!=(byte/signed byte/word/signed word/dword/signed dword) $15) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@4 + [11] return + to:@return +main::@3: scope:[main] from main::@2 + [12] *((byte*) main::screen#2) ← *((byte*) main::text#3) + [13] (byte*) main::screen#1 ← ++ (byte*) main::screen#2 + [14] (byte*) main::text#1 ← ++ (byte*) main::text#3 + to:main::@2 +nexttext: scope:[nexttext] from main::@1 + [15] (byte~) nexttext::$0 ← (byte) textid#11 & (byte/signed byte/word/signed word/dword/signed dword) 1 + [16] (byte) textid#13 ← ++ (byte) textid#11 + [17] if((byte~) nexttext::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto nexttext::@1 + to:nexttext::@2 +nexttext::@2: scope:[nexttext] from nexttext + [18] *((const byte**) nexttext::textp#0) ← (const byte[]) text2#0 + to:nexttext::@return +nexttext::@return: scope:[nexttext] from nexttext::@1 nexttext::@2 + [19] return + to:@return +nexttext::@1: scope:[nexttext] from nexttext + [20] *((const byte**) nexttext::textp#0) ← (const byte[]) text1#0 + to:nexttext::@return diff --git a/src/test/ref/pointer-pointer-2.log b/src/test/ref/pointer-pointer-2.log new file mode 100644 index 000000000..61b6ba1ea --- /dev/null +++ b/src/test/ref/pointer-pointer-2.log @@ -0,0 +1,866 @@ + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + to:@1 +main: scope:[main] from @2 + (byte) textid#16 ← phi( @2/(byte) textid#15 ) + (byte*) main::screen#0 ← ((byte*)) (word/signed word/dword/signed dword) $400 + (byte*) main::text#0 ← (byte*) 0 + (byte) main::i#0 ← (byte/signed byte/word/signed word/dword/signed dword) 0 + to:main::@1 +main::@1: scope:[main] from main main::@4 + (byte) main::i#6 ← phi( main/(byte) main::i#0 main::@4/(byte) main::i#1 ) + (byte*) main::screen#5 ← phi( main/(byte*) main::screen#0 main::@4/(byte*) main::screen#6 ) + (byte) textid#11 ← phi( main/(byte) textid#16 main::@4/(byte) textid#12 ) + (byte*) main::text#2 ← phi( main/(byte*) main::text#0 main::@4/(byte*) main::text#5 ) + (byte**~) main::$0 ← & (byte*) main::text#2 + (byte**) nexttext::textp#0 ← (byte**~) main::$0 + call nexttext + to:main::@9 +main::@9: scope:[main] from main::@1 + (byte) main::i#5 ← phi( main::@1/(byte) main::i#6 ) + (byte*) main::screen#4 ← phi( main::@1/(byte*) main::screen#5 ) + (byte*) main::text#6 ← phi( main::@1/(byte*) main::text#2 ) + (byte) textid#6 ← phi( main::@1/(byte) textid#4 ) + (byte) textid#0 ← (byte) textid#6 + to:main::@2 +main::@2: scope:[main] from main::@3 main::@9 + (byte) textid#17 ← phi( main::@3/(byte) textid#18 main::@9/(byte) textid#0 ) + (byte) main::i#3 ← phi( main::@3/(byte) main::i#4 main::@9/(byte) main::i#5 ) + (byte*) main::screen#3 ← phi( main::@3/(byte*) main::screen#1 main::@9/(byte*) main::screen#4 ) + (byte*) main::text#3 ← phi( main::@3/(byte*) main::text#1 main::@9/(byte*) main::text#6 ) + (bool~) main::$2 ← *((byte*) main::text#3) != (byte) '@' + if((bool~) main::$2) goto main::@3 + to:main::@4 +main::@3: scope:[main] from main::@2 + (byte) textid#18 ← phi( main::@2/(byte) textid#17 ) + (byte) main::i#4 ← phi( main::@2/(byte) main::i#3 ) + (byte*) main::screen#2 ← phi( main::@2/(byte*) main::screen#3 ) + (byte*) main::text#4 ← phi( main::@2/(byte*) main::text#3 ) + *((byte*) main::screen#2) ← *((byte*) main::text#4) + (byte*) main::screen#1 ← ++ (byte*) main::screen#2 + (byte*) main::text#1 ← ++ (byte*) main::text#4 + to:main::@2 +main::@4: scope:[main] from main::@2 + (byte*) main::screen#6 ← phi( main::@2/(byte*) main::screen#3 ) + (byte) textid#12 ← phi( main::@2/(byte) textid#17 ) + (byte*) main::text#5 ← phi( main::@2/(byte*) main::text#3 ) + (byte) main::i#2 ← phi( main::@2/(byte) main::i#3 ) + (byte) main::i#1 ← (byte) main::i#2 + rangenext(0,$14) + (bool~) main::$3 ← (byte) main::i#1 != rangelast(0,$14) + if((bool~) main::$3) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@4 + (byte) textid#7 ← phi( main::@4/(byte) textid#12 ) + (byte) textid#1 ← (byte) textid#7 + return + to:@return +@1: scope:[] from @begin + (byte[]) text1#0 ← (const string) $0 + (byte[]) text2#0 ← (const string) $1 + (byte) textid#2 ← (byte/signed byte/word/signed word/dword/signed dword) 0 + to:@2 +nexttext: scope:[nexttext] from main::@1 + (byte**) nexttext::textp#3 ← phi( main::@1/(byte**) nexttext::textp#0 ) + (byte) textid#8 ← phi( main::@1/(byte) textid#11 ) + (byte~) nexttext::$0 ← (byte) textid#8 & (byte/signed byte/word/signed word/dword/signed dword) 1 + (bool~) nexttext::$1 ← (byte~) nexttext::$0 == (byte/signed byte/word/signed word/dword/signed dword) 0 + (byte) textid#3 ← ++ (byte) textid#8 + if((bool~) nexttext::$1) goto nexttext::@1 + to:nexttext::@3 +nexttext::@1: scope:[nexttext] from nexttext + (byte) textid#13 ← phi( nexttext/(byte) textid#3 ) + (byte**) nexttext::textp#1 ← phi( nexttext/(byte**) nexttext::textp#3 ) + *((byte**) nexttext::textp#1) ← (byte[]) text1#0 + to:nexttext::@return +nexttext::@3: scope:[nexttext] from nexttext + (byte) textid#14 ← phi( nexttext/(byte) textid#3 ) + (byte**) nexttext::textp#2 ← phi( nexttext/(byte**) nexttext::textp#3 ) + *((byte**) nexttext::textp#2) ← (byte[]) text2#0 + to:nexttext::@return +nexttext::@return: scope:[nexttext] from nexttext::@1 nexttext::@3 + (byte) textid#9 ← phi( nexttext::@1/(byte) textid#13 nexttext::@3/(byte) textid#14 ) + (byte) textid#4 ← (byte) textid#9 + return + to:@return +@2: scope:[] from @1 + (byte) textid#15 ← phi( @1/(byte) textid#2 ) + call main + to:@3 +@3: scope:[] from @2 + (byte) textid#10 ← phi( @2/(byte) textid#1 ) + (byte) textid#5 ← (byte) textid#10 + to:@end +@end: scope:[] from @3 + +SYMBOL TABLE SSA +(const string) $0 = (string) "camelot @" +(const string) $1 = (string) "rex @" +(label) @1 +(label) @2 +(label) @3 +(label) @begin +(label) @end +(void()) main() +(byte**~) main::$0 +(bool~) main::$2 +(bool~) main::$3 +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(label) main::@9 +(label) main::@return +(byte) main::i +(byte) main::i#0 +(byte) main::i#1 +(byte) main::i#2 +(byte) main::i#3 +(byte) main::i#4 +(byte) main::i#5 +(byte) main::i#6 +(byte*) main::screen +(byte*) main::screen#0 +(byte*) main::screen#1 +(byte*) main::screen#2 +(byte*) main::screen#3 +(byte*) main::screen#4 +(byte*) main::screen#5 +(byte*) main::screen#6 +(byte*) main::text +(byte*) main::text#0 +(byte*) main::text#1 +(byte*) main::text#2 +(byte*) main::text#3 +(byte*) main::text#4 +(byte*) main::text#5 +(byte*) main::text#6 +(void()) nexttext((byte**) nexttext::textp) +(byte~) nexttext::$0 +(bool~) nexttext::$1 +(label) nexttext::@1 +(label) nexttext::@3 +(label) nexttext::@return +(byte**) nexttext::textp +(byte**) nexttext::textp#0 +(byte**) nexttext::textp#1 +(byte**) nexttext::textp#2 +(byte**) nexttext::textp#3 +(byte[]) text1 +(byte[]) text1#0 +(byte[]) text2 +(byte[]) text2#0 +(byte) textid +(byte) textid#0 +(byte) textid#1 +(byte) textid#10 +(byte) textid#11 +(byte) textid#12 +(byte) textid#13 +(byte) textid#14 +(byte) textid#15 +(byte) textid#16 +(byte) textid#17 +(byte) textid#18 +(byte) textid#2 +(byte) textid#3 +(byte) textid#4 +(byte) textid#5 +(byte) textid#6 +(byte) textid#7 +(byte) textid#8 +(byte) textid#9 + +Alias (byte**) nexttext::textp#0 = (byte**~) main::$0 +Alias (byte*) main::text#2 = (byte*) main::text#6 +Alias (byte*) main::screen#4 = (byte*) main::screen#5 +Alias (byte) main::i#5 = (byte) main::i#6 +Alias (byte) textid#0 = (byte) textid#6 +Alias (byte*) main::text#3 = (byte*) main::text#4 (byte*) main::text#5 +Alias (byte*) main::screen#2 = (byte*) main::screen#3 (byte*) main::screen#6 +Alias (byte) main::i#2 = (byte) main::i#4 (byte) main::i#3 +Alias (byte) textid#1 = (byte) textid#18 (byte) textid#17 (byte) textid#12 (byte) textid#7 +Alias (byte**) nexttext::textp#1 = (byte**) nexttext::textp#3 (byte**) nexttext::textp#2 +Alias (byte) textid#13 = (byte) textid#3 (byte) textid#14 +Alias (byte) textid#4 = (byte) textid#9 +Alias (byte) textid#15 = (byte) textid#2 +Alias (byte) textid#10 = (byte) textid#5 +Successful SSA optimization Pass2AliasElimination +Alias (byte) textid#13 = (byte) textid#4 +Successful SSA optimization Pass2AliasElimination +Self Phi Eliminated (byte) main::i#2 +Self Phi Eliminated (byte) textid#1 +Successful SSA optimization Pass2SelfPhiElimination +Redundant Phi (byte) textid#16 (byte) textid#15 +Redundant Phi (byte) textid#0 (byte) textid#13 +Redundant Phi (byte) main::i#2 (byte) main::i#5 +Redundant Phi (byte) textid#1 (byte) textid#0 +Redundant Phi (byte) textid#8 (byte) textid#11 +Redundant Phi (byte**) nexttext::textp#1 (byte**) nexttext::textp#0 +Redundant Phi (byte) textid#10 (byte) textid#1 +Successful SSA optimization Pass2RedundantPhiElimination +Simple Condition (bool~) main::$2 [12] if(*((byte*) main::text#3)!=(byte) '@') goto main::@3 +Simple Condition (bool~) main::$3 [20] if((byte) main::i#1!=rangelast(0,$14)) goto main::@1 +Simple Condition (bool~) nexttext::$1 [31] if((byte~) nexttext::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto nexttext::@1 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const byte*) main::screen#0 = ((byte*))$400 +Constant (const byte*) main::text#0 = 0 +Constant (const byte) main::i#0 = 0 +Constant (const byte**) nexttext::textp#0 = &main::text#2 +Constant (const byte[]) text1#0 = $0 +Constant (const byte[]) text2#0 = $1 +Constant (const byte) textid#15 = 0 +Successful SSA optimization Pass2ConstantIdentification +Resolved ranged next value main::i#1 ← ++ main::i#5 to ++ +Resolved ranged comparison value if(main::i#1!=rangelast(0,$14)) goto main::@1 to (byte/signed byte/word/signed word/dword/signed dword) $15 +Culled Empty Block (label) main::@9 +Culled Empty Block (label) @1 +Culled Empty Block (label) @3 +Successful SSA optimization Pass2CullEmptyBlocks +Inlining constant with var siblings (const byte*) main::screen#0 +Inlining constant with var siblings (const byte*) main::text#0 +Inlining constant with var siblings (const byte) main::i#0 +Inlining constant with var siblings (const byte) textid#15 +Constant inlined main::screen#0 = ((byte*))(word/signed word/dword/signed dword) $400 +Constant inlined main::i#0 = (byte/signed byte/word/signed word/dword/signed dword) 0 +Constant inlined main::text#0 = (byte*) 0 +Constant inlined textid#15 = (byte/signed byte/word/signed word/dword/signed dword) 0 +Constant inlined $0 = (const byte[]) text1#0 +Constant inlined $1 = (const byte[]) text2#0 +Successful SSA optimization Pass2ConstantInlining +Added new block during phi lifting main::@10(between main::@4 and main::@1) +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +CALL GRAPH +Calls in [] to main:2 +Calls in [main] to nexttext:6 + +Created 6 initial phi equivalence classes +Coalesced [7] main::text#9 ← main::text#2 +Coalesced [8] main::screen#9 ← main::screen#4 +Coalesced (already) [14] main::text#7 ← main::text#3 +Coalesced [15] textid#19 ← textid#13 +Coalesced (already) [16] main::screen#7 ← main::screen#2 +Coalesced [17] main::i#7 ← main::i#1 +Coalesced [21] main::text#8 ← main::text#1 +Coalesced [22] main::screen#8 ← main::screen#1 +Coalesced down to 4 phi equivalence classes +Culled Empty Block (label) main::@10 +Renumbering block @2 to @1 +Renumbering block nexttext::@3 to nexttext::@2 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] phi() + to:main::@1 +main::@1: scope:[main] from main main::@4 + [5] (byte) main::i#5 ← phi( main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@4/(byte) main::i#1 ) + [5] (byte*) main::screen#4 ← phi( main/((byte*))(word/signed word/dword/signed dword) $400 main::@4/(byte*) main::screen#2 ) + [5] (byte) textid#11 ← phi( main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@4/(byte) textid#13 ) + [5] (byte*) main::text#2 ← phi( main/(byte*) 0 main::@4/(byte*) main::text#3 ) + [6] call nexttext + to:main::@2 +main::@2: scope:[main] from main::@1 main::@3 + [7] (byte*) main::screen#2 ← phi( main::@3/(byte*) main::screen#1 main::@1/(byte*) main::screen#4 ) + [7] (byte*) main::text#3 ← phi( main::@3/(byte*) main::text#1 main::@1/(byte*) main::text#2 ) + [8] if(*((byte*) main::text#3)!=(byte) '@') goto main::@3 + to:main::@4 +main::@4: scope:[main] from main::@2 + [9] (byte) main::i#1 ← ++ (byte) main::i#5 + [10] if((byte) main::i#1!=(byte/signed byte/word/signed word/dword/signed dword) $15) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@4 + [11] return + to:@return +main::@3: scope:[main] from main::@2 + [12] *((byte*) main::screen#2) ← *((byte*) main::text#3) + [13] (byte*) main::screen#1 ← ++ (byte*) main::screen#2 + [14] (byte*) main::text#1 ← ++ (byte*) main::text#3 + to:main::@2 +nexttext: scope:[nexttext] from main::@1 + [15] (byte~) nexttext::$0 ← (byte) textid#11 & (byte/signed byte/word/signed word/dword/signed dword) 1 + [16] (byte) textid#13 ← ++ (byte) textid#11 + [17] if((byte~) nexttext::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto nexttext::@1 + to:nexttext::@2 +nexttext::@2: scope:[nexttext] from nexttext + [18] *((const byte**) nexttext::textp#0) ← (const byte[]) text2#0 + to:nexttext::@return +nexttext::@return: scope:[nexttext] from nexttext::@1 nexttext::@2 + [19] return + to:@return +nexttext::@1: scope:[nexttext] from nexttext + [20] *((const byte**) nexttext::textp#0) ← (const byte[]) text1#0 + to:nexttext::@return + + +VARIABLE REGISTER WEIGHTS +(void()) main() +(byte) main::i +(byte) main::i#1 16.5 +(byte) main::i#5 3.142857142857143 +(byte*) main::screen +(byte*) main::screen#1 101.0 +(byte*) main::screen#2 65.0 +(byte*) main::screen#4 11.0 +(byte*) main::text +(byte*) main::text#1 202.0 +(byte*) main::text#2 11.0 +(byte*) main::text#3 70.99999999999999 +(void()) nexttext((byte**) nexttext::textp) +(byte~) nexttext::$0 2.0 +(byte**) nexttext::textp +(byte[]) text1 +(byte[]) text2 +(byte) textid +(byte) textid#11 7.5 +(byte) textid#13 1.0 + +Initial phi equivalence classes +[ main::text#2 main::text#3 main::text#1 ] +[ textid#11 textid#13 ] +[ main::screen#4 main::screen#2 main::screen#1 ] +[ main::i#5 main::i#1 ] +Added variable nexttext::$0 to zero page equivalence class [ nexttext::$0 ] +Complete equivalence classes +[ main::text#2 main::text#3 main::text#1 ] +[ textid#11 textid#13 ] +[ main::screen#4 main::screen#2 main::screen#1 ] +[ main::i#5 main::i#1 ] +[ nexttext::$0 ] +Allocated zp ZP_WORD:2 [ main::text#2 main::text#3 main::text#1 ] +Allocated zp ZP_BYTE:4 [ textid#11 textid#13 ] +Allocated zp ZP_WORD:5 [ main::screen#4 main::screen#2 main::screen#1 ] +Allocated zp ZP_BYTE:7 [ main::i#5 main::i#1 ] +Allocated zp ZP_BYTE:8 [ nexttext::$0 ] + +INITIAL ASM +//SEG0 File Comments +// Tests pointer to pointer in a more complex setup +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .label textid = 4 +//SEG3 @begin +bbegin: +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 +//SEG5 @1 +b1: +//SEG6 [2] call main +//SEG7 [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main +//SEG8 [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend +//SEG9 @end +bend: +//SEG10 main +main: { + .label screen = 5 + .label text = 2 + .label i = 7 + //SEG11 [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + //SEG12 [5] phi (byte) main::i#5 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta i + //SEG13 [5] phi (byte*) main::screen#4 = ((byte*))(word/signed word/dword/signed dword) $400 [phi:main->main::@1#1] -- pbuz1=pbuc1 + lda #<$400 + sta screen + lda #>$400 + sta screen+1 + //SEG14 [5] phi (byte) textid#11 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#2] -- vbuz1=vbuc1 + lda #0 + sta textid + //SEG15 [5] phi (byte*) main::text#2 = (byte*) 0 [phi:main->main::@1#3] -- pbuz1=pbuc1 + lda #<0 + sta text + lda #>0 + sta text+1 + jmp b1 + //SEG16 [5] phi from main::@4 to main::@1 [phi:main::@4->main::@1] + b1_from_b4: + //SEG17 [5] phi (byte) main::i#5 = (byte) main::i#1 [phi:main::@4->main::@1#0] -- register_copy + //SEG18 [5] phi (byte*) main::screen#4 = (byte*) main::screen#2 [phi:main::@4->main::@1#1] -- register_copy + //SEG19 [5] phi (byte) textid#11 = (byte) textid#13 [phi:main::@4->main::@1#2] -- register_copy + //SEG20 [5] phi (byte*) main::text#2 = (byte*) main::text#3 [phi:main::@4->main::@1#3] -- register_copy + jmp b1 + //SEG21 main::@1 + b1: + //SEG22 [6] call nexttext + jsr nexttext + //SEG23 [7] phi from main::@1 main::@3 to main::@2 [phi:main::@1/main::@3->main::@2] + b2_from_b1: + b2_from_b3: + //SEG24 [7] phi (byte*) main::screen#2 = (byte*) main::screen#4 [phi:main::@1/main::@3->main::@2#0] -- register_copy + //SEG25 [7] phi (byte*) main::text#3 = (byte*) main::text#2 [phi:main::@1/main::@3->main::@2#1] -- register_copy + jmp b2 + //SEG26 main::@2 + b2: + //SEG27 [8] if(*((byte*) main::text#3)!=(byte) '@') goto main::@3 -- _deref_pbuz1_neq_vbuc1_then_la1 + ldy #0 + lda (text),y + cmp #'@' + bne b3 + jmp b4 + //SEG28 main::@4 + b4: + //SEG29 [9] (byte) main::i#1 ← ++ (byte) main::i#5 -- vbuz1=_inc_vbuz1 + inc i + //SEG30 [10] if((byte) main::i#1!=(byte/signed byte/word/signed word/dword/signed dword) $15) goto main::@1 -- vbuz1_neq_vbuc1_then_la1 + lda #$15 + cmp i + bne b1_from_b4 + jmp breturn + //SEG31 main::@return + breturn: + //SEG32 [11] return + rts + //SEG33 main::@3 + b3: + //SEG34 [12] *((byte*) main::screen#2) ← *((byte*) main::text#3) -- _deref_pbuz1=_deref_pbuz2 + ldy #0 + lda (text),y + ldy #0 + sta (screen),y + //SEG35 [13] (byte*) main::screen#1 ← ++ (byte*) main::screen#2 -- pbuz1=_inc_pbuz1 + inc screen + bne !+ + inc screen+1 + !: + //SEG36 [14] (byte*) main::text#1 ← ++ (byte*) main::text#3 -- pbuz1=_inc_pbuz1 + inc text + bne !+ + inc text+1 + !: + jmp b2_from_b3 +} +//SEG37 nexttext +// Choose the next text to show - by updating the text pointer pointed to by the passed pointer to a pointer +nexttext: { + .label textp = main.text + .label _0 = 8 + //SEG38 [15] (byte~) nexttext::$0 ← (byte) textid#11 & (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuz1=vbuz2_band_vbuc1 + lda #1 + and textid + sta _0 + //SEG39 [16] (byte) textid#13 ← ++ (byte) textid#11 -- vbuz1=_inc_vbuz1 + inc textid + //SEG40 [17] if((byte~) nexttext::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto nexttext::@1 -- vbuz1_eq_0_then_la1 + lda _0 + cmp #0 + beq b1 + jmp b2 + //SEG41 nexttext::@2 + b2: + //SEG42 [18] *((const byte**) nexttext::textp#0) ← (const byte[]) text2#0 -- _deref_pptc1=pbuc2 + lda #text2 + sta textp+1 + jmp breturn + //SEG43 nexttext::@return + breturn: + //SEG44 [19] return + rts + //SEG45 nexttext::@1 + b1: + //SEG46 [20] *((const byte**) nexttext::textp#0) ← (const byte[]) text1#0 -- _deref_pptc1=pbuc2 + lda #text1 + sta textp+1 + jmp breturn +} + text1: .text "camelot @" + text2: .text "rex @" + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [8] if(*((byte*) main::text#3)!=(byte) '@') goto main::@3 [ main::i#5 main::text#3 textid#13 main::screen#2 ] ( main:2 [ main::i#5 main::text#3 textid#13 main::screen#2 ] ) always clobbers reg byte a reg byte y +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:7 [ main::i#5 main::i#1 ] +Removing always clobbered register reg byte y as potential for zp ZP_BYTE:7 [ main::i#5 main::i#1 ] +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:4 [ textid#11 textid#13 ] +Removing always clobbered register reg byte y as potential for zp ZP_BYTE:4 [ textid#11 textid#13 ] +Statement [12] *((byte*) main::screen#2) ← *((byte*) main::text#3) [ main::i#5 main::text#3 textid#13 main::screen#2 ] ( main:2 [ main::i#5 main::text#3 textid#13 main::screen#2 ] ) always clobbers reg byte a reg byte y +Statement [15] (byte~) nexttext::$0 ← (byte) textid#11 & (byte/signed byte/word/signed word/dword/signed dword) 1 [ textid#11 nexttext::$0 ] ( main:2::nexttext:6 [ main::text#2 main::screen#4 main::i#5 textid#11 nexttext::$0 ] ) always clobbers reg byte a +Statement [18] *((const byte**) nexttext::textp#0) ← (const byte[]) text2#0 [ textid#13 ] ( main:2::nexttext:6 [ main::text#2 main::screen#4 main::i#5 textid#13 ] ) always clobbers reg byte a +Statement [20] *((const byte**) nexttext::textp#0) ← (const byte[]) text1#0 [ textid#13 ] ( main:2::nexttext:6 [ main::text#2 main::screen#4 main::i#5 textid#13 ] ) always clobbers reg byte a +Statement [8] if(*((byte*) main::text#3)!=(byte) '@') goto main::@3 [ main::i#5 main::text#3 textid#13 main::screen#2 ] ( main:2 [ main::i#5 main::text#3 textid#13 main::screen#2 ] ) always clobbers reg byte a reg byte y +Statement [12] *((byte*) main::screen#2) ← *((byte*) main::text#3) [ main::i#5 main::text#3 textid#13 main::screen#2 ] ( main:2 [ main::i#5 main::text#3 textid#13 main::screen#2 ] ) always clobbers reg byte a reg byte y +Statement [15] (byte~) nexttext::$0 ← (byte) textid#11 & (byte/signed byte/word/signed word/dword/signed dword) 1 [ textid#11 nexttext::$0 ] ( main:2::nexttext:6 [ main::text#2 main::screen#4 main::i#5 textid#11 nexttext::$0 ] ) always clobbers reg byte a +Statement [18] *((const byte**) nexttext::textp#0) ← (const byte[]) text2#0 [ textid#13 ] ( main:2::nexttext:6 [ main::text#2 main::screen#4 main::i#5 textid#13 ] ) always clobbers reg byte a +Statement [20] *((const byte**) nexttext::textp#0) ← (const byte[]) text1#0 [ textid#13 ] ( main:2::nexttext:6 [ main::text#2 main::screen#4 main::i#5 textid#13 ] ) always clobbers reg byte a +Potential registers zp ZP_WORD:2 [ main::text#2 main::text#3 main::text#1 ] : zp ZP_WORD:2 , +Potential registers zp ZP_BYTE:4 [ textid#11 textid#13 ] : zp ZP_BYTE:4 , reg byte x , +Potential registers zp ZP_WORD:5 [ main::screen#4 main::screen#2 main::screen#1 ] : zp ZP_WORD:5 , +Potential registers zp ZP_BYTE:7 [ main::i#5 main::i#1 ] : zp ZP_BYTE:7 , reg byte x , +Potential registers zp ZP_BYTE:8 [ nexttext::$0 ] : zp ZP_BYTE:8 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 284: zp ZP_WORD:2 [ main::text#2 main::text#3 main::text#1 ] 177: zp ZP_WORD:5 [ main::screen#4 main::screen#2 main::screen#1 ] 19.64: zp ZP_BYTE:7 [ main::i#5 main::i#1 ] +Uplift Scope [] 8.5: zp ZP_BYTE:4 [ textid#11 textid#13 ] +Uplift Scope [nexttext] 2: zp ZP_BYTE:8 [ nexttext::$0 ] + +Uplifting [main] best 6712 combination zp ZP_WORD:2 [ main::text#2 main::text#3 main::text#1 ] zp ZP_WORD:5 [ main::screen#4 main::screen#2 main::screen#1 ] reg byte x [ main::i#5 main::i#1 ] +Uplifting [] best 6712 combination zp ZP_BYTE:4 [ textid#11 textid#13 ] +Uplifting [nexttext] best 6706 combination reg byte a [ nexttext::$0 ] +Attempting to uplift remaining variables inzp ZP_BYTE:4 [ textid#11 textid#13 ] +Uplifting [] best 6706 combination zp ZP_BYTE:4 [ textid#11 textid#13 ] + +ASSEMBLER BEFORE OPTIMIZATION +//SEG0 File Comments +// Tests pointer to pointer in a more complex setup +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .label textid = 4 +//SEG3 @begin +bbegin: +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 +//SEG5 @1 +b1: +//SEG6 [2] call main +//SEG7 [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main +//SEG8 [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend +//SEG9 @end +bend: +//SEG10 main +main: { + .label screen = 5 + .label text = 2 + //SEG11 [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + //SEG12 [5] phi (byte) main::i#5 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + //SEG13 [5] phi (byte*) main::screen#4 = ((byte*))(word/signed word/dword/signed dword) $400 [phi:main->main::@1#1] -- pbuz1=pbuc1 + lda #<$400 + sta screen + lda #>$400 + sta screen+1 + //SEG14 [5] phi (byte) textid#11 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#2] -- vbuz1=vbuc1 + lda #0 + sta textid + //SEG15 [5] phi (byte*) main::text#2 = (byte*) 0 [phi:main->main::@1#3] -- pbuz1=pbuc1 + lda #<0 + sta text + lda #>0 + sta text+1 + jmp b1 + //SEG16 [5] phi from main::@4 to main::@1 [phi:main::@4->main::@1] + b1_from_b4: + //SEG17 [5] phi (byte) main::i#5 = (byte) main::i#1 [phi:main::@4->main::@1#0] -- register_copy + //SEG18 [5] phi (byte*) main::screen#4 = (byte*) main::screen#2 [phi:main::@4->main::@1#1] -- register_copy + //SEG19 [5] phi (byte) textid#11 = (byte) textid#13 [phi:main::@4->main::@1#2] -- register_copy + //SEG20 [5] phi (byte*) main::text#2 = (byte*) main::text#3 [phi:main::@4->main::@1#3] -- register_copy + jmp b1 + //SEG21 main::@1 + b1: + //SEG22 [6] call nexttext + jsr nexttext + //SEG23 [7] phi from main::@1 main::@3 to main::@2 [phi:main::@1/main::@3->main::@2] + b2_from_b1: + b2_from_b3: + //SEG24 [7] phi (byte*) main::screen#2 = (byte*) main::screen#4 [phi:main::@1/main::@3->main::@2#0] -- register_copy + //SEG25 [7] phi (byte*) main::text#3 = (byte*) main::text#2 [phi:main::@1/main::@3->main::@2#1] -- register_copy + jmp b2 + //SEG26 main::@2 + b2: + //SEG27 [8] if(*((byte*) main::text#3)!=(byte) '@') goto main::@3 -- _deref_pbuz1_neq_vbuc1_then_la1 + ldy #0 + lda (text),y + cmp #'@' + bne b3 + jmp b4 + //SEG28 main::@4 + b4: + //SEG29 [9] (byte) main::i#1 ← ++ (byte) main::i#5 -- vbuxx=_inc_vbuxx + inx + //SEG30 [10] if((byte) main::i#1!=(byte/signed byte/word/signed word/dword/signed dword) $15) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 + cpx #$15 + bne b1_from_b4 + jmp breturn + //SEG31 main::@return + breturn: + //SEG32 [11] return + rts + //SEG33 main::@3 + b3: + //SEG34 [12] *((byte*) main::screen#2) ← *((byte*) main::text#3) -- _deref_pbuz1=_deref_pbuz2 + ldy #0 + lda (text),y + ldy #0 + sta (screen),y + //SEG35 [13] (byte*) main::screen#1 ← ++ (byte*) main::screen#2 -- pbuz1=_inc_pbuz1 + inc screen + bne !+ + inc screen+1 + !: + //SEG36 [14] (byte*) main::text#1 ← ++ (byte*) main::text#3 -- pbuz1=_inc_pbuz1 + inc text + bne !+ + inc text+1 + !: + jmp b2_from_b3 +} +//SEG37 nexttext +// Choose the next text to show - by updating the text pointer pointed to by the passed pointer to a pointer +nexttext: { + .label textp = main.text + //SEG38 [15] (byte~) nexttext::$0 ← (byte) textid#11 & (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuaa=vbuz1_band_vbuc1 + lda #1 + and textid + //SEG39 [16] (byte) textid#13 ← ++ (byte) textid#11 -- vbuz1=_inc_vbuz1 + inc textid + //SEG40 [17] if((byte~) nexttext::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto nexttext::@1 -- vbuaa_eq_0_then_la1 + cmp #0 + beq b1 + jmp b2 + //SEG41 nexttext::@2 + b2: + //SEG42 [18] *((const byte**) nexttext::textp#0) ← (const byte[]) text2#0 -- _deref_pptc1=pbuc2 + lda #text2 + sta textp+1 + jmp breturn + //SEG43 nexttext::@return + breturn: + //SEG44 [19] return + rts + //SEG45 nexttext::@1 + b1: + //SEG46 [20] *((const byte**) nexttext::textp#0) ← (const byte[]) text1#0 -- _deref_pptc1=pbuc2 + lda #text1 + sta textp+1 + jmp breturn +} + text1: .text "camelot @" + text2: .text "rex @" + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b1 +Removing instruction jmp b2 +Removing instruction jmp b4 +Removing instruction jmp breturn +Removing instruction jmp b2 +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing instruction lda #0 with TXA +Removing instruction lda #<0 +Removing instruction lda #>0 +Removing instruction ldy #0 +Succesful ASM optimization Pass5UnnecesaryLoadElimination +Replacing label b1_from_b4 with b1 +Replacing label b2_from_b3 with b2 +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Removing instruction b1_from_b4: +Removing instruction b2_from_b1: +Removing instruction b2_from_b3: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction b1_from_main: +Removing instruction b4: +Removing instruction breturn: +Removing instruction b2: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction jmp b1 +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction bbegin: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 16.5 +(byte) main::i#5 reg byte x 3.142857142857143 +(byte*) main::screen +(byte*) main::screen#1 screen zp ZP_WORD:5 101.0 +(byte*) main::screen#2 screen zp ZP_WORD:5 65.0 +(byte*) main::screen#4 screen zp ZP_WORD:5 11.0 +(byte*) main::text +(byte*) main::text#1 text zp ZP_WORD:2 202.0 +(byte*) main::text#2 text zp ZP_WORD:2 11.0 +(byte*) main::text#3 text zp ZP_WORD:2 70.99999999999999 +(void()) nexttext((byte**) nexttext::textp) +(byte~) nexttext::$0 reg byte a 2.0 +(label) nexttext::@1 +(label) nexttext::@2 +(label) nexttext::@return +(byte**) nexttext::textp +(const byte**) nexttext::textp#0 textp = &(byte*) main::text#2 +(byte[]) text1 +(const byte[]) text1#0 text1 = (string) "camelot @" +(byte[]) text2 +(const byte[]) text2#0 text2 = (string) "rex @" +(byte) textid +(byte) textid#11 textid zp ZP_BYTE:4 7.5 +(byte) textid#13 textid zp ZP_BYTE:4 1.0 + +zp ZP_WORD:2 [ main::text#2 main::text#3 main::text#1 ] +zp ZP_BYTE:4 [ textid#11 textid#13 ] +zp ZP_WORD:5 [ main::screen#4 main::screen#2 main::screen#1 ] +reg byte x [ main::i#5 main::i#1 ] +reg byte a [ nexttext::$0 ] + + +FINAL ASSEMBLER +Score: 5758 + +//SEG0 File Comments +// Tests pointer to pointer in a more complex setup +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .label textid = 4 +//SEG3 @begin +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +//SEG5 @1 +//SEG6 [2] call main +//SEG7 [4] phi from @1 to main [phi:@1->main] +//SEG8 [3] phi from @1 to @end [phi:@1->@end] +//SEG9 @end +//SEG10 main +main: { + .label screen = 5 + .label text = 2 + //SEG11 [5] phi from main to main::@1 [phi:main->main::@1] + //SEG12 [5] phi (byte) main::i#5 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + //SEG13 [5] phi (byte*) main::screen#4 = ((byte*))(word/signed word/dword/signed dword) $400 [phi:main->main::@1#1] -- pbuz1=pbuc1 + lda #<$400 + sta screen + lda #>$400 + sta screen+1 + //SEG14 [5] phi (byte) textid#11 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#2] -- vbuz1=vbuc1 + txa + sta textid + //SEG15 [5] phi (byte*) main::text#2 = (byte*) 0 [phi:main->main::@1#3] -- pbuz1=pbuc1 + sta text + sta text+1 + //SEG16 [5] phi from main::@4 to main::@1 [phi:main::@4->main::@1] + //SEG17 [5] phi (byte) main::i#5 = (byte) main::i#1 [phi:main::@4->main::@1#0] -- register_copy + //SEG18 [5] phi (byte*) main::screen#4 = (byte*) main::screen#2 [phi:main::@4->main::@1#1] -- register_copy + //SEG19 [5] phi (byte) textid#11 = (byte) textid#13 [phi:main::@4->main::@1#2] -- register_copy + //SEG20 [5] phi (byte*) main::text#2 = (byte*) main::text#3 [phi:main::@4->main::@1#3] -- register_copy + //SEG21 main::@1 + b1: + //SEG22 [6] call nexttext + jsr nexttext + //SEG23 [7] phi from main::@1 main::@3 to main::@2 [phi:main::@1/main::@3->main::@2] + //SEG24 [7] phi (byte*) main::screen#2 = (byte*) main::screen#4 [phi:main::@1/main::@3->main::@2#0] -- register_copy + //SEG25 [7] phi (byte*) main::text#3 = (byte*) main::text#2 [phi:main::@1/main::@3->main::@2#1] -- register_copy + //SEG26 main::@2 + b2: + //SEG27 [8] if(*((byte*) main::text#3)!=(byte) '@') goto main::@3 -- _deref_pbuz1_neq_vbuc1_then_la1 + ldy #0 + lda (text),y + cmp #'@' + bne b3 + //SEG28 main::@4 + //SEG29 [9] (byte) main::i#1 ← ++ (byte) main::i#5 -- vbuxx=_inc_vbuxx + inx + //SEG30 [10] if((byte) main::i#1!=(byte/signed byte/word/signed word/dword/signed dword) $15) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 + cpx #$15 + bne b1 + //SEG31 main::@return + //SEG32 [11] return + rts + //SEG33 main::@3 + b3: + //SEG34 [12] *((byte*) main::screen#2) ← *((byte*) main::text#3) -- _deref_pbuz1=_deref_pbuz2 + ldy #0 + lda (text),y + sta (screen),y + //SEG35 [13] (byte*) main::screen#1 ← ++ (byte*) main::screen#2 -- pbuz1=_inc_pbuz1 + inc screen + bne !+ + inc screen+1 + !: + //SEG36 [14] (byte*) main::text#1 ← ++ (byte*) main::text#3 -- pbuz1=_inc_pbuz1 + inc text + bne !+ + inc text+1 + !: + jmp b2 +} +//SEG37 nexttext +// Choose the next text to show - by updating the text pointer pointed to by the passed pointer to a pointer +nexttext: { + .label textp = main.text + //SEG38 [15] (byte~) nexttext::$0 ← (byte) textid#11 & (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuaa=vbuz1_band_vbuc1 + lda #1 + and textid + //SEG39 [16] (byte) textid#13 ← ++ (byte) textid#11 -- vbuz1=_inc_vbuz1 + inc textid + //SEG40 [17] if((byte~) nexttext::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto nexttext::@1 -- vbuaa_eq_0_then_la1 + cmp #0 + beq b1 + //SEG41 nexttext::@2 + //SEG42 [18] *((const byte**) nexttext::textp#0) ← (const byte[]) text2#0 -- _deref_pptc1=pbuc2 + lda #text2 + sta textp+1 + //SEG43 nexttext::@return + breturn: + //SEG44 [19] return + rts + //SEG45 nexttext::@1 + b1: + //SEG46 [20] *((const byte**) nexttext::textp#0) ← (const byte[]) text1#0 -- _deref_pptc1=pbuc2 + lda #text1 + sta textp+1 + jmp breturn +} + text1: .text "camelot @" + text2: .text "rex @" + diff --git a/src/test/ref/pointer-pointer-2.sym b/src/test/ref/pointer-pointer-2.sym new file mode 100644 index 000000000..d52c58270 --- /dev/null +++ b/src/test/ref/pointer-pointer-2.sym @@ -0,0 +1,40 @@ +(label) @1 +(label) @begin +(label) @end +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 16.5 +(byte) main::i#5 reg byte x 3.142857142857143 +(byte*) main::screen +(byte*) main::screen#1 screen zp ZP_WORD:5 101.0 +(byte*) main::screen#2 screen zp ZP_WORD:5 65.0 +(byte*) main::screen#4 screen zp ZP_WORD:5 11.0 +(byte*) main::text +(byte*) main::text#1 text zp ZP_WORD:2 202.0 +(byte*) main::text#2 text zp ZP_WORD:2 11.0 +(byte*) main::text#3 text zp ZP_WORD:2 70.99999999999999 +(void()) nexttext((byte**) nexttext::textp) +(byte~) nexttext::$0 reg byte a 2.0 +(label) nexttext::@1 +(label) nexttext::@2 +(label) nexttext::@return +(byte**) nexttext::textp +(const byte**) nexttext::textp#0 textp = &(byte*) main::text#2 +(byte[]) text1 +(const byte[]) text1#0 text1 = (string) "camelot @" +(byte[]) text2 +(const byte[]) text2#0 text2 = (string) "rex @" +(byte) textid +(byte) textid#11 textid zp ZP_BYTE:4 7.5 +(byte) textid#13 textid zp ZP_BYTE:4 1.0 + +zp ZP_WORD:2 [ main::text#2 main::text#3 main::text#1 ] +zp ZP_BYTE:4 [ textid#11 textid#13 ] +zp ZP_WORD:5 [ main::screen#4 main::screen#2 main::screen#1 ] +reg byte x [ main::i#5 main::i#1 ] +reg byte a [ nexttext::$0 ] diff --git a/src/test/ref/test-address-of-param.log b/src/test/ref/test-address-of-param.log index 44a9eb608..4629a4199 100644 --- a/src/test/ref/test-address-of-param.log +++ b/src/test/ref/test-address-of-param.log @@ -345,16 +345,18 @@ setByte: { } REGISTER UPLIFT POTENTIAL REGISTERS -Statement [17] *((byte*) setByte::ptr#3) ← (byte) setByte::b#3 [ main::b1#0 main::b2#0 main::b3#0 ] ( main:2::setByte:7 [ main::b1#0 main::b2#0 main::b3#0 ] main:2::setByte:9 [ main::b1#0 main::b2#0 main::b3#0 ] main:2::setByte:11 [ main::b1#0 main::b2#0 main::b3#0 ] ) always clobbers reg byte y -Removing always clobbered register reg byte y as potential for zp ZP_BYTE:5 [ main::b1#0 ] -Removing always clobbered register reg byte y as potential for zp ZP_BYTE:6 [ main::b2#0 ] -Removing always clobbered register reg byte y as potential for zp ZP_BYTE:7 [ main::b3#0 ] +Statement [4] (byte) main::b1#0 ← (byte/signed byte/word/signed word/dword/signed dword) 0 [ main::b1#0 ] ( main:2 [ main::b1#0 ] ) always clobbers reg byte a +Statement [5] (byte) main::b2#0 ← (byte/signed byte/word/signed word/dword/signed dword) 0 [ main::b1#0 main::b2#0 ] ( main:2 [ main::b1#0 main::b2#0 ] ) always clobbers reg byte a +Statement [6] (byte) main::b3#0 ← (byte/signed byte/word/signed word/dword/signed dword) 0 [ main::b1#0 main::b2#0 main::b3#0 ] ( main:2 [ main::b1#0 main::b2#0 main::b3#0 ] ) always clobbers reg byte a +Statement [12] *((const byte*) main::SCREEN#0) ← (byte) main::b1#0 [ main::b2#0 main::b3#0 ] ( main:2 [ main::b2#0 main::b3#0 ] ) always clobbers reg byte a +Statement [13] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 1) ← (byte) main::b2#0 [ main::b3#0 ] ( main:2 [ main::b3#0 ] ) always clobbers reg byte a +Statement [14] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 2) ← (byte) main::b3#0 [ ] ( main:2 [ ] ) always clobbers reg byte a Statement [17] *((byte*) setByte::ptr#3) ← (byte) setByte::b#3 [ main::b1#0 main::b2#0 main::b3#0 ] ( main:2::setByte:7 [ main::b1#0 main::b2#0 main::b3#0 ] main:2::setByte:9 [ main::b1#0 main::b2#0 main::b3#0 ] main:2::setByte:11 [ main::b1#0 main::b2#0 main::b3#0 ] ) always clobbers reg byte y Potential registers zp ZP_BYTE:2 [ setByte::b#3 ] : zp ZP_BYTE:2 , reg byte a , reg byte x , reg byte y , Potential registers zp ZP_WORD:3 [ setByte::ptr#3 ] : zp ZP_WORD:3 , -Potential registers zp ZP_BYTE:5 [ main::b1#0 ] : zp ZP_BYTE:5 , reg byte a , reg byte x , -Potential registers zp ZP_BYTE:6 [ main::b2#0 ] : zp ZP_BYTE:6 , reg byte a , reg byte x , -Potential registers zp ZP_BYTE:7 [ main::b3#0 ] : zp ZP_BYTE:7 , reg byte a , reg byte x , +Potential registers zp ZP_BYTE:5 [ main::b1#0 ] : zp ZP_BYTE:5 , +Potential registers zp ZP_BYTE:6 [ main::b2#0 ] : zp ZP_BYTE:6 , +Potential registers zp ZP_BYTE:7 [ main::b3#0 ] : zp ZP_BYTE:7 , REGISTER UPLIFT SCOPES Uplift Scope [setByte] 2: zp ZP_BYTE:2 [ setByte::b#3 ] 2: zp ZP_WORD:3 [ setByte::ptr#3 ]