From 00c9b47481def9507a69cce37d2278521cebee66 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Mon, 26 Aug 2019 19:31:55 +0200 Subject: [PATCH] Added support for specifying that inline kickasm uses a procedure - preventing it from being culled. Closes #294 --- .../kickc/model/iterator/ProgramValue.java | 2 +- .../Pass1EliminateUncalledProcedures.java | 1 + .../passes/Pass2ConstantIdentification.java | 14 +- .../dk/camelot64/kickc/test/TestPrograms.java | 5 + src/test/kc/kickasm-uses-prevent-deletion.kc | 28 ++ .../ref/kickasm-uses-prevent-deletion.asm | 27 ++ .../ref/kickasm-uses-prevent-deletion.cfg | 28 ++ .../ref/kickasm-uses-prevent-deletion.log | 363 ++++++++++++++++++ .../ref/kickasm-uses-prevent-deletion.sym | 16 + 9 files changed, 476 insertions(+), 8 deletions(-) create mode 100644 src/test/kc/kickasm-uses-prevent-deletion.kc create mode 100644 src/test/ref/kickasm-uses-prevent-deletion.asm create mode 100644 src/test/ref/kickasm-uses-prevent-deletion.cfg create mode 100644 src/test/ref/kickasm-uses-prevent-deletion.log create mode 100644 src/test/ref/kickasm-uses-prevent-deletion.sym diff --git a/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValue.java b/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValue.java index 16e4dc247..ba282792e 100644 --- a/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValue.java +++ b/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValue.java @@ -288,7 +288,7 @@ public interface ProgramValue { @Override public void set(Value value) { - statementKickAsm.getUses().set(idx, (SymbolVariableRef) value); + statementKickAsm.getUses().set(idx, (SymbolRef) value); } } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1EliminateUncalledProcedures.java b/src/main/java/dk/camelot64/kickc/passes/Pass1EliminateUncalledProcedures.java index a3feb6070..64043fcc3 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1EliminateUncalledProcedures.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1EliminateUncalledProcedures.java @@ -35,6 +35,7 @@ public class Pass1EliminateUncalledProcedures extends Pass1Base { Set unusedProcedures = new LinkedHashSet<>(); Collection allProcedures = getProgram().getScope().getAllProcedures(true); for(Procedure procedure : allProcedures) { + // TODO Also look at kickasm/asm uses! (Maybe also look at some directive like "export" ) if(!calledProcedures.contains(procedure.getRef()) && !Pass2ConstantIdentification.isAddressOfUsed(procedure.getRef(), getProgram())) { // The procedure is not used - mark for removal! unusedProcedures.add(procedure.getRef()); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java index a0886a446..afe14a284 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java @@ -1,6 +1,9 @@ package dk.camelot64.kickc.passes; -import dk.camelot64.kickc.model.*; +import dk.camelot64.kickc.model.CompileError; +import dk.camelot64.kickc.model.ConstantNotLiteral; +import dk.camelot64.kickc.model.ControlFlowBlock; +import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.iterator.ProgramValueIterator; import dk.camelot64.kickc.model.operators.OperatorBinary; import dk.camelot64.kickc.model.operators.OperatorUnary; @@ -77,7 +80,7 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization { variableType, constVal, variable.getDataSegment() - ); + ); constantVar.setInferredType(variable.isInferredType()); constantVar.setDeclaredAlignment(variable.getDeclaredAlignment()); @@ -286,11 +289,8 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization { // Examine all program values in expressions ProgramValueIterator.execute(program, (programValue, currentStmt, stmtIt, currentBlock) -> { Value value = programValue.get(); - if(value instanceof ConstantSymbolPointer) { - ConstantSymbolPointer constantSymbolPointer = (ConstantSymbolPointer) value; - if(constantSymbolPointer.getToSymbol().equals(procedureRef)) { - found[0] = true; - } + if(value instanceof ProcedureRef && value.equals(procedureRef)) { + found[0] = true; } }); if(found[0]) { diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index dcbb3418e..997fd77b8 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -36,6 +36,11 @@ public class TestPrograms { public TestPrograms() { } + @Test + public void testKickasmUsesPreventDeletion() throws IOException, URISyntaxException { + compileAndCompare("kickasm-uses-prevent-deletion"); + } + @Test public void testBitmapCircle() throws IOException, URISyntaxException { compileAndCompare("bitmap-circle"); diff --git a/src/test/kc/kickasm-uses-prevent-deletion.kc b/src/test/kc/kickasm-uses-prevent-deletion.kc new file mode 100644 index 000000000..3a1424f06 --- /dev/null +++ b/src/test/kc/kickasm-uses-prevent-deletion.kc @@ -0,0 +1,28 @@ +// Ensure that an inline kickasm uses-clause is anough to prevent a function from being deleted + +// The vector used when the KERNAL serves IRQ interrupts +const void()** KERNEL_IRQ = $0314; +const byte* BGCOL = $d021; +const byte BLACK = $0; +const byte WHITE = $1; + + +void main() { + kickasm(uses irq, uses KERNEL_IRQ) {{ + sei + lda #irq; + sta KERNEL_IRQ+1 + cli + }} +} + + +// The Interrupt Handler +interrupt(kernel_keyboard) void irq() { + *BGCOL = WHITE; + *BGCOL = BLACK; +} + + diff --git a/src/test/ref/kickasm-uses-prevent-deletion.asm b/src/test/ref/kickasm-uses-prevent-deletion.asm new file mode 100644 index 000000000..0c5d58f0f --- /dev/null +++ b/src/test/ref/kickasm-uses-prevent-deletion.asm @@ -0,0 +1,27 @@ +// Ensure that an inline kickasm uses-clause is anough to prevent a function from being deleted +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // The vector used when the KERNAL serves IRQ interrupts + .label KERNEL_IRQ = $314 + .label BGCOL = $d021 + .const BLACK = 0 + .const WHITE = 1 +main: { + sei + lda #irq; + sta KERNEL_IRQ+1 + cli + + rts +} +// The Interrupt Handler +irq: { + lda #WHITE + sta BGCOL + lda #BLACK + sta BGCOL + jmp $ea31 +} diff --git a/src/test/ref/kickasm-uses-prevent-deletion.cfg b/src/test/ref/kickasm-uses-prevent-deletion.cfg new file mode 100644 index 000000000..694375c16 --- /dev/null +++ b/src/test/ref/kickasm-uses-prevent-deletion.cfg @@ -0,0 +1,28 @@ +@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 + kickasm( uses irq uses KERNEL_IRQ#0) {{ sei + lda #irq; + sta KERNEL_IRQ+1 + cli + }} + to:main::@return +main::@return: scope:[main] from main + [5] return + to:@return +irq: scope:[irq] from + [6] *((const byte*) BGCOL#0) ← (const byte) WHITE#0 + [7] *((const byte*) BGCOL#0) ← (const byte) BLACK#0 + to:irq::@return +irq::@return: scope:[irq] from irq + [8] return + to:@return diff --git a/src/test/ref/kickasm-uses-prevent-deletion.log b/src/test/ref/kickasm-uses-prevent-deletion.log new file mode 100644 index 000000000..d712c21ef --- /dev/null +++ b/src/test/ref/kickasm-uses-prevent-deletion.log @@ -0,0 +1,363 @@ +Resolved forward reference irq to interrupt(KERNEL_KEYBOARD)(void()) irq() +Culled Empty Block (label) @1 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (void()**) KERNEL_IRQ#0 ← ((void()**)) (number) $314 + (byte*) BGCOL#0 ← ((byte*)) (number) $d021 + (byte) BLACK#0 ← (number) 0 + (byte) WHITE#0 ← (number) 1 + to:@2 +main: scope:[main] from @2 + kickasm( uses irq uses KERNEL_IRQ#0) {{ sei + lda #irq; + sta KERNEL_IRQ+1 + cli + }} + to:main::@return +main::@return: scope:[main] from main + return + to:@return +irq: scope:[irq] from + *((byte*) BGCOL#0) ← (byte) WHITE#0 + *((byte*) BGCOL#0) ← (byte) BLACK#0 + to:irq::@return +irq::@return: scope:[irq] from irq + return + to:@return +@2: scope:[] from @begin + call main + to:@3 +@3: scope:[] from @2 + to:@end +@end: scope:[] from @3 + +SYMBOL TABLE SSA +(label) @2 +(label) @3 +(label) @begin +(label) @end +(byte*) BGCOL +(byte*) BGCOL#0 +(byte) BLACK +(byte) BLACK#0 +(void()**) KERNEL_IRQ +(void()**) KERNEL_IRQ#0 +(byte) WHITE +(byte) WHITE#0 +interrupt(KERNEL_KEYBOARD)(void()) irq() +(label) irq::@return +(void()) main() +(label) main::@return + +Adding number conversion cast (unumber) 0 in (byte) BLACK#0 ← (number) 0 +Adding number conversion cast (unumber) 1 in (byte) WHITE#0 ← (number) 1 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (void()**) KERNEL_IRQ#0 ← (void()**)(number) $314 +Inlining cast (byte*) BGCOL#0 ← (byte*)(number) $d021 +Inlining cast (byte) BLACK#0 ← (unumber)(number) 0 +Inlining cast (byte) WHITE#0 ← (unumber)(number) 1 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (void()**) 788 +Simplifying constant pointer cast (byte*) 53281 +Simplifying constant integer cast 0 +Simplifying constant integer cast 1 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 1 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Constant (const void()**) KERNEL_IRQ#0 = (void()**) 788 +Constant (const byte*) BGCOL#0 = (byte*) 53281 +Constant (const byte) BLACK#0 = 0 +Constant (const byte) WHITE#0 = 1 +Successful SSA optimization Pass2ConstantIdentification +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @3 +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 +Culled Empty Block (label) @3 +Renumbering block @2 to @1 +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 + kickasm( uses irq uses KERNEL_IRQ#0) {{ sei + lda #irq; + sta KERNEL_IRQ+1 + cli + }} + to:main::@return +main::@return: scope:[main] from main + [5] return + to:@return +irq: scope:[irq] from + [6] *((const byte*) BGCOL#0) ← (const byte) WHITE#0 + [7] *((const byte*) BGCOL#0) ← (const byte) BLACK#0 + to:irq::@return +irq::@return: scope:[irq] from irq + [8] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(byte*) BGCOL +(byte) BLACK +(void()**) KERNEL_IRQ +(byte) WHITE +interrupt(KERNEL_KEYBOARD)(void()) irq() +(void()) main() + +Initial phi equivalence classes +Complete equivalence classes + +INITIAL ASM +Target platform is c64basic + // File Comments +// Ensure that an inline kickasm uses-clause is anough to prevent a function from being deleted + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + // The vector used when the KERNAL serves IRQ interrupts + .label KERNEL_IRQ = $314 + .label BGCOL = $d021 + .const BLACK = 0 + .const WHITE = 1 + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + // kickasm( uses irq uses KERNEL_IRQ#0) {{ sei lda #irq; sta KERNEL_IRQ+1 cli }} + sei + lda #irq; + sta KERNEL_IRQ+1 + cli + + jmp breturn + // main::@return + breturn: + // [5] return + rts +} + // irq +// The Interrupt Handler +irq: { + // entry interrupt(KERNEL_KEYBOARD) + // [6] *((const byte*) BGCOL#0) ← (const byte) WHITE#0 -- _deref_pbuc1=vbuc2 + lda #WHITE + sta BGCOL + // [7] *((const byte*) BGCOL#0) ← (const byte) BLACK#0 -- _deref_pbuc1=vbuc2 + lda #BLACK + sta BGCOL + jmp breturn + // irq::@return + breturn: + // [8] return - exit interrupt(KERNEL_KEYBOARD) + jmp $ea31 +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [6] *((const byte*) BGCOL#0) ← (const byte) WHITE#0 [ ] ( [ ] ) always clobbers reg byte a +Statement [7] *((const byte*) BGCOL#0) ← (const byte) BLACK#0 [ ] ( [ ] ) always clobbers reg byte a + +REGISTER UPLIFT SCOPES +Uplift Scope [main] +Uplift Scope [irq] +Uplift Scope [] + +Uplifting [main] best 295 combination +Uplifting [irq] best 295 combination +Uplifting [] best 295 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Ensure that an inline kickasm uses-clause is anough to prevent a function from being deleted + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + // The vector used when the KERNAL serves IRQ interrupts + .label KERNEL_IRQ = $314 + .label BGCOL = $d021 + .const BLACK = 0 + .const WHITE = 1 + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + // kickasm( uses irq uses KERNEL_IRQ#0) {{ sei lda #irq; sta KERNEL_IRQ+1 cli }} + sei + lda #irq; + sta KERNEL_IRQ+1 + cli + + jmp breturn + // main::@return + breturn: + // [5] return + rts +} + // irq +// The Interrupt Handler +irq: { + // entry interrupt(KERNEL_KEYBOARD) + // [6] *((const byte*) BGCOL#0) ← (const byte) WHITE#0 -- _deref_pbuc1=vbuc2 + lda #WHITE + sta BGCOL + // [7] *((const byte*) BGCOL#0) ← (const byte) BLACK#0 -- _deref_pbuc1=vbuc2 + lda #BLACK + sta BGCOL + jmp breturn + // irq::@return + breturn: + // [8] return - exit interrupt(KERNEL_KEYBOARD) + jmp $ea31 +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp breturn +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: +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 +(byte*) BGCOL +(const byte*) BGCOL#0 BGCOL = (byte*) 53281 +(byte) BLACK +(const byte) BLACK#0 BLACK = (byte) 0 +(void()**) KERNEL_IRQ +(const void()**) KERNEL_IRQ#0 KERNEL_IRQ = (void()**) 788 +(byte) WHITE +(const byte) WHITE#0 WHITE = (byte) 1 +interrupt(KERNEL_KEYBOARD)(void()) irq() +(label) irq::@return +(void()) main() +(label) main::@return + + + +FINAL ASSEMBLER +Score: 277 + + // File Comments +// Ensure that an inline kickasm uses-clause is anough to prevent a function from being deleted + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + // The vector used when the KERNAL serves IRQ interrupts + .label KERNEL_IRQ = $314 + .label BGCOL = $d021 + .const BLACK = 0 + .const WHITE = 1 + // @begin + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] call main + // [3] phi from @1 to @end [phi:@1->@end] + // @end + // main +main: { + // kickasm + // kickasm( uses irq uses KERNEL_IRQ#0) {{ sei lda #irq; sta KERNEL_IRQ+1 cli }} + sei + lda #irq; + sta KERNEL_IRQ+1 + cli + + // main::@return + // } + // [5] return + rts +} + // irq +// The Interrupt Handler +irq: { + // entry interrupt(KERNEL_KEYBOARD) + // *BGCOL = WHITE + // [6] *((const byte*) BGCOL#0) ← (const byte) WHITE#0 -- _deref_pbuc1=vbuc2 + lda #WHITE + sta BGCOL + // *BGCOL = BLACK + // [7] *((const byte*) BGCOL#0) ← (const byte) BLACK#0 -- _deref_pbuc1=vbuc2 + lda #BLACK + sta BGCOL + // irq::@return + // } + // [8] return - exit interrupt(KERNEL_KEYBOARD) + jmp $ea31 +} + // File Data + diff --git a/src/test/ref/kickasm-uses-prevent-deletion.sym b/src/test/ref/kickasm-uses-prevent-deletion.sym new file mode 100644 index 000000000..7d936b35c --- /dev/null +++ b/src/test/ref/kickasm-uses-prevent-deletion.sym @@ -0,0 +1,16 @@ +(label) @1 +(label) @begin +(label) @end +(byte*) BGCOL +(const byte*) BGCOL#0 BGCOL = (byte*) 53281 +(byte) BLACK +(const byte) BLACK#0 BLACK = (byte) 0 +(void()**) KERNEL_IRQ +(const void()**) KERNEL_IRQ#0 KERNEL_IRQ = (void()**) 788 +(byte) WHITE +(const byte) WHITE#0 WHITE = (byte) 1 +interrupt(KERNEL_KEYBOARD)(void()) irq() +(label) irq::@return +(void()) main() +(label) main::@return +