diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index b849a12e3..02b577df4 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -7,18 +7,9 @@ - diff --git a/src/main/fragment/cache/fragment-cache-mos6502x.asm b/src/main/fragment/cache/fragment-cache-mos6502x.asm index 23aa3e4e2..11af6d8fb 100644 --- a/src/main/fragment/cache/fragment-cache-mos6502x.asm +++ b/src/main/fragment/cache/fragment-cache-mos6502x.asm @@ -15819,3 +15819,536 @@ sta {c1},x lda #{c2} ora {c1},y sta {c1},y +//FRAGMENT vbsz1=vwsz2 +lda {z2} +sta {z1} +//FRAGMENT _deref_pbuz1_eq__deref_pbuz2_then_la1 +ldy #0 +lda ({z1}),y +ldy #0 +cmp ({z2}),y +beq {la1} +//FRAGMENT vbuz1=_deref_pbuz2_minus__deref_pbuz3 +ldy #0 +lda ({z2}),y +sec +ldy #0 +sbc ({z3}),y +sta {z1} +//FRAGMENT vwsz1=_sword_vbsz2 +lda {z2} +sta {z1} +// sign-extend the byte +ora #$7f +bmi !+ +lda #0 +!: +sta {z1}+1 +//FRAGMENT vbsz1_eq_vbsc1_then_la1 +lda #{c1} +cmp {z1} +beq {la1} +//FRAGMENT vbsz1_eq_0_then_la1 +lda {z1} +cmp #0 +beq {la1} +//FRAGMENT vboz1=vbsz2_gt_vbsc1 +lda {z2} +sec +sbc #{c1} +beq !a+ +bvs !+ +eor #$80 +!: +asl +lda #0 +rol +!a: +sta {z1} +//FRAGMENT vboz1=vbsz2_eq_vbsc1 +lda {z2} +eor #{c1} +beq !+ +lda #1 +!: +eor #1 +sta {z1} +//FRAGMENT vboz1=vbsz2_lt_vbsc1 +lda {z2} +sec +sbc #{c1} +bvc !+ +eor #$80 +!: +asl +lda #0 +rol +sta {z1} +//FRAGMENT _deref_pbuz1_eq_0_then_la1 +ldy #0 +lda ({z1}),y +cmp #0 +beq {la1} +//FRAGMENT vbsaa=vwsz1 +lda {z1} +//FRAGMENT vbsxx=vwsz1 +lda {z1} +tax +//FRAGMENT vbsyy=vwsz1 +lda {z1} +tay +//FRAGMENT vbuaa=_deref_pbuz1_minus__deref_pbuz2 +ldy #0 +lda ({z1}),y +sec +ldy #0 +sbc ({z2}),y +//FRAGMENT vbuxx=_deref_pbuz1_minus__deref_pbuz2 +ldy #0 +lda ({z1}),y +sec +ldy #0 +sbc ({z2}),y +tax +//FRAGMENT vbuyy=_deref_pbuz1_minus__deref_pbuz2 +ldy #0 +lda ({z1}),y +sec +ldy #0 +sbc ({z2}),y +tay +//FRAGMENT vwsz1=_sword_vbsaa +sta {z1} +// sign-extend the byte +ora #$7f +bmi !+ +lda #0 +!: +sta {z1}+1 +//FRAGMENT vwsz1=_sword_vbsxx +txa +sta {z1} +// sign-extend the byte +ora #$7f +bmi !+ +lda #0 +!: +sta {z1}+1 +//FRAGMENT vwsz1=_sword_vbsyy +tya +sta {z1} +// sign-extend the byte +ora #$7f +bmi !+ +lda #0 +!: +sta {z1}+1 +//FRAGMENT vbsaa_eq_vbsc1_then_la1 +cmp #{c1} +beq {la1} +//FRAGMENT vbsaa_eq_0_then_la1 +cmp #0 +beq {la1} +//FRAGMENT vboz1=vbsaa_gt_vbsc1 +sec +sbc #{c1} +beq !a+ +bvs !+ +eor #$80 +!: +asl +lda #0 +rol +!a: +sta {z1} +//FRAGMENT vboz1=vbsxx_gt_vbsc1 +txa +sec +sbc #{c1} +beq !a+ +bvs !+ +eor #$80 +!: +asl +lda #0 +rol +!a: +sta {z1} +//FRAGMENT vboz1=vbsyy_gt_vbsc1 +tya +sec +sbc #{c1} +beq !a+ +bvs !+ +eor #$80 +!: +asl +lda #0 +rol +!a: +sta {z1} +//FRAGMENT vboaa=vbsz1_gt_vbsc1 +lda {z1} +sec +sbc #{c1} +beq !a+ +bvs !+ +eor #$80 +!: +asl +lda #0 +rol +!a: +//FRAGMENT vboaa=vbsaa_gt_vbsc1 +sec +sbc #{c1} +beq !a+ +bvs !+ +eor #$80 +!: +asl +lda #0 +rol +!a: +//FRAGMENT vboaa=vbsxx_gt_vbsc1 +txa +sec +sbc #{c1} +beq !a+ +bvs !+ +eor #$80 +!: +asl +lda #0 +rol +!a: +//FRAGMENT vboaa=vbsyy_gt_vbsc1 +tya +sec +sbc #{c1} +beq !a+ +bvs !+ +eor #$80 +!: +asl +lda #0 +rol +!a: +//FRAGMENT vboxx=vbsz1_gt_vbsc1 +lda {z1} +sec +sbc #{c1} +beq !a+ +bvs !+ +eor #$80 +!: +asl +lda #0 +rol +!a: +tax +//FRAGMENT vboxx=vbsaa_gt_vbsc1 +sec +sbc #{c1} +beq !a+ +bvs !+ +eor #$80 +!: +asl +lda #0 +rol +!a: +tax +//FRAGMENT vboxx=vbsxx_gt_vbsc1 +txa +sec +sbc #{c1} +beq !a+ +bvs !+ +eor #$80 +!: +asl +lda #0 +rol +!a: +tax +//FRAGMENT vboxx=vbsyy_gt_vbsc1 +tya +sec +sbc #{c1} +beq !a+ +bvs !+ +eor #$80 +!: +asl +lda #0 +rol +!a: +tax +//FRAGMENT vboyy=vbsz1_gt_vbsc1 +lda {z1} +sec +sbc #{c1} +beq !a+ +bvs !+ +eor #$80 +!: +asl +lda #0 +rol +!a: +tay +//FRAGMENT vboyy=vbsaa_gt_vbsc1 +sec +sbc #{c1} +beq !a+ +bvs !+ +eor #$80 +!: +asl +lda #0 +rol +!a: +tay +//FRAGMENT vboyy=vbsxx_gt_vbsc1 +txa +sec +sbc #{c1} +beq !a+ +bvs !+ +eor #$80 +!: +asl +lda #0 +rol +!a: +tay +//FRAGMENT vboyy=vbsyy_gt_vbsc1 +tya +sec +sbc #{c1} +beq !a+ +bvs !+ +eor #$80 +!: +asl +lda #0 +rol +!a: +tay +//FRAGMENT vboz1=vbsxx_eq_vbsc1 +txa +eor #{c1} +beq !+ +lda #1 +!: +eor #1 +sta {z1} +//FRAGMENT vboz1=vbsyy_eq_vbsc1 +tya +eor #{c1} +beq !+ +lda #1 +!: +eor #1 +sta {z1} +//FRAGMENT vboaa=vbsz1_eq_vbsc1 +lda {z1} +eor #{c1} +beq !+ +lda #1 +!: +eor #1 +//FRAGMENT vboaa=vbsxx_eq_vbsc1 +txa +eor #{c1} +beq !+ +lda #1 +!: +eor #1 +//FRAGMENT vboaa=vbsyy_eq_vbsc1 +tya +eor #{c1} +beq !+ +lda #1 +!: +eor #1 +//FRAGMENT vboxx=vbsz1_eq_vbsc1 +lda {z1} +eor #{c1} +beq !+ +lda #1 +!: +eor #1 +tax +//FRAGMENT vboxx=vbsxx_eq_vbsc1 +txa +eor #{c1} +beq !+ +lda #1 +!: +eor #1 +tax +//FRAGMENT vboxx=vbsyy_eq_vbsc1 +tya +eor #{c1} +beq !+ +lda #1 +!: +eor #1 +tax +//FRAGMENT vboyy=vbsz1_eq_vbsc1 +lda {z1} +eor #{c1} +beq !+ +lda #1 +!: +eor #1 +tay +//FRAGMENT vboyy=vbsxx_eq_vbsc1 +txa +eor #{c1} +beq !+ +lda #1 +!: +eor #1 +tay +//FRAGMENT vboyy=vbsyy_eq_vbsc1 +tya +eor #{c1} +beq !+ +lda #1 +!: +eor #1 +tay +//FRAGMENT vboz1=vbsxx_lt_vbsc1 +txa +sec +sbc #{c1} +bvc !+ +eor #$80 +!: +asl +lda #0 +rol +sta {z1} +//FRAGMENT vboz1=vbsyy_lt_vbsc1 +tya +sec +sbc #{c1} +bvc !+ +eor #$80 +!: +asl +lda #0 +rol +sta {z1} +//FRAGMENT vboaa=vbsz1_lt_vbsc1 +lda {z1} +sec +sbc #{c1} +bvc !+ +eor #$80 +!: +asl +lda #0 +rol +//FRAGMENT vboaa=vbsxx_lt_vbsc1 +txa +sec +sbc #{c1} +bvc !+ +eor #$80 +!: +asl +lda #0 +rol +//FRAGMENT vboaa=vbsyy_lt_vbsc1 +tya +sec +sbc #{c1} +bvc !+ +eor #$80 +!: +asl +lda #0 +rol +//FRAGMENT vboxx=vbsz1_lt_vbsc1 +lda {z1} +sec +sbc #{c1} +bvc !+ +eor #$80 +!: +asl +lda #0 +rol +tax +//FRAGMENT vboxx=vbsxx_lt_vbsc1 +txa +sec +sbc #{c1} +bvc !+ +eor #$80 +!: +asl +lda #0 +rol +tax +//FRAGMENT vboxx=vbsyy_lt_vbsc1 +tya +sec +sbc #{c1} +bvc !+ +eor #$80 +!: +asl +lda #0 +rol +tax +//FRAGMENT vboyy=vbsz1_lt_vbsc1 +lda {z1} +sec +sbc #{c1} +bvc !+ +eor #$80 +!: +asl +lda #0 +rol +tay +//FRAGMENT vboyy=vbsxx_lt_vbsc1 +txa +sec +sbc #{c1} +bvc !+ +eor #$80 +!: +asl +lda #0 +rol +tay +//FRAGMENT vboyy=vbsyy_lt_vbsc1 +tya +sec +sbc #{c1} +bvc !+ +eor #$80 +!: +asl +lda #0 +rol +tay +//FRAGMENT vbsxx_eq_vbsc1_then_la1 +cpx #{c1} +beq {la1} +//FRAGMENT vbsxx_eq_0_then_la1 +cpx #0 +beq {la1} +//FRAGMENT vbsyy_lt_0_then_la1 +cpy #0 +bmi {la1} +//FRAGMENT _stackidxbyte_vbuc1=vbuc2 +lda #{c2} +tsx +sta STACK_BASE+{c1},x diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index 5e7e952d8..d19b04e9e 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -230,11 +230,11 @@ public class Compiler { getLog().append(program.getScope().toStringVars(program, false)); } - new Pass1AddressOfHandling(program).execute(); - new Pass1AsmUsesHandling(program).execute(); new Pass1FixLValuesLoHi(program).execute(); new Pass1AssertNoLValueIntermediate(program).execute(); new PassNAddTypeConversionAssignment(program, true).execute(); + new Pass1AddressOfHandling(program).execute(); + new Pass1AsmUsesHandling(program).execute(); new Pass1AssertProcedureCallParameters(program).execute(); new Pass1ModifiedVarsAnalysis(program).execute(); new Pass1CallStackVarPrepare(program).execute(); diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNAddTypeConversionAssignment.java b/src/main/java/dk/camelot64/kickc/passes/PassNAddTypeConversionAssignment.java index ee4efc210..c98996872 100644 --- a/src/main/java/dk/camelot64/kickc/passes/PassNAddTypeConversionAssignment.java +++ b/src/main/java/dk/camelot64/kickc/passes/PassNAddTypeConversionAssignment.java @@ -4,14 +4,12 @@ import dk.camelot64.kickc.model.CompileError; import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.iterator.ProgramExpressionBinary; import dk.camelot64.kickc.model.iterator.ProgramExpressionIterator; -import dk.camelot64.kickc.model.types.SymbolType; -import dk.camelot64.kickc.model.types.SymbolTypeConversion; -import dk.camelot64.kickc.model.types.SymbolTypeInference; -import dk.camelot64.kickc.model.types.SymbolTypePointer; +import dk.camelot64.kickc.model.iterator.ProgramValue; +import dk.camelot64.kickc.model.types.*; +import dk.camelot64.kickc.model.values.ConstantSymbolPointer; import dk.camelot64.kickc.model.values.RValue; -import dk.camelot64.kickc.model.values.ValueList; +import dk.camelot64.kickc.model.values.SymbolRef; -import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -56,27 +54,12 @@ public class PassNAddTypeConversionAssignment extends Pass2SsaOptimization { getLog().append("Adding pointer type conversion cast to void pointer (" + leftType + ") " + binary.getRight().toString() + " in " + currentStmt.toString(getProgram(), false)); binary.addRightCast(leftType, stmtIt, currentBlock.getScope(), getProgram()); modified.set(true); - } else if(SymbolType.WORD.equals(leftType) && isLiteralWordCandidate(right)) { - // Detect word literal constructor - SymbolType conversionType = SymbolType.WORD; + } else if((leftType instanceof SymbolTypePointer) && (((SymbolTypePointer) leftType).getElementType() instanceof SymbolTypeProcedure) && rightType instanceof SymbolTypeProcedure) { + // Assigning procedure to pointer to procedure - add pointer if(!pass1 || getLog().isVerbosePass1CreateSsa()) - getLog().append("Identified literal word (" + conversionType + ") " + binary.getRight().toString() + " in " + (currentStmt == null ? "" : currentStmt.toString(getProgram(), false))); - binary.addRightCast(conversionType, stmtIt, currentBlock == null ? null : currentBlock.getScope(), getProgram()); - modified.set(true); - } else if(leftType instanceof SymbolTypePointer && isLiteralWordCandidate(right)) { - // Detect word literal constructor - SymbolType conversionType = SymbolType.WORD; - if(!pass1 || getLog().isVerbosePass1CreateSsa()) - getLog().append("Identified literal word (" + conversionType + ") " + binary.getRight().toString() + " in " + (currentStmt == null ? "" : currentStmt.toString(getProgram(), false))); - binary.addRightCast(conversionType, stmtIt, currentBlock == null ? null : currentBlock.getScope(), getProgram()); - modified.set(true); - } else if(SymbolType.DWORD.equals(leftType) && isLiteralWordCandidate(right)) { - // Detect dword literal constructor - SymbolType conversionType = SymbolType.DWORD; - if(!pass1 || getLog().isVerbosePass1CreateSsa()) - getLog().append("Identified literal word (" + conversionType + ") " + binary.getRight().toString() + " in " + (currentStmt == null ? "" : currentStmt.toString(getProgram(), false))); - binary.addRightCast(conversionType, stmtIt, currentBlock == null ? null : currentBlock.getScope(), getProgram()); - modified.set(true); + getLog().append("Adding address-of to function reference " + binary.getRight().toString() + " in " + currentStmt.toString(getProgram(), false)); + ProgramValue rightValue = binary.getRightValue(); + rightValue.set(new ConstantSymbolPointer((SymbolRef) rightValue.get())); } } } @@ -85,20 +68,4 @@ public class PassNAddTypeConversionAssignment extends Pass2SsaOptimization { return modified.get(); } - private boolean isLiteralWordCandidate(RValue rValue) { - if(rValue instanceof ValueList) { - List list = ((ValueList) rValue).getList(); - if(list.size() == 2) { - for(RValue elm : list) { - if(!SymbolType.isInteger(SymbolTypeInference.inferType(getProgram().getScope(), elm))) { - return false; - } - } - // Two integer elements - return true; - } - } - return false; - } - } diff --git a/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java b/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java index ba5b1827f..211748e14 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java +++ b/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java @@ -3393,6 +3393,11 @@ public class TestProgramsFast extends TestPrograms { compileAndCompare("function-pointer-noarg-call.c"); } + @Test + public void testFunctionPointerReturn2() throws IOException { + compileAndCompare("function-pointer-return-2.c"); + } + @Test public void testFunctionPointerReturn1() throws IOException { compileAndCompare("function-pointer-return-1.c"); diff --git a/src/test/kc/function-pointer-return-2.c b/src/test/kc/function-pointer-return-2.c new file mode 100644 index 000000000..31e81e2aa --- /dev/null +++ b/src/test/kc/function-pointer-return-2.c @@ -0,0 +1,24 @@ +// Calling a function pointer with parameters +// Reference the function without & + +char * const RASTER = (char*)0xd012; +char * const BORDER = (char*)0xd020; + +char fn1() { + return *RASTER; +} + +char fn2() { + return 0; +} + +void set_border(char (*fn)(void)) { + *BORDER = (*fn)(); +} + +void main() { + for(;;) { + set_border(fn1); + set_border(fn2); + } +} \ No newline at end of file diff --git a/src/test/ref/function-pointer-return-2.asm b/src/test/ref/function-pointer-return-2.asm new file mode 100644 index 000000000..2c9865984 --- /dev/null +++ b/src/test/ref/function-pointer-return-2.asm @@ -0,0 +1,62 @@ +// Calling a function pointer with parameters +// Reference the function without & + // Commodore 64 PRG executable file +.file [name="function-pointer-return-2.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + .const STACK_BASE = $103 + .label RASTER = $d012 + .label BORDER = $d020 +.segment Code +main: { + __b1: + // set_border(fn1) + lda #fn1 + sta.z set_border.fn+1 + jsr set_border + // set_border(fn2) + lda #fn2 + sta.z set_border.fn+1 + jsr set_border + jmp __b1 +} +fn2: { + .const OFFSET_STACK_RETURN_0 = 0 + .const return = 0 + // } + lda #return + tsx + sta STACK_BASE+OFFSET_STACK_RETURN_0,x + rts +} +fn1: { + .const OFFSET_STACK_RETURN_0 = 0 + // return *RASTER; + lda RASTER + // } + tsx + sta STACK_BASE+OFFSET_STACK_RETURN_0,x + rts +} +// set_border(byte()* zp(2) fn) +set_border: { + .label fn = 2 + // (*fn)() + pha + jsr bi_fn + pla + // *BORDER = (*fn)() + sta BORDER + // } + rts + bi_fn: + jmp (fn) +} diff --git a/src/test/ref/function-pointer-return-2.cfg b/src/test/ref/function-pointer-return-2.cfg new file mode 100644 index 000000000..2d5d04c38 --- /dev/null +++ b/src/test/ref/function-pointer-return-2.cfg @@ -0,0 +1,45 @@ + +void main() +main: scope:[main] from + [0] phi() + to:main::@1 +main::@1: scope:[main] from main main::@2 + [1] phi() + [2] call set_border + to:main::@2 +main::@2: scope:[main] from main::@1 + [3] phi() + [4] call set_border + to:main::@1 + +__stackcall byte fn2() +fn2: scope:[fn2] from + [5] phi() + to:fn2::@return +fn2::@return: scope:[fn2] from fn2 + [6] stackidx(byte,fn2::OFFSET_STACK_RETURN_0) = fn2::return#0 + [7] return + to:@return + +__stackcall byte fn1() +fn1: scope:[fn1] from + [8] fn1::return#0 = *RASTER + to:fn1::@return +fn1::@return: scope:[fn1] from fn1 + [9] stackidx(byte,fn1::OFFSET_STACK_RETURN_0) = fn1::return#0 + [10] return + to:@return + +void set_border(byte()* set_border::fn) +set_border: scope:[set_border] from main::@1 main::@2 + [11] set_border::fn#2 = phi( main::@1/&fn1, main::@2/&fn2 ) + sideeffect stackpushbytes(1) + [13] callexecute *set_border::fn#2 + [14] set_border::$0 = stackpull(byte) + to:set_border::@1 +set_border::@1: scope:[set_border] from set_border + [15] *BORDER = set_border::$0 + to:set_border::@return +set_border::@return: scope:[set_border] from set_border::@1 + [16] return + to:@return diff --git a/src/test/ref/function-pointer-return-2.log b/src/test/ref/function-pointer-return-2.log new file mode 100644 index 000000000..c0c3408d5 --- /dev/null +++ b/src/test/ref/function-pointer-return-2.log @@ -0,0 +1,485 @@ +Setting inferred __stackcall on procedure affected by address-of __stackcall byte fn1() caused by statement main::$0 = call set_border(&fn1) +Setting inferred __stackcall on procedure affected by address-of __stackcall byte fn2() caused by statement main::$1 = call set_border(&fn2) +Calling convention STACK_CALL adding prepare/execute/finalize for set_border::$0 = call *set_border::fn +Calling convention STACK_CALL adding stack return stackidx(byte,fn1::OFFSET_STACK_RETURN_0) = fn1::return +Calling convention STACK_CALL adding stack return stackidx(byte,fn2::OFFSET_STACK_RETURN_0) = fn2::return +Calling convention STACK_CALL adding stack pull set_border::$0 = stackpull(byte) + +CONTROL FLOW GRAPH SSA + +__stackcall byte fn1() +fn1: scope:[fn1] from + fn1::return#0 = *RASTER + to:fn1::@return +fn1::@return: scope:[fn1] from fn1 + fn1::return#2 = phi( fn1/fn1::return#0 ) + fn1::return#1 = fn1::return#2 + stackidx(byte,fn1::OFFSET_STACK_RETURN_0) = fn1::return#1 + return + to:@return + +__stackcall byte fn2() +fn2: scope:[fn2] from + fn2::return#0 = 0 + to:fn2::@return +fn2::@return: scope:[fn2] from fn2 + fn2::return#2 = phi( fn2/fn2::return#0 ) + fn2::return#1 = fn2::return#2 + stackidx(byte,fn2::OFFSET_STACK_RETURN_0) = fn2::return#1 + return + to:@return + +void set_border(byte()* set_border::fn) +set_border: scope:[set_border] from main::@1 main::@2 + set_border::fn#2 = phi( main::@1/set_border::fn#0, main::@2/set_border::fn#1 ) + sideeffect stackpushbytes(1) + callexecute *set_border::fn#2 + set_border::$0 = stackpull(byte) + to:set_border::@1 +set_border::@1: scope:[set_border] from set_border + *BORDER = set_border::$0 + to:set_border::@return +set_border::@return: scope:[set_border] from set_border::@1 + return + to:@return + +void main() +main: scope:[main] from __start + to:main::@1 +main::@1: scope:[main] from main main::@3 + set_border::fn#0 = &fn1 + call set_border + to:main::@2 +main::@2: scope:[main] from main::@1 + set_border::fn#1 = &fn2 + call set_border + to:main::@3 +main::@3: scope:[main] from main::@2 + to:main::@1 +main::@return: scope:[main] from + 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 byte* const BORDER = (byte*)$d020 +constant byte* const RASTER = (byte*)$d012 +constant word STACK_BASE = $103 +void __start() +__stackcall byte fn1() +constant byte fn1::OFFSET_STACK_RETURN_0 = 0 +byte fn1::return +byte fn1::return#0 +byte fn1::return#1 +byte fn1::return#2 +__stackcall byte fn2() +constant byte fn2::OFFSET_STACK_RETURN_0 = 0 +byte fn2::return +byte fn2::return#0 +byte fn2::return#1 +byte fn2::return#2 +void main() +void set_border(byte()* set_border::fn) +byte~ set_border::$0 +byte()* set_border::fn +byte()* set_border::fn#0 +byte()* set_border::fn#1 +byte()* set_border::fn#2 + +Adding number conversion cast (unumber) 0 in fn2::return#0 = 0 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast fn2::return#0 = (unumber)0 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (byte*) 53266 +Simplifying constant pointer cast (byte*) 53280 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias fn1::return#0 = fn1::return#2 fn1::return#1 +Alias fn2::return#0 = fn2::return#2 fn2::return#1 +Successful SSA optimization Pass2AliasElimination +Constant fn2::return#0 = 0 +Constant set_border::fn#0 = &fn1 +Constant set_border::fn#1 = &fn2 +Successful SSA optimization Pass2ConstantIdentification +Removing unused block main::@return +Successful SSA optimization Pass2EliminateUnusedBlocks +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 set_border::fn#0 +Inlining constant with var siblings set_border::fn#1 +Constant inlined set_border::fn#0 = &fn1 +Constant inlined set_border::fn#1 = &fn2 +Successful SSA optimization Pass2ConstantInlining +Finalized unsigned number type (byte) 1 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Adding NOP phi() at start of main +Adding NOP phi() at start of main::@1 +Adding NOP phi() at start of main::@2 +Adding NOP phi() at start of main::@3 +Adding NOP phi() at start of fn2 +CALL GRAPH +Calls in [main] to set_border:2 set_border:4 +Calls in [set_border] to null:14 + +Created 1 initial phi equivalence classes +Coalesced down to 1 phi equivalence classes +Culled Empty Block label main::@3 +Adding NOP phi() at start of main +Adding NOP phi() at start of main::@1 +Adding NOP phi() at start of main::@2 +Adding NOP phi() at start of fn2 + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] phi() + to:main::@1 +main::@1: scope:[main] from main main::@2 + [1] phi() + [2] call set_border + to:main::@2 +main::@2: scope:[main] from main::@1 + [3] phi() + [4] call set_border + to:main::@1 + +__stackcall byte fn2() +fn2: scope:[fn2] from + [5] phi() + to:fn2::@return +fn2::@return: scope:[fn2] from fn2 + [6] stackidx(byte,fn2::OFFSET_STACK_RETURN_0) = fn2::return#0 + [7] return + to:@return + +__stackcall byte fn1() +fn1: scope:[fn1] from + [8] fn1::return#0 = *RASTER + to:fn1::@return +fn1::@return: scope:[fn1] from fn1 + [9] stackidx(byte,fn1::OFFSET_STACK_RETURN_0) = fn1::return#0 + [10] return + to:@return + +void set_border(byte()* set_border::fn) +set_border: scope:[set_border] from main::@1 main::@2 + [11] set_border::fn#2 = phi( main::@1/&fn1, main::@2/&fn2 ) + sideeffect stackpushbytes(1) + [13] callexecute *set_border::fn#2 + [14] set_border::$0 = stackpull(byte) + to:set_border::@1 +set_border::@1: scope:[set_border] from set_border + [15] *BORDER = set_border::$0 + to:set_border::@return +set_border::@return: scope:[set_border] from set_border::@1 + [16] return + to:@return + + +VARIABLE REGISTER WEIGHTS +__stackcall byte fn1() +byte fn1::return +byte fn1::return#0 4.0 +__stackcall byte fn2() +byte fn2::return +void main() +void set_border(byte()* set_border::fn) +byte~ set_border::$0 202.0 +byte()* set_border::fn +byte()* set_border::fn#2 + +Initial phi equivalence classes +[ set_border::fn#2 ] +Added variable fn1::return#0 to live range equivalence class [ fn1::return#0 ] +Added variable set_border::$0 to live range equivalence class [ set_border::$0 ] +Complete equivalence classes +[ set_border::fn#2 ] +[ fn1::return#0 ] +[ set_border::$0 ] +Allocated zp[2]:2 [ set_border::fn#2 ] +Allocated zp[1]:4 [ fn1::return#0 ] +Allocated zp[1]:5 [ set_border::$0 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [6] stackidx(byte,fn2::OFFSET_STACK_RETURN_0) = fn2::return#0 [ ] ( [ ] { } ) always clobbers reg byte a reg byte x +Statement [9] stackidx(byte,fn1::OFFSET_STACK_RETURN_0) = fn1::return#0 [ ] ( [ ] { } ) always clobbers reg byte x +Statement [13] callexecute *set_border::fn#2 [ ] ( set_border:2 [ ] { } set_border:4 [ ] { } ) always clobbers reg byte a reg byte x reg byte y +Statement [14] set_border::$0 = stackpull(byte) [ set_border::$0 ] ( set_border:2 [ set_border::$0 ] { } set_border:4 [ set_border::$0 ] { } ) always clobbers reg byte a +Potential registers zp[2]:2 [ set_border::fn#2 ] : zp[2]:2 , +Potential registers zp[1]:4 [ fn1::return#0 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:5 [ set_border::$0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [set_border] 202: zp[1]:5 [ set_border::$0 ] 0: zp[2]:2 [ set_border::fn#2 ] +Uplift Scope [fn1] 4: zp[1]:4 [ fn1::return#0 ] +Uplift Scope [fn2] +Uplift Scope [main] +Uplift Scope [] + +Uplifting [set_border] best 200 combination reg byte a [ set_border::$0 ] zp[2]:2 [ set_border::fn#2 ] +Uplifting [fn1] best 194 combination reg byte a [ fn1::return#0 ] +Uplifting [fn2] best 194 combination +Uplifting [main] best 194 combination +Uplifting [] best 194 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Calling a function pointer with parameters +// Reference the function without & + // Upstart + // Commodore 64 PRG executable file +.file [name="function-pointer-return-2.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .const STACK_BASE = $103 + .label RASTER = $d012 + .label BORDER = $d020 +.segment Code + // main +main: { + // [1] phi from main main::@2 to main::@1 [phi:main/main::@2->main::@1] + __b1_from_main: + __b1_from___b2: + jmp __b1 + // main::@1 + __b1: + // [2] call set_border + // [11] phi from main::@1 to set_border [phi:main::@1->set_border] + set_border_from___b1: + // [11] phi set_border::fn#2 = &fn1 [phi:main::@1->set_border#0] -- pprz1=pprc1 + lda #fn1 + sta.z set_border.fn+1 + jsr set_border + // [3] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + __b2_from___b1: + jmp __b2 + // main::@2 + __b2: + // [4] call set_border + // [11] phi from main::@2 to set_border [phi:main::@2->set_border] + set_border_from___b2: + // [11] phi set_border::fn#2 = &fn2 [phi:main::@2->set_border#0] -- pprz1=pprc1 + lda #fn2 + sta.z set_border.fn+1 + jsr set_border + jmp __b1_from___b2 +} + // fn2 +fn2: { + .const OFFSET_STACK_RETURN_0 = 0 + .const return = 0 + jmp __breturn + // fn2::@return + __breturn: + // [6] stackidx(byte,fn2::OFFSET_STACK_RETURN_0) = fn2::return#0 -- _stackidxbyte_vbuc1=vbuc2 + lda #return + tsx + sta STACK_BASE+OFFSET_STACK_RETURN_0,x + // [7] return + rts +} + // fn1 +fn1: { + .const OFFSET_STACK_RETURN_0 = 0 + // [8] fn1::return#0 = *RASTER -- vbuaa=_deref_pbuc1 + lda RASTER + jmp __breturn + // fn1::@return + __breturn: + // [9] stackidx(byte,fn1::OFFSET_STACK_RETURN_0) = fn1::return#0 -- _stackidxbyte_vbuc1=vbuaa + tsx + sta STACK_BASE+OFFSET_STACK_RETURN_0,x + // [10] return + rts +} + // set_border +// set_border(byte()* zp(2) fn) +set_border: { + .label fn = 2 + // sideeffect stackpushbytes(1) -- _stackpushbyte_1 + pha + // [13] callexecute *set_border::fn#2 + jsr bi_fn + // [14] set_border::$0 = stackpull(byte) -- vbuaa=_stackpullbyte_ + pla + jmp __b1 + // set_border::@1 + __b1: + // [15] *BORDER = set_border::$0 -- _deref_pbuc1=vbuaa + sta BORDER + jmp __breturn + // set_border::@return + __breturn: + // [16] return + rts + bi_fn: + jmp (fn) +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __b2 +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing label __b1_from___b2 with __b1 +Removing instruction __b1_from_main: +Removing instruction __b1_from___b2: +Removing instruction set_border_from___b1: +Removing instruction __b2_from___b1: +Removing instruction set_border_from___b2: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction __b2: +Removing instruction __breturn: +Removing instruction __breturn: +Removing instruction __b1: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +constant byte* const BORDER = (byte*) 53280 +constant byte* const RASTER = (byte*) 53266 +constant word STACK_BASE = $103 +__stackcall byte fn1() +constant byte fn1::OFFSET_STACK_RETURN_0 = 0 +byte fn1::return +byte fn1::return#0 reg byte a 4.0 +__stackcall byte fn2() +constant byte fn2::OFFSET_STACK_RETURN_0 = 0 +byte fn2::return +constant byte fn2::return#0 return = 0 +void main() +void set_border(byte()* set_border::fn) +byte~ set_border::$0 reg byte a 202.0 +byte()* set_border::fn +byte()* set_border::fn#2 fn zp[2]:2 + +zp[2]:2 [ set_border::fn#2 ] +reg byte a [ fn1::return#0 ] +reg byte a [ set_border::$0 ] + + +FINAL ASSEMBLER +Score: 95 + + // File Comments +// Calling a function pointer with parameters +// Reference the function without & + // Upstart + // Commodore 64 PRG executable file +.file [name="function-pointer-return-2.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .const STACK_BASE = $103 + .label RASTER = $d012 + .label BORDER = $d020 +.segment Code + // main +main: { + // [1] phi from main main::@2 to main::@1 [phi:main/main::@2->main::@1] + // main::@1 + __b1: + // set_border(fn1) + // [2] call set_border + // [11] phi from main::@1 to set_border [phi:main::@1->set_border] + // [11] phi set_border::fn#2 = &fn1 [phi:main::@1->set_border#0] -- pprz1=pprc1 + lda #fn1 + sta.z set_border.fn+1 + jsr set_border + // [3] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + // main::@2 + // set_border(fn2) + // [4] call set_border + // [11] phi from main::@2 to set_border [phi:main::@2->set_border] + // [11] phi set_border::fn#2 = &fn2 [phi:main::@2->set_border#0] -- pprz1=pprc1 + lda #fn2 + sta.z set_border.fn+1 + jsr set_border + jmp __b1 +} + // fn2 +fn2: { + .const OFFSET_STACK_RETURN_0 = 0 + .const return = 0 + // fn2::@return + // } + // [6] stackidx(byte,fn2::OFFSET_STACK_RETURN_0) = fn2::return#0 -- _stackidxbyte_vbuc1=vbuc2 + lda #return + tsx + sta STACK_BASE+OFFSET_STACK_RETURN_0,x + // [7] return + rts +} + // fn1 +fn1: { + .const OFFSET_STACK_RETURN_0 = 0 + // return *RASTER; + // [8] fn1::return#0 = *RASTER -- vbuaa=_deref_pbuc1 + lda RASTER + // fn1::@return + // } + // [9] stackidx(byte,fn1::OFFSET_STACK_RETURN_0) = fn1::return#0 -- _stackidxbyte_vbuc1=vbuaa + tsx + sta STACK_BASE+OFFSET_STACK_RETURN_0,x + // [10] return + rts +} + // set_border +// set_border(byte()* zp(2) fn) +set_border: { + .label fn = 2 + // (*fn)() + // sideeffect stackpushbytes(1) -- _stackpushbyte_1 + pha + // [13] callexecute *set_border::fn#2 + jsr bi_fn + // [14] set_border::$0 = stackpull(byte) -- vbuaa=_stackpullbyte_ + pla + // set_border::@1 + // *BORDER = (*fn)() + // [15] *BORDER = set_border::$0 -- _deref_pbuc1=vbuaa + sta BORDER + // set_border::@return + // } + // [16] return + rts + bi_fn: + jmp (fn) +} + // File Data + diff --git a/src/test/ref/function-pointer-return-2.sym b/src/test/ref/function-pointer-return-2.sym new file mode 100644 index 000000000..5fe4b3f5c --- /dev/null +++ b/src/test/ref/function-pointer-return-2.sym @@ -0,0 +1,20 @@ +constant byte* const BORDER = (byte*) 53280 +constant byte* const RASTER = (byte*) 53266 +constant word STACK_BASE = $103 +__stackcall byte fn1() +constant byte fn1::OFFSET_STACK_RETURN_0 = 0 +byte fn1::return +byte fn1::return#0 reg byte a 4.0 +__stackcall byte fn2() +constant byte fn2::OFFSET_STACK_RETURN_0 = 0 +byte fn2::return +constant byte fn2::return#0 return = 0 +void main() +void set_border(byte()* set_border::fn) +byte~ set_border::$0 reg byte a 202.0 +byte()* set_border::fn +byte()* set_border::fn#2 fn zp[2]:2 + +zp[2]:2 [ set_border::fn#2 ] +reg byte a [ fn1::return#0 ] +reg byte a [ set_border::$0 ] diff --git a/src/test/ref/pointer-void-1.log b/src/test/ref/pointer-void-1.log index 958104f2c..1531aa3c5 100644 --- a/src/test/ref/pointer-void-1.log +++ b/src/test/ref/pointer-void-1.log @@ -1,7 +1,7 @@ -Setting inferred volatile on symbol affected by address-of main::vb = &main::b -Setting inferred volatile on symbol affected by address-of main::vw = &main::w -Setting inferred volatile on symbol affected by address-of main::vd = &main::d -Inlined call call __init +Setting inferred volatile on symbol affected by address-of main::vb = ((void*)) &main::b +Setting inferred volatile on symbol affected by address-of main::vw = ((void*)) &main::w +Setting inferred volatile on symbol affected by address-of main::vd = ((void*)) &main::d +Inlined call call __init CONTROL FLOW GRAPH SSA @@ -12,19 +12,19 @@ main: scope:[main] from __start::@1 main::w = $1234 main::b = $12 print::ptr#0 = main::vb - call print + call print to:main::@1 main::@1: scope:[main] from main idx#9 = phi( main/idx#5 ) idx#0 = idx#9 print::ptr#1 = main::vw - call print + call print to:main::@2 main::@2: scope:[main] from main::@1 idx#10 = phi( main::@1/idx#5 ) idx#1 = idx#10 print::ptr#2 = main::vd - call print + call print to:main::@3 main::@3: scope:[main] from main::@2 idx#11 = phi( main::@2/idx#5 ) @@ -57,7 +57,7 @@ __start::__init1: scope:[__start] from __start to:__start::@1 __start::@1: scope:[__start] from __start::__init1 idx#18 = phi( __start::__init1/idx#6 ) - call main + call main to:__start::@2 __start::@2: scope:[__start] from __start::@1 idx#15 = phi( __start::@1/idx#3 ) @@ -161,15 +161,15 @@ main: scope:[main] from [0] main::d = $12345678 [1] main::w = $1234 [2] main::b = $12 - [3] call print + [3] call print to:main::@1 main::@1: scope:[main] from main [4] phi() - [5] call print + [5] call print to:main::@2 main::@2: scope:[main] from main::@1 [6] phi() - [7] call print + [7] call print to:main::@return main::@return: scope:[main] from main::@2 [8] return @@ -287,7 +287,7 @@ main: { // [2] main::b = $12 -- vbuz1=vbuc1 lda #$12 sta.z b - // [3] call print + // [3] call print // [9] phi from main to print [phi:main->print] print_from_main: // [9] phi idx#13 = 0 [phi:main->print#0] -- vbuxx=vbuc1 @@ -303,7 +303,7 @@ main: { jmp __b1 // main::@1 __b1: - // [5] call print + // [5] call print // [9] phi from main::@1 to print [phi:main::@1->print] print_from___b1: // [9] phi idx#13 = idx#14 [phi:main::@1->print#0] -- register_copy @@ -318,7 +318,7 @@ main: { jmp __b2 // main::@2 __b2: - // [7] call print + // [7] call print // [9] phi from main::@2 to print [phi:main::@2->print] print_from___b2: // [9] phi idx#13 = idx#14 [phi:main::@2->print#0] -- register_copy @@ -439,7 +439,7 @@ main: { lda #$12 sta.z b // print(vb) - // [3] call print + // [3] call print // [9] phi from main to print [phi:main->print] // [9] phi idx#13 = 0 [phi:main->print#0] -- vbuxx=vbuc1 ldx #0 @@ -452,7 +452,7 @@ main: { // [4] phi from main to main::@1 [phi:main->main::@1] // main::@1 // print(vw) - // [5] call print + // [5] call print // [9] phi from main::@1 to print [phi:main::@1->print] // [9] phi idx#13 = idx#14 [phi:main::@1->print#0] -- register_copy // [9] phi print::ptr#3 = main::vw [phi:main::@1->print#1] -- pvoz1=pvoc1 @@ -464,7 +464,7 @@ main: { // [6] phi from main::@1 to main::@2 [phi:main::@1->main::@2] // main::@2 // print(vd) - // [7] call print + // [7] call print // [9] phi from main::@2 to print [phi:main::@2->print] // [9] phi idx#13 = idx#14 [phi:main::@2->print#0] -- register_copy // [9] phi print::ptr#3 = main::vd [phi:main::@2->print#1] -- pvoz1=pvoc1 diff --git a/src/test/ref/pointer-void-2.log b/src/test/ref/pointer-void-2.log index e458efb75..e24b6e975 100644 --- a/src/test/ref/pointer-void-2.log +++ b/src/test/ref/pointer-void-2.log @@ -1,7 +1,7 @@ -Setting inferred volatile on symbol affected by address-of main::$0 = call print(&main::b) -Setting inferred volatile on symbol affected by address-of main::$1 = call print(&main::w) -Setting inferred volatile on symbol affected by address-of main::$2 = call print(&main::d) -Inlined call call __init +Setting inferred volatile on symbol affected by address-of main::$0 = call print((void*)&main::b) +Setting inferred volatile on symbol affected by address-of main::$1 = call print((void*)&main::w) +Setting inferred volatile on symbol affected by address-of main::$2 = call print((void*)&main::d) +Inlined call call __init CONTROL FLOW GRAPH SSA @@ -12,19 +12,19 @@ main: scope:[main] from __start::@1 main::w = $1234 main::b = $12 print::ptr#0 = (void*)&main::b - call print + call print to:main::@1 main::@1: scope:[main] from main idx#9 = phi( main/idx#5 ) idx#0 = idx#9 print::ptr#1 = (void*)&main::w - call print + call print to:main::@2 main::@2: scope:[main] from main::@1 idx#10 = phi( main::@1/idx#5 ) idx#1 = idx#10 print::ptr#2 = (void*)&main::d - call print + call print to:main::@3 main::@3: scope:[main] from main::@2 idx#11 = phi( main::@2/idx#5 ) @@ -57,7 +57,7 @@ __start::__init1: scope:[__start] from __start to:__start::@1 __start::@1: scope:[__start] from __start::__init1 idx#18 = phi( __start::__init1/idx#6 ) - call main + call main to:__start::@2 __start::@2: scope:[__start] from __start::@1 idx#15 = phi( __start::@1/idx#3 ) @@ -158,15 +158,15 @@ main: scope:[main] from [0] main::d = $12345678 [1] main::w = $1234 [2] main::b = $12 - [3] call print + [3] call print to:main::@1 main::@1: scope:[main] from main [4] phi() - [5] call print + [5] call print to:main::@2 main::@2: scope:[main] from main::@1 [6] phi() - [7] call print + [7] call print to:main::@return main::@return: scope:[main] from main::@2 [8] return @@ -281,7 +281,7 @@ main: { // [2] main::b = $12 -- vbuz1=vbuc1 lda #$12 sta.z b - // [3] call print + // [3] call print // [9] phi from main to print [phi:main->print] print_from_main: // [9] phi idx#13 = 0 [phi:main->print#0] -- vbuxx=vbuc1 @@ -297,7 +297,7 @@ main: { jmp __b1 // main::@1 __b1: - // [5] call print + // [5] call print // [9] phi from main::@1 to print [phi:main::@1->print] print_from___b1: // [9] phi idx#13 = idx#14 [phi:main::@1->print#0] -- register_copy @@ -312,7 +312,7 @@ main: { jmp __b2 // main::@2 __b2: - // [7] call print + // [7] call print // [9] phi from main::@2 to print [phi:main::@2->print] print_from___b2: // [9] phi idx#13 = idx#14 [phi:main::@2->print#0] -- register_copy @@ -427,7 +427,7 @@ main: { lda #$12 sta.z b // print(&b) - // [3] call print + // [3] call print // [9] phi from main to print [phi:main->print] // [9] phi idx#13 = 0 [phi:main->print#0] -- vbuxx=vbuc1 ldx #0 @@ -440,7 +440,7 @@ main: { // [4] phi from main to main::@1 [phi:main->main::@1] // main::@1 // print(&w) - // [5] call print + // [5] call print // [9] phi from main::@1 to print [phi:main::@1->print] // [9] phi idx#13 = idx#14 [phi:main::@1->print#0] -- register_copy // [9] phi print::ptr#3 = (void*)&main::w [phi:main::@1->print#1] -- pvoz1=pvoc1 @@ -452,7 +452,7 @@ main: { // [6] phi from main::@1 to main::@2 [phi:main::@1->main::@2] // main::@2 // print(&d) - // [7] call print + // [7] call print // [9] phi from main::@2 to print [phi:main::@2->print] // [9] phi idx#13 = idx#14 [phi:main::@2->print#0] -- register_copy // [9] phi print::ptr#3 = (void*)&main::d [phi:main::@2->print#1] -- pvoz1=pvoc1