1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2025-01-16 08:33:37 +00:00

Fixed problem where zeropage allocations inside IRQ and outside could overlap. Closes #154

This commit is contained in:
jespergravgaard 2019-03-29 08:01:02 +01:00
parent c81147f60f
commit 7fcb6e525a
16 changed files with 2933 additions and 35 deletions

View File

@ -7,6 +7,7 @@ import dk.camelot64.kickc.model.StatementSequence;
import dk.camelot64.kickc.model.statements.StatementCall;
import dk.camelot64.kickc.model.statements.StatementSource;
import dk.camelot64.kickc.model.symbols.Variable;
import dk.camelot64.kickc.model.values.SymbolRef;
import dk.camelot64.kickc.parser.KickCLexer;
import dk.camelot64.kickc.parser.KickCParser;
import dk.camelot64.kickc.passes.*;
@ -126,7 +127,7 @@ public class Compiler {
loadAndParseFile(fileName, program, currentPath.toPath());
StatementSequence sequence = program.getStatementSequence();
sequence.addStatement(new StatementCall(null, "main", new ArrayList<>(), new StatementSource(RuleContext.EMPTY), Comment.NO_COMMENTS));
sequence.addStatement(new StatementCall(null, SymbolRef.MAIN_PROC_NAME, new ArrayList<>(), new StatementSource(RuleContext.EMPTY), Comment.NO_COMMENTS));
program.setStatementSequence(sequence);
pass1GenerateSSA();

View File

@ -107,8 +107,6 @@ public class CallGraph {
* This includes the recursive closure of calls (ie. sub-calls and their sub-calls).
* @param scopeRef The scope (procedure/root) to examine
* @param found The scopes already found
*
* @return All scopes called in the closure of calls
*/
private void addRecursiveCalls(ScopeRef scopeRef, Collection<ScopeRef> found) {
if(found.contains(scopeRef)) {
@ -124,8 +122,6 @@ public class CallGraph {
}
}
@Override
public String toString() {
StringBuilder out = new StringBuilder();
@ -153,6 +149,55 @@ public class CallGraph {
return callers;
}
/**
* Get all procedures containing calls of a specific procedure
*
* @param label The label of the procedure
* @return All calls
*/
public Collection<ScopeRef> getCallerProcs(ScopeRef label) {
Collection<ScopeRef> callers = new ArrayList<>();
for(CallBlock callBlock : callBlocks) {
for(CallBlock.Call call : callBlock.getCalls()) {
if(call.getProcedure().equals(label)) {
callers.add(callBlock.getScopeLabel());
}
}
}
return callers;
}
/**
* Get the closure of all procedures calling a specific scope.
* This includes the recursive closure of callers (ie. callers and their callers).
* @param scopeRef The scope (procedure/root) to examine
* @return All scopes calling the passed scope (potentially through other callers)
*/
public Collection<ScopeRef> getRecursiveCallers(ScopeRef scopeRef) {
ArrayList<ScopeRef> closure = new ArrayList<>();
addRecursiveCallers(scopeRef, closure);
return closure;
}
/**
* Get the closure of all procedures calling a specific scope.
* This includes the recursive closure of callers (ie. callers and their callers).
* @param scopeRef The scope (procedure/root) to examine
* @param found The scopes already found
* */
private void addRecursiveCallers(ScopeRef scopeRef, Collection<ScopeRef> found) {
if(found.contains(scopeRef)) {
// Recursion detected - stop here
return;
}
found.add(scopeRef);
Collection<ScopeRef> callerProcs = getCallerProcs(scopeRef);
for(ScopeRef callerProc : callerProcs) {
addRecursiveCallers(callerProc, found);
}
}
/**
* A block in the call graph, matching a scope in the program.
*/

View File

@ -8,6 +8,7 @@ import dk.camelot64.kickc.model.symbols.Label;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.values.LabelRef;
import dk.camelot64.kickc.model.values.ScopeRef;
import dk.camelot64.kickc.model.values.SymbolRef;
import dk.camelot64.kickc.model.values.VariableRef;
import dk.camelot64.kickc.passes.Pass2ConstantIdentification;
@ -172,14 +173,13 @@ public class ControlFlowGraph {
public ControlFlowBlock getMainBlock() {
for(ControlFlowBlock block : getAllBlocks()) {
LabelRef label = block.getLabel();
if(label.getFullName().equals("main")) {
if(label.getFullName().equals(SymbolRef.MAIN_PROC_NAME)) {
return block;
}
}
return null;
}
/**
* Get all blocks that are program entry points. This is the main-block and any blocks referenced by the address-off operator (&)
* @param program The program

View File

@ -251,7 +251,7 @@ public class Pass1GenerateSingleStaticAssignmentForm extends Pass1Base {
if(symbol instanceof Procedure) {
if(((Procedure) symbol).getInterruptType()!=null) {
// Find all root-level predecessors to the main block
ControlFlowBlock mainBlock = getGraph().getBlock(new LabelRef("main"));
ControlFlowBlock mainBlock = getGraph().getBlock(new LabelRef(SymbolRef.MAIN_PROC_NAME));
List<ControlFlowBlock> mainPredecessors = getGraph().getPredecessors(mainBlock);
for(ControlFlowBlock mainPredecessor : mainPredecessors) {
if(mainPredecessor.getScope().equals(ScopeRef.ROOT)) {

View File

@ -1,10 +1,15 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.symbols.ProgramScope;
import dk.camelot64.kickc.model.symbols.Variable;
import dk.camelot64.kickc.model.symbols.VariableUnversioned;
import dk.camelot64.kickc.model.values.ScopeRef;
import dk.camelot64.kickc.model.values.SymbolRef;
import dk.camelot64.kickc.model.values.VariableRef;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
@ -21,9 +26,10 @@ public class Pass4ZeroPageCoalesce extends Pass2Base {
public void coalesce() {
LinkedHashSet<String> unknownFragments = new LinkedHashSet<>();
LiveRangeEquivalenceClassSet liveRangeEquivalenceClassSet = getProgram().getLiveRangeEquivalenceClassSet();
Collection<ScopeRef> threads = getThreadHeads(getSymbols());
boolean change;
do {
change = coalesce(liveRangeEquivalenceClassSet, unknownFragments);
change = coalesce(liveRangeEquivalenceClassSet, threads, unknownFragments);
} while(change);
if(unknownFragments.size() > 0) {
@ -35,6 +41,26 @@ public class Pass4ZeroPageCoalesce extends Pass2Base {
}
/**
* Get all functions that represents a separate thread in the program.
* Each interrupt function and the main() function are threads.
*
* @return The threads.
*/
public static Collection<ScopeRef> getThreadHeads(ProgramScope programScope) {
ArrayList<ScopeRef> threadHeads = new ArrayList<>();
Collection<Procedure> procedures = programScope.getAllProcedures(true);
for(Procedure procedure : procedures) {
if(procedure.getInterruptType() != null) {
threadHeads.add(procedure.getRef());
}
if(procedure.getFullName().equals(SymbolRef.MAIN_PROC_NAME)) {
threadHeads.add(procedure.getRef());
}
}
return threadHeads;
}
/**
* Find two equivalence classes that can be coalesced into one - and perform the coalescence.
*
@ -42,11 +68,11 @@ public class Pass4ZeroPageCoalesce extends Pass2Base {
* @param unknownFragments Receives information about any unknown fragments encountered during ASM generation
* @return true if any classes were coalesced. False otherwise.
*/
private boolean coalesce(LiveRangeEquivalenceClassSet liveRangeEquivalenceClassSet, Set<String> unknownFragments) {
private boolean coalesce(LiveRangeEquivalenceClassSet liveRangeEquivalenceClassSet, Collection<ScopeRef> threadHeads, Set<String> unknownFragments) {
for(LiveRangeEquivalenceClass thisEquivalenceClass : liveRangeEquivalenceClassSet.getEquivalenceClasses()) {
for(LiveRangeEquivalenceClass otherEquivalenceClass : liveRangeEquivalenceClassSet.getEquivalenceClasses()) {
if(!thisEquivalenceClass.equals(otherEquivalenceClass)) {
if(canCoalesce(thisEquivalenceClass, otherEquivalenceClass, unknownFragments, getProgram())) {
if(canCoalesce(thisEquivalenceClass, otherEquivalenceClass, threadHeads, unknownFragments, getProgram())) {
getLog().append("Coalescing zero page register [ " + thisEquivalenceClass + " ] with [ " + otherEquivalenceClass + " ]");
liveRangeEquivalenceClassSet.consolidate(thisEquivalenceClass, otherEquivalenceClass);
// Reset the program register allocation
@ -68,8 +94,56 @@ public class Pass4ZeroPageCoalesce extends Pass2Base {
* @param program The program
* @return True if the two equivalence classes can be coalesced into one without problems.
*/
public static boolean canCoalesce(LiveRangeEquivalenceClass ec1, LiveRangeEquivalenceClass ec2, Set<String> unknownFragments, Program program) {
return canCoalesceVolatile(ec1, ec2, program) && canCoalesceClobber(ec1, ec2, unknownFragments, program);
static boolean canCoalesce(LiveRangeEquivalenceClass ec1, LiveRangeEquivalenceClass ec2, Collection<ScopeRef> threadHeads, Set<String> unknownFragments, Program program) {
return canCoalesceVolatile(ec1, ec2, program) && canCoalesceThreads(ec1, ec2, threadHeads, program) && canCoalesceClobber(ec1, ec2, unknownFragments, program);
}
/**
* Determines if two live range equivalence classes can be coalesced without cross-thread clobber.
* This is possible if they are both only called from the same thread head.
*
* @param ec1 One equivalence class
* @param ec2 Another equivalence class
* @param threadHeads The heads (in the call graph) from each thread in the program
* @param program The program
* @return True if the two equivalence classes can be coalesced into one without problems.
*/
private static boolean canCoalesceThreads(LiveRangeEquivalenceClass ec1, LiveRangeEquivalenceClass ec2, Collection<ScopeRef> threadHeads, Program program) {
CallGraph callGraph = program.getCallGraph();
Collection<ScopeRef> threads1 = getEquivalenceClassThreads(ec1, program, threadHeads, callGraph);
Collection<ScopeRef> threads2 = getEquivalenceClassThreads(ec2, program, threadHeads, callGraph);
if(threads1.isEmpty() || threads2.isEmpty()) {
return true;
}
return threads1.equals(threads2);
}
/**
* Find the threads for the variables in an equivalence class.
*
* @param equivalenceClass The equivalence class
* @param program The program
* @param threadHeads The threads (heads)
* @param callGraph The call graph
* @return All threads containing variables in the equivalence class.
*/
private static Collection<ScopeRef> getEquivalenceClassThreads(LiveRangeEquivalenceClass equivalenceClass, Program program, Collection<ScopeRef> threadHeads, CallGraph callGraph) {
Collection<ScopeRef> threads = new ArrayList<>();
for(VariableRef varRef : equivalenceClass.getVariables()) {
Variable variable = program.getScope().getVariable(varRef);
ScopeRef scopeRef = variable.getScope().getRef();
Collection<ScopeRef> recursiveCallers = callGraph.getRecursiveCallers(scopeRef);
for(ScopeRef threadHead : threadHeads) {
if(recursiveCallers.contains(threadHead)) {
if(!threads.contains(threadHead)) {
threads.add(threadHead);
}
}
}
}
return threads;
}
/**
@ -96,7 +170,8 @@ public class Pass4ZeroPageCoalesce extends Pass2Base {
}
/**
* Determines if any volatile varialbes prevents coalescing two equivalence classes
* Determines if any volatile variables prevents coalescing two equivalence classes
*
* @param ec1 One equivalence class
* @param ec2 Another equivalence class
* @param program The program

View File

@ -1,6 +1,7 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.values.ScopeRef;
import dk.camelot64.kickc.model.values.VariableRef;
import dk.camelot64.kickc.model.statements.Statement;
@ -19,6 +20,7 @@ public class Pass4ZeroPageCoalesceAssignment extends Pass2Base {
public void coalesce() {
CoalesceVarScores coalesceVarScores = new CoalesceVarScores(getProgram());
LinkedHashSet<String> unknownFragments = new LinkedHashSet<>();
Collection<ScopeRef> threadHeads = Pass4ZeroPageCoalesce.getThreadHeads(getSymbols());
boolean change;
do {
@ -28,7 +30,7 @@ public class Pass4ZeroPageCoalesceAssignment extends Pass2Base {
List<LiveRangeEquivalenceClassCoalesceCandidate> coalesceCandidates =
equivalenceClassScores.getCoalesceCandidates();
for(LiveRangeEquivalenceClassCoalesceCandidate candidate : coalesceCandidates) {
change |= attemptCoalesce(candidate, unknownFragments);
change |= attemptCoalesce(candidate, threadHeads, unknownFragments);
}
} while(change);
@ -40,12 +42,12 @@ public class Pass4ZeroPageCoalesceAssignment extends Pass2Base {
}
}
private boolean attemptCoalesce(LiveRangeEquivalenceClassCoalesceCandidate candidate, LinkedHashSet<String> unknownFragments) {
private boolean attemptCoalesce(LiveRangeEquivalenceClassCoalesceCandidate candidate, Collection<ScopeRef> threadHeads, LinkedHashSet<String> unknownFragments) {
LiveRangeEquivalenceClassSet liveRangeEquivalenceClassSet = getProgram().getLiveRangeEquivalenceClassSet();
List<LiveRangeEquivalenceClass> equivalenceClasses = liveRangeEquivalenceClassSet.getEquivalenceClasses();
if(equivalenceClasses.contains(candidate.getEc1()) && equivalenceClasses.contains(candidate.getEc2())) {
// Both equivalence classes still exist
if(Pass4ZeroPageCoalesce.canCoalesce(candidate.getEc1(), candidate.getEc2(), unknownFragments, getProgram())) {
if(Pass4ZeroPageCoalesce.canCoalesce(candidate.getEc1(), candidate.getEc2(), threadHeads, unknownFragments, getProgram())) {
getLog().append("Coalescing zero page register with common assignment [ " + candidate.getEc1() + " ] with [ " + candidate.getEc2()+ " ] - score: "+candidate.getScore());
liveRangeEquivalenceClassSet.consolidate(candidate.getEc1(), candidate.getEc2());
// Reset the program register allocation

View File

@ -2,6 +2,7 @@ package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.values.LabelRef;
import dk.camelot64.kickc.model.values.SymbolRef;
import java.util.ArrayList;
import java.util.List;
@ -31,7 +32,7 @@ public class PassNDominatorsAnalysis extends Pass2SsaOptimization {
for(ControlFlowBlock entryPointBlock : entryPointBlocks) {
LabelRef firstBlock = entryPointBlock.getLabel();
// Skip main-block, as it will be called by @begin anyways
if(firstBlock.getFullName().equals("main")) continue;
if(firstBlock.getFullName().equals(SymbolRef.MAIN_PROC_NAME)) continue;
DominatorsBlock firstDominators = dominatorsGraph.addDominators(firstBlock);
firstDominators.add(firstBlock);
firstBlocks.add(firstBlock);

View File

@ -32,6 +32,11 @@ public class TestPrograms {
public TestPrograms() {
}
@Test
public void testIrqLocalVarOverlap() throws IOException, URISyntaxException {
compileAndCompare("irq-local-var-overlap-problem");
}
@Test
public void testMultiplexerIrq() throws IOException, URISyntaxException {
compileAndCompare("multiplexer-irq/simple-multiplexer-irq", 10);

View File

@ -0,0 +1,62 @@
// Illustrates a problem where local variables inside an IRQ are assigned the same zeropage as a variable outside the IRQ
const void()** KERNEL_IRQ = $0314;
const byte* RASTER = $d012;
const byte* VIC_CONTROL = $d011;
const byte* IRQ_STATUS = $d019;
const byte* IRQ_ENABLE = $d01a;
const byte IRQ_RASTER = %00000001;
const byte* BGCOL = $d020;
const byte* FGCOL = $d021;
const byte* CIA1_INTERRUPT = $dc0d;
const byte CIA_INTERRUPT_CLEAR = $7f;
void main() {
asm { sei }
// Disable CIA 1 Timer IRQ
*CIA1_INTERRUPT = CIA_INTERRUPT_CLEAR;
// Set raster line to $0fd
*VIC_CONTROL &=$7f;
*RASTER = $fd;
// Enable Raster Interrupt
*IRQ_ENABLE = IRQ_RASTER;
// Set the IRQ routine
*KERNEL_IRQ = &irq;
asm { cli }
while(true) {
for( byte i: 0..10 )
for( byte j: 0..10 )
for( byte k: 0..10 ) {
*FGCOL = i+j+k;
sub_main();
}
}
}
interrupt(kernel_min) void irq() {
(*BGCOL)++;
for( byte i: 0..10 )
for( byte j: 0..10 )
for( byte k: 0..10 ) {
*FGCOL = i+j+k;
sub_irq();
}
*IRQ_STATUS = IRQ_RASTER;
(*BGCOL)--;
}
void sub_main() {
for( byte i: 0..10 )
for( byte j: 0..10 )
for( byte k: 0..10 )
*BGCOL = i+j+k;
}
void sub_irq() {
for( byte i: 0..10 )
for( byte j: 0..10 )
for( byte k: 0..10 )
*BGCOL = i+j+k;
}

View File

@ -0,0 +1,158 @@
// Illustrates a problem where local variables inside an IRQ are assigned the same zeropage as a variable outside the IRQ
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
.label KERNEL_IRQ = $314
.label RASTER = $d012
.label VIC_CONTROL = $d011
.label IRQ_STATUS = $d019
.label IRQ_ENABLE = $d01a
.const IRQ_RASTER = 1
.label BGCOL = $d020
.label FGCOL = $d021
.label CIA1_INTERRUPT = $dc0d
.const CIA_INTERRUPT_CLEAR = $7f
main: {
.label k = 4
.label j = 3
.label i = 2
sei
// Disable CIA 1 Timer IRQ
lda #CIA_INTERRUPT_CLEAR
sta CIA1_INTERRUPT
// Set raster line to $0fd
lda #$7f
and VIC_CONTROL
sta VIC_CONTROL
lda #$fd
sta RASTER
// Enable Raster Interrupt
lda #IRQ_RASTER
sta IRQ_ENABLE
// Set the IRQ routine
lda #<irq
sta KERNEL_IRQ
lda #>irq
sta KERNEL_IRQ+1
cli
b1:
lda #0
sta i
b4:
lda #0
sta j
b5:
lda #0
sta k
b6:
lda i
clc
adc j
clc
adc k
sta FGCOL
jsr sub_main
inc k
lda #$b
cmp k
bne b6
inc j
cmp j
bne b5
inc i
cmp i
bne b4
jmp b1
}
sub_main: {
.label i = 5
lda #0
sta i
b1:
ldx #0
b2:
ldy #0
b3:
txa
clc
adc i
sty $ff
clc
adc $ff
sta BGCOL
iny
cpy #$b
bne b3
inx
cpx #$b
bne b2
inc i
lda #$b
cmp i
bne b1
rts
}
irq: {
.label k = 8
.label j = 7
.label i = 6
inc BGCOL
lda #0
sta i
b1:
lda #0
sta j
b2:
lda #0
sta k
b3:
lda i
clc
adc j
clc
adc k
sta FGCOL
jsr sub_irq
inc k
lda #$b
cmp k
bne b3
inc j
cmp j
bne b2
inc i
cmp i
bne b1
lda #IRQ_RASTER
sta IRQ_STATUS
dec BGCOL
jmp $ea81
}
sub_irq: {
.label i = 9
lda #0
sta i
b1:
ldx #0
b2:
ldy #0
b3:
txa
clc
adc i
sty $ff
clc
adc $ff
sta BGCOL
iny
cpy #$b
bne b3
inx
cpx #$b
bne b2
inc i
lda #$b
cmp i
bne b1
rts
}

View File

@ -0,0 +1,137 @@
@begin: scope:[] from
[0] phi()
to:@4
@4: scope:[] from @begin
[1] phi()
[2] call main
to:@end
@end: scope:[] from @4
[3] phi()
main: scope:[main] from @4
asm { sei }
[5] *((const byte*) CIA1_INTERRUPT#0) ← (const byte) CIA_INTERRUPT_CLEAR#0
[6] *((const byte*) VIC_CONTROL#0) ← *((const byte*) VIC_CONTROL#0) & (byte/signed byte/word/signed word/dword/signed dword) $7f
[7] *((const byte*) RASTER#0) ← (byte/word/signed word/dword/signed dword) $fd
[8] *((const byte*) IRQ_ENABLE#0) ← (const byte) IRQ_RASTER#0
[9] *((const void()**) KERNEL_IRQ#0) ← &interrupt(KERNEL_MIN)(void()) irq()
asm { cli }
to:main::@4
main::@4: scope:[main] from main main::@10 main::@14
[11] (byte) main::i#7 ← phi( main::@14/(byte) main::i#1 main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@10/(byte/signed byte/word/signed word/dword/signed dword) 0 )
to:main::@5
main::@5: scope:[main] from main::@4 main::@9
[12] (byte) main::j#5 ← phi( main::@4/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@9/(byte) main::j#1 )
to:main::@6
main::@6: scope:[main] from main::@13 main::@5
[13] (byte) main::k#2 ← phi( main::@13/(byte) main::k#1 main::@5/(byte/signed byte/word/signed word/dword/signed dword) 0 )
[14] (byte~) main::$1 ← (byte) main::i#7 + (byte) main::j#5
[15] (byte~) main::$2 ← (byte~) main::$1 + (byte) main::k#2
[16] *((const byte*) FGCOL#0) ← (byte~) main::$2
[17] call sub_main
to:main::@13
main::@13: scope:[main] from main::@6
[18] (byte) main::k#1 ← ++ (byte) main::k#2
[19] if((byte) main::k#1!=(byte/signed byte/word/signed word/dword/signed dword) $b) goto main::@6
to:main::@9
main::@9: scope:[main] from main::@13
[20] (byte) main::j#1 ← ++ (byte) main::j#5
[21] if((byte) main::j#1!=(byte/signed byte/word/signed word/dword/signed dword) $b) goto main::@5
to:main::@10
main::@10: scope:[main] from main::@9
[22] (byte) main::i#1 ← ++ (byte) main::i#7
[23] if((byte) main::i#1!=(byte/signed byte/word/signed word/dword/signed dword) $b) goto main::@14
to:main::@4
main::@14: scope:[main] from main::@10
[24] phi()
to:main::@4
sub_main: scope:[sub_main] from main::@6
[25] phi()
to:sub_main::@1
sub_main::@1: scope:[sub_main] from sub_main sub_main::@5
[26] (byte) sub_main::i#6 ← phi( sub_main/(byte/signed byte/word/signed word/dword/signed dword) 0 sub_main::@5/(byte) sub_main::i#1 )
to:sub_main::@2
sub_main::@2: scope:[sub_main] from sub_main::@1 sub_main::@4
[27] (byte) sub_main::j#4 ← phi( sub_main::@1/(byte/signed byte/word/signed word/dword/signed dword) 0 sub_main::@4/(byte) sub_main::j#1 )
to:sub_main::@3
sub_main::@3: scope:[sub_main] from sub_main::@2 sub_main::@3
[28] (byte) sub_main::k#2 ← phi( sub_main::@2/(byte/signed byte/word/signed word/dword/signed dword) 0 sub_main::@3/(byte) sub_main::k#1 )
[29] (byte~) sub_main::$0 ← (byte) sub_main::i#6 + (byte) sub_main::j#4
[30] (byte~) sub_main::$1 ← (byte~) sub_main::$0 + (byte) sub_main::k#2
[31] *((const byte*) BGCOL#0) ← (byte~) sub_main::$1
[32] (byte) sub_main::k#1 ← ++ (byte) sub_main::k#2
[33] if((byte) sub_main::k#1!=(byte/signed byte/word/signed word/dword/signed dword) $b) goto sub_main::@3
to:sub_main::@4
sub_main::@4: scope:[sub_main] from sub_main::@3
[34] (byte) sub_main::j#1 ← ++ (byte) sub_main::j#4
[35] if((byte) sub_main::j#1!=(byte/signed byte/word/signed word/dword/signed dword) $b) goto sub_main::@2
to:sub_main::@5
sub_main::@5: scope:[sub_main] from sub_main::@4
[36] (byte) sub_main::i#1 ← ++ (byte) sub_main::i#6
[37] if((byte) sub_main::i#1!=(byte/signed byte/word/signed word/dword/signed dword) $b) goto sub_main::@1
to:sub_main::@return
sub_main::@return: scope:[sub_main] from sub_main::@5
[38] return
to:@return
irq: scope:[irq] from
[39] *((const byte*) BGCOL#0) ← ++ *((const byte*) BGCOL#0)
to:irq::@1
irq::@1: scope:[irq] from irq irq::@5
[40] (byte) irq::i#7 ← phi( irq/(byte/signed byte/word/signed word/dword/signed dword) 0 irq::@5/(byte) irq::i#1 )
to:irq::@2
irq::@2: scope:[irq] from irq::@1 irq::@4
[41] (byte) irq::j#4 ← phi( irq::@1/(byte/signed byte/word/signed word/dword/signed dword) 0 irq::@4/(byte) irq::j#1 )
to:irq::@3
irq::@3: scope:[irq] from irq::@2 irq::@7
[42] (byte) irq::k#2 ← phi( irq::@2/(byte/signed byte/word/signed word/dword/signed dword) 0 irq::@7/(byte) irq::k#1 )
[43] (byte~) irq::$0 ← (byte) irq::i#7 + (byte) irq::j#4
[44] (byte~) irq::$1 ← (byte~) irq::$0 + (byte) irq::k#2
[45] *((const byte*) FGCOL#0) ← (byte~) irq::$1
[46] call sub_irq
to:irq::@7
irq::@7: scope:[irq] from irq::@3
[47] (byte) irq::k#1 ← ++ (byte) irq::k#2
[48] if((byte) irq::k#1!=(byte/signed byte/word/signed word/dword/signed dword) $b) goto irq::@3
to:irq::@4
irq::@4: scope:[irq] from irq::@7
[49] (byte) irq::j#1 ← ++ (byte) irq::j#4
[50] if((byte) irq::j#1!=(byte/signed byte/word/signed word/dword/signed dword) $b) goto irq::@2
to:irq::@5
irq::@5: scope:[irq] from irq::@4
[51] (byte) irq::i#1 ← ++ (byte) irq::i#7
[52] if((byte) irq::i#1!=(byte/signed byte/word/signed word/dword/signed dword) $b) goto irq::@1
to:irq::@6
irq::@6: scope:[irq] from irq::@5
[53] *((const byte*) IRQ_STATUS#0) ← (const byte) IRQ_RASTER#0
[54] *((const byte*) BGCOL#0) ← -- *((const byte*) BGCOL#0)
to:irq::@return
irq::@return: scope:[irq] from irq::@6
[55] return
to:@return
sub_irq: scope:[sub_irq] from irq::@3
[56] phi()
to:sub_irq::@1
sub_irq::@1: scope:[sub_irq] from sub_irq sub_irq::@5
[57] (byte) sub_irq::i#6 ← phi( sub_irq/(byte/signed byte/word/signed word/dword/signed dword) 0 sub_irq::@5/(byte) sub_irq::i#1 )
to:sub_irq::@2
sub_irq::@2: scope:[sub_irq] from sub_irq::@1 sub_irq::@4
[58] (byte) sub_irq::j#4 ← phi( sub_irq::@1/(byte/signed byte/word/signed word/dword/signed dword) 0 sub_irq::@4/(byte) sub_irq::j#1 )
to:sub_irq::@3
sub_irq::@3: scope:[sub_irq] from sub_irq::@2 sub_irq::@3
[59] (byte) sub_irq::k#2 ← phi( sub_irq::@2/(byte/signed byte/word/signed word/dword/signed dword) 0 sub_irq::@3/(byte) sub_irq::k#1 )
[60] (byte~) sub_irq::$0 ← (byte) sub_irq::i#6 + (byte) sub_irq::j#4
[61] (byte~) sub_irq::$1 ← (byte~) sub_irq::$0 + (byte) sub_irq::k#2
[62] *((const byte*) BGCOL#0) ← (byte~) sub_irq::$1
[63] (byte) sub_irq::k#1 ← ++ (byte) sub_irq::k#2
[64] if((byte) sub_irq::k#1!=(byte/signed byte/word/signed word/dword/signed dword) $b) goto sub_irq::@3
to:sub_irq::@4
sub_irq::@4: scope:[sub_irq] from sub_irq::@3
[65] (byte) sub_irq::j#1 ← ++ (byte) sub_irq::j#4
[66] if((byte) sub_irq::j#1!=(byte/signed byte/word/signed word/dword/signed dword) $b) goto sub_irq::@2
to:sub_irq::@5
sub_irq::@5: scope:[sub_irq] from sub_irq::@4
[67] (byte) sub_irq::i#1 ← ++ (byte) sub_irq::i#6
[68] if((byte) sub_irq::i#1!=(byte/signed byte/word/signed word/dword/signed dword) $b) goto sub_irq::@1
to:sub_irq::@return
sub_irq::@return: scope:[sub_irq] from sub_irq::@5
[69] return
to:@return

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,119 @@
(label) @4
(label) @begin
(label) @end
(byte*) BGCOL
(const byte*) BGCOL#0 BGCOL = ((byte*))(word/dword/signed dword) $d020
(byte*) CIA1_INTERRUPT
(const byte*) CIA1_INTERRUPT#0 CIA1_INTERRUPT = ((byte*))(word/dword/signed dword) $dc0d
(byte) CIA_INTERRUPT_CLEAR
(const byte) CIA_INTERRUPT_CLEAR#0 CIA_INTERRUPT_CLEAR = (byte/signed byte/word/signed word/dword/signed dword) $7f
(byte*) FGCOL
(const byte*) FGCOL#0 FGCOL = ((byte*))(word/dword/signed dword) $d021
(byte*) IRQ_ENABLE
(const byte*) IRQ_ENABLE#0 IRQ_ENABLE = ((byte*))(word/dword/signed dword) $d01a
(byte) IRQ_RASTER
(const byte) IRQ_RASTER#0 IRQ_RASTER = (byte/signed byte/word/signed word/dword/signed dword) 1
(byte*) IRQ_STATUS
(const byte*) IRQ_STATUS#0 IRQ_STATUS = ((byte*))(word/dword/signed dword) $d019
(void()**) KERNEL_IRQ
(const void()**) KERNEL_IRQ#0 KERNEL_IRQ = ((void()**))(word/signed word/dword/signed dword) $314
(byte*) RASTER
(const byte*) RASTER#0 RASTER = ((byte*))(word/dword/signed dword) $d012
(byte*) VIC_CONTROL
(const byte*) VIC_CONTROL#0 VIC_CONTROL = ((byte*))(word/dword/signed dword) $d011
interrupt(KERNEL_MIN)(void()) irq()
(byte~) irq::$0 reg byte a 2002.0
(byte~) irq::$1 reg byte a 2002.0
(label) irq::@1
(label) irq::@2
(label) irq::@3
(label) irq::@4
(label) irq::@5
(label) irq::@6
(label) irq::@7
(label) irq::@return
(byte) irq::i
(byte) irq::i#1 i zp ZP_BYTE:6 16.5
(byte) irq::i#7 i zp ZP_BYTE:6 93.0
(byte) irq::j
(byte) irq::j#1 j zp ZP_BYTE:7 151.5
(byte) irq::j#4 j zp ZP_BYTE:7 150.375
(byte) irq::k
(byte) irq::k#1 k zp ZP_BYTE:8 1501.5
(byte) irq::k#2 k zp ZP_BYTE:8 600.5999999999999
(void()) main()
(byte~) main::$1 reg byte a 20002.0
(byte~) main::$2 reg byte a 20002.0
(label) main::@10
(label) main::@13
(label) main::@14
(label) main::@4
(label) main::@5
(label) main::@6
(label) main::@9
(byte) main::i
(byte) main::i#1 i zp ZP_BYTE:2 71.0
(byte) main::i#7 i zp ZP_BYTE:2 919.3636363636363
(byte) main::j
(byte) main::j#1 j zp ZP_BYTE:3 1501.5
(byte) main::j#5 j zp ZP_BYTE:3 1500.375
(byte) main::k
(byte) main::k#1 k zp ZP_BYTE:4 15001.5
(byte) main::k#2 k zp ZP_BYTE:4 6000.6
(void()) sub_irq()
(byte~) sub_irq::$0 reg byte a 2000002.0
(byte~) sub_irq::$1 reg byte a 2000002.0
(label) sub_irq::@1
(label) sub_irq::@2
(label) sub_irq::@3
(label) sub_irq::@4
(label) sub_irq::@5
(label) sub_irq::@return
(byte) sub_irq::i
(byte) sub_irq::i#1 i zp ZP_BYTE:9 15001.5
(byte) sub_irq::i#6 i zp ZP_BYTE:9 102000.30000000002
(byte) sub_irq::j
(byte) sub_irq::j#1 reg byte x 150001.5
(byte) sub_irq::j#4 reg byte x 171428.99999999997
(byte) sub_irq::k
(byte) sub_irq::k#1 reg byte y 1500001.5
(byte) sub_irq::k#2 reg byte y 750000.75
(void()) sub_main()
(byte~) sub_main::$0 reg byte a 2.0000002E7
(byte~) sub_main::$1 reg byte a 2.0000002E7
(label) sub_main::@1
(label) sub_main::@2
(label) sub_main::@3
(label) sub_main::@4
(label) sub_main::@5
(label) sub_main::@return
(byte) sub_main::i
(byte) sub_main::i#1 i zp ZP_BYTE:5 150001.5
(byte) sub_main::i#6 i zp ZP_BYTE:5 1020000.2999999999
(byte) sub_main::j
(byte) sub_main::j#1 reg byte x 1500001.5
(byte) sub_main::j#4 reg byte x 1714286.1428571427
(byte) sub_main::k
(byte) sub_main::k#1 reg byte y 1.50000015E7
(byte) sub_main::k#2 reg byte y 7500000.75
zp ZP_BYTE:2 [ main::i#7 main::i#1 ]
zp ZP_BYTE:3 [ main::j#5 main::j#1 ]
zp ZP_BYTE:4 [ main::k#2 main::k#1 ]
zp ZP_BYTE:5 [ sub_main::i#6 sub_main::i#1 ]
reg byte x [ sub_main::j#4 sub_main::j#1 ]
reg byte y [ sub_main::k#2 sub_main::k#1 ]
zp ZP_BYTE:6 [ irq::i#7 irq::i#1 ]
zp ZP_BYTE:7 [ irq::j#4 irq::j#1 ]
zp ZP_BYTE:8 [ irq::k#2 irq::k#1 ]
zp ZP_BYTE:9 [ sub_irq::i#6 sub_irq::i#1 ]
reg byte x [ sub_irq::j#4 sub_irq::j#1 ]
reg byte y [ sub_irq::k#2 sub_irq::k#1 ]
reg byte a [ main::$1 ]
reg byte a [ main::$2 ]
reg byte a [ sub_main::$0 ]
reg byte a [ sub_main::$1 ]
reg byte a [ irq::$0 ]
reg byte a [ irq::$1 ]
reg byte a [ sub_irq::$0 ]
reg byte a [ sub_irq::$1 ]

View File

@ -239,7 +239,7 @@ plexInit: {
rts
}
plex_irq: {
.label _3 = 5
.label _3 = $10
lda #WHITE
sta BORDERCOL
b1:
@ -277,7 +277,7 @@ plex_irq: {
// plexSort() prepares showing the sprites
plexShowSprite: {
.label _8 = 8
.label plex_sprite_idx2 = 5
.label plex_sprite_idx2 = $10
.label plexFreeAdd1__2 = $a
lda plex_sprite_idx
asl

View File

@ -3451,11 +3451,10 @@ Uplifting [plexShowSprite] best 58508 combination reg byte x [ plexShowSprite::x
Attempting to uplift remaining variables inzp ZP_BYTE:29 [ plexShowSprite::plex_sprite_idx2#0 ]
Uplifting [plexShowSprite] best 58508 combination zp ZP_BYTE:29 [ plexShowSprite::plex_sprite_idx2#0 ]
Coalescing zero page register [ zp ZP_BOOL:2 [ framedone#12 framedone#19 framedone#5 ] ] with [ zp ZP_BOOL:28 [ framedone#3 ] ]
Coalescing zero page register [ zp ZP_BYTE:6 [ plexSort::m#2 plexSort::m#1 ] ] with [ zp ZP_BYTE:27 [ plex_irq::$3 ] ]
Coalescing zero page register [ zp ZP_BYTE:6 [ plexSort::m#2 plexSort::m#1 plex_irq::$3 ] ] with [ zp ZP_BYTE:29 [ plexShowSprite::plex_sprite_idx2#0 ] ]
Coalescing zero page register [ zp ZP_BYTE:15 [ plex_show_idx#27 plex_show_idx#0 plex_show_idx#16 ] ] with [ zp ZP_BYTE:21 [ plex_show_idx#1 ] ]
Coalescing zero page register [ zp ZP_BYTE:17 [ plex_sprite_msb#29 plex_sprite_msb#0 plex_sprite_msb#17 plex_sprite_msb#27 plex_sprite_msb#4 ] ] with [ zp ZP_BYTE:23 [ plex_sprite_msb#1 ] ]
Allocated (was zp ZP_BYTE:6) zp ZP_BYTE:5 [ plexSort::m#2 plexSort::m#1 plex_irq::$3 plexShowSprite::plex_sprite_idx2#0 ]
Coalescing zero page register [ zp ZP_BYTE:27 [ plex_irq::$3 ] ] with [ zp ZP_BYTE:29 [ plexShowSprite::plex_sprite_idx2#0 ] ]
Allocated (was zp ZP_BYTE:6) zp ZP_BYTE:5 [ plexSort::m#2 plexSort::m#1 ]
Allocated (was zp ZP_WORD:10) zp ZP_WORD:6 [ init::xp#2 init::xp#1 ]
Allocated (was zp ZP_BYTE:14) zp ZP_BYTE:8 [ plex_sprite_idx#25 plex_sprite_idx#0 plexShowSprite::$8 ]
Allocated (was zp ZP_BYTE:15) zp ZP_BYTE:9 [ plex_show_idx#27 plex_show_idx#0 plex_show_idx#16 plex_show_idx#1 ]
@ -3465,6 +3464,7 @@ Allocated (was zp ZP_BYTE:18) zp ZP_BYTE:12 [ plexSort::nxt_idx#0 ]
Allocated (was zp ZP_BYTE:19) zp ZP_BYTE:13 [ plexSort::nxt_y#0 ]
Allocated (was zp ZP_BYTE:22) zp ZP_BYTE:14 [ plex_sprite_idx#1 ]
Allocated (was zp ZP_BYTE:24) zp ZP_BYTE:15 [ plex_free_next#0 ]
Allocated (was zp ZP_BYTE:27) zp ZP_BYTE:16 [ plex_irq::$3 plexShowSprite::plex_sprite_idx2#0 ]
ASSEMBLER BEFORE OPTIMIZATION
//SEG0 File Comments
@ -3947,7 +3947,7 @@ plexInit: {
}
//SEG152 plex_irq
plex_irq: {
.label _3 = 5
.label _3 = $10
//SEG153 entry interrupt(KERNEL_MIN)
//SEG154 [86] *((const byte*) BORDERCOL#0) ← (const byte) WHITE#0 -- _deref_pbuc1=vbuc2
lda #WHITE
@ -4026,7 +4026,7 @@ plex_irq: {
// plexSort() prepares showing the sprites
plexShowSprite: {
.label _8 = 8
.label plex_sprite_idx2 = 5
.label plex_sprite_idx2 = $10
.label plexFreeAdd1__2 = $a
//SEG181 [99] (byte) plexShowSprite::plex_sprite_idx2#0 ← (byte) plex_sprite_idx#25 << (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuz1=vbuz2_rol_1
lda plex_sprite_idx
@ -4483,7 +4483,7 @@ FINAL SYMBOL TABLE
(byte) plexShowSprite::plexFreeAdd1_ypos
(byte) plexShowSprite::plexFreeAdd1_ypos#0 reg byte a 3.0
(byte) plexShowSprite::plex_sprite_idx2
(byte) plexShowSprite::plex_sprite_idx2#0 plex_sprite_idx2 zp ZP_BYTE:5 0.6000000000000001
(byte) plexShowSprite::plex_sprite_idx2#0 plex_sprite_idx2 zp ZP_BYTE:16 0.6000000000000001
(byte) plexShowSprite::xpos_idx
(byte) plexShowSprite::xpos_idx#0 reg byte x 2.0
(byte) plexShowSprite::ypos
@ -4520,7 +4520,7 @@ FINAL SYMBOL TABLE
(byte) plex_free_next#27 plex_free_next#27 zp ZP_BYTE:10 2.8333333333333335
(byte) plex_free_next#31 plex_free_next#31 zp ZP_BYTE:10 4.0
interrupt(KERNEL_MIN)(void()) plex_irq()
(byte/signed word/word/dword/signed dword~) plex_irq::$3 $3 zp ZP_BYTE:5 11.0
(byte/signed word/word/dword/signed dword~) plex_irq::$3 $3 zp ZP_BYTE:16 11.0
(label) plex_irq::@1
(label) plex_irq::@2
(label) plex_irq::@3
@ -4554,7 +4554,7 @@ zp ZP_BOOL:2 [ framedone#12 framedone#19 framedone#5 framedone#3 ]
zp ZP_BYTE:3 [ loop::sin_idx#6 loop::sin_idx#0 loop::sin_idx#1 ]
zp ZP_BYTE:4 [ loop::y_idx#2 loop::y_idx#0 loop::y_idx#1 ]
reg byte y [ loop::sy#2 loop::sy#1 ]
zp ZP_BYTE:5 [ plexSort::m#2 plexSort::m#1 plex_irq::$3 plexShowSprite::plex_sprite_idx2#0 ]
zp ZP_BYTE:5 [ plexSort::m#2 plexSort::m#1 ]
reg byte x [ plexSort::s#3 plexSort::s#1 plexSort::s#6 ]
reg byte x [ plexSort::plexFreePrepare1_s#2 plexSort::plexFreePrepare1_s#1 ]
reg byte x [ init::sx#2 init::sx#1 ]
@ -4572,6 +4572,7 @@ zp ZP_BYTE:14 [ plex_sprite_idx#1 ]
zp ZP_BYTE:15 [ plex_free_next#0 ]
reg byte a [ init::$6 ]
reg byte x [ plex_irq::plexFreeNextYpos1_return#0 ]
zp ZP_BYTE:16 [ plex_irq::$3 plexShowSprite::plex_sprite_idx2#0 ]
reg byte a [ plexShowSprite::plexFreeAdd1_ypos#0 ]
reg byte a [ plexShowSprite::plexFreeAdd1_$0#0 ]
reg byte x [ plexShowSprite::plexFreeAdd1_$1#0 ]
@ -4979,7 +4980,7 @@ plexInit: {
}
//SEG152 plex_irq
plex_irq: {
.label _3 = 5
.label _3 = $10
//SEG153 entry interrupt(KERNEL_MIN)
//SEG154 [86] *((const byte*) BORDERCOL#0) ← (const byte) WHITE#0 -- _deref_pbuc1=vbuc2
lda #WHITE
@ -5045,7 +5046,7 @@ plex_irq: {
// plexSort() prepares showing the sprites
plexShowSprite: {
.label _8 = 8
.label plex_sprite_idx2 = 5
.label plex_sprite_idx2 = $10
.label plexFreeAdd1__2 = $a
//SEG181 [99] (byte) plexShowSprite::plex_sprite_idx2#0 ← (byte) plex_sprite_idx#25 << (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuz1=vbuz2_rol_1
lda plex_sprite_idx

View File

@ -197,7 +197,7 @@
(byte) plexShowSprite::plexFreeAdd1_ypos
(byte) plexShowSprite::plexFreeAdd1_ypos#0 reg byte a 3.0
(byte) plexShowSprite::plex_sprite_idx2
(byte) plexShowSprite::plex_sprite_idx2#0 plex_sprite_idx2 zp ZP_BYTE:5 0.6000000000000001
(byte) plexShowSprite::plex_sprite_idx2#0 plex_sprite_idx2 zp ZP_BYTE:16 0.6000000000000001
(byte) plexShowSprite::xpos_idx
(byte) plexShowSprite::xpos_idx#0 reg byte x 2.0
(byte) plexShowSprite::ypos
@ -234,7 +234,7 @@
(byte) plex_free_next#27 plex_free_next#27 zp ZP_BYTE:10 2.8333333333333335
(byte) plex_free_next#31 plex_free_next#31 zp ZP_BYTE:10 4.0
interrupt(KERNEL_MIN)(void()) plex_irq()
(byte/signed word/word/dword/signed dword~) plex_irq::$3 $3 zp ZP_BYTE:5 11.0
(byte/signed word/word/dword/signed dword~) plex_irq::$3 $3 zp ZP_BYTE:16 11.0
(label) plex_irq::@1
(label) plex_irq::@2
(label) plex_irq::@3
@ -268,7 +268,7 @@ zp ZP_BOOL:2 [ framedone#12 framedone#19 framedone#5 framedone#3 ]
zp ZP_BYTE:3 [ loop::sin_idx#6 loop::sin_idx#0 loop::sin_idx#1 ]
zp ZP_BYTE:4 [ loop::y_idx#2 loop::y_idx#0 loop::y_idx#1 ]
reg byte y [ loop::sy#2 loop::sy#1 ]
zp ZP_BYTE:5 [ plexSort::m#2 plexSort::m#1 plex_irq::$3 plexShowSprite::plex_sprite_idx2#0 ]
zp ZP_BYTE:5 [ plexSort::m#2 plexSort::m#1 ]
reg byte x [ plexSort::s#3 plexSort::s#1 plexSort::s#6 ]
reg byte x [ plexSort::plexFreePrepare1_s#2 plexSort::plexFreePrepare1_s#1 ]
reg byte x [ init::sx#2 init::sx#1 ]
@ -286,6 +286,7 @@ zp ZP_BYTE:14 [ plex_sprite_idx#1 ]
zp ZP_BYTE:15 [ plex_free_next#0 ]
reg byte a [ init::$6 ]
reg byte x [ plex_irq::plexFreeNextYpos1_return#0 ]
zp ZP_BYTE:16 [ plex_irq::$3 plexShowSprite::plex_sprite_idx2#0 ]
reg byte a [ plexShowSprite::plexFreeAdd1_ypos#0 ]
reg byte a [ plexShowSprite::plexFreeAdd1_$0#0 ]
reg byte x [ plexShowSprite::plexFreeAdd1_$1#0 ]