1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-12-26 03:32:23 +00:00

Fixed stack-overflow on recursive __stackcall functions. Added <math.h> abs(). Added <stdio.h> printf(). Closes #415

This commit is contained in:
jespergravgaard 2020-04-25 16:51:35 +02:00
parent 3b6c14be7a
commit 5b2e993994
13 changed files with 731 additions and 20 deletions

View File

@ -221,21 +221,16 @@ public class CallGraph {
}
}
public int getCallDepth(ProcedureRef procedureRef) {
final Collection<CallBlock.Call> callers = getCallers(procedureRef);
int maxCallDepth = 1;
for(CallBlock.Call caller : callers) {
final ScopeRef callStatementScope = caller.getCallStatementScope();
if(callStatementScope instanceof ProcedureRef) {
int callerDepth = getCallDepth((ProcedureRef) callStatementScope)+1;
if(callerDepth>maxCallDepth)
maxCallDepth = callerDepth;
}
}
return maxCallDepth;
/**
* Determine if a specific call is part of a recursive procedure call
* @param call to examine
* @return true if the call is part of procedure recursive calls
*/
public boolean isRecursive(CallBlock.Call call) {
final Collection<ScopeRef> recursiveCalls = getRecursiveCalls(call.callStatementScope);
return recursiveCalls.contains(call.callStatementScope);
}
/**
* A block in the call graph, matching a scope in the program.
*/

View File

@ -79,7 +79,7 @@ public class PassNCalcVariableRegisterWeight extends PassNCalcBase<VariableRegis
}
private static double addWeight(VariableRef variable, LabelRef block, LiveRangeVariables liveRangeVariables, NaturalLoopSet loopSet, CallGraph callGraph, StatementInfos statementInfos, VariableRegisterWeights variableRegisterWeights) {
int loopCallDepth = getLoopCallDepth(block, loopSet, callGraph,statementInfos);
int loopCallDepth = getLoopCallDepth(block, loopSet, callGraph, statementInfos);
double w = 1.0 + Math.pow(10.0, loopCallDepth);
LiveRange liveRange = liveRangeVariables.getLiveRange(variable);
double s = liveRange == null ? 0.0 : liveRange.size();
@ -104,11 +104,15 @@ public class PassNCalcVariableRegisterWeight extends PassNCalcBase<VariableRegis
final Collection<CallGraph.CallBlock.Call> callers = callGraph.getCallers(procedureRef);
int maxCallDepth = 0;
for(CallGraph.CallBlock.Call caller : callers) {
final Integer callStatementIdx = caller.getCallStatementIdx();
final ControlFlowBlock callBlock = statementInfos.getBlock(callStatementIdx);
int callDepth = getLoopCallDepth(callBlock.getLabel(), loopSet, callGraph, statementInfos) + 1;
if(callDepth > maxCallDepth)
maxCallDepth = callDepth;
if(callGraph.isRecursive(caller)) {
maxCallDepth = 1;
} else {
final Integer callStatementIdx = caller.getCallStatementIdx();
final ControlFlowBlock callBlock = statementInfos.getBlock(callStatementIdx);
int callDepth = getLoopCallDepth(callBlock.getLabel(), loopSet, callGraph, statementInfos) + 1;
if(callDepth > maxCallDepth)
maxCallDepth = callDepth;
}
}
int loopDepth = loopSet.getMaxLoopDepth(block);
return maxCallDepth + loopDepth;

View File

View File

@ -0,0 +1,3 @@
// Functions for performing input and output.
#include <printf.h>

View File

@ -39,3 +39,6 @@ void utoa(unsigned int value, char* buffer, enum RADIX radix);
// - radix : The radix to convert the number to (from the enum RADIX)
void ultoa(unsigned long value, char* buffer, enum RADIX radix);
// Returns the absolute value of int x.
int abs(int x);

0
src/main/kc/lib/math.c Normal file
View File

View File

@ -247,4 +247,12 @@ unsigned long ultoa_append(char *buffer, unsigned long value, unsigned long sub)
while (value >= sub){ digit++; value -= sub; }
*buffer = DIGITS[digit];
return value;
}
}
// Returns the absolute value of int x.
inline int abs(int x) {
if(x<0)
return -x;
else
return x;
}

View File

@ -517,6 +517,11 @@ public class TestPrograms {
compileAndCompare("declared-memory-var-0.c");
}
@Test
public void testProcedureCallingConventionStack13() throws IOException, URISyntaxException {
compileAndCompare("procedure-callingconvention-stack-13.c");
}
@Test
public void testProcedureCallingConventionStack12() throws IOException, URISyntaxException {
compileAndCompare("procedure-callingconvention-stack-12.c");

View File

@ -0,0 +1,18 @@
// Test a procedure with calling convention stack
// Recursion that works (no local variables)
char* const SCREEN = 0x0400;
void main(void) {
*SCREEN = pow2(6);
}
char __stackcall pow2(char n) {
if (n == 0)
return 1;
else {
char c = pow2(n-1);
return c+c;
}
}

View File

@ -0,0 +1,46 @@
// Test a procedure with calling convention stack
// Recursion that works (no local variables)
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
.label SCREEN = $400
.const STACK_BASE = $103
main: {
// pow2(6)
lda #6
pha
jsr pow2
pla
// *SCREEN = pow2(6)
sta SCREEN
// }
rts
}
// pow2(byte register(A) n)
pow2: {
.const OFFSET_STACK_N = 0
.const OFFSET_STACK_RETURN = 0
tsx
lda STACK_BASE+OFFSET_STACK_N,x
// if (n == 0)
cmp #0
beq __b1
// n-1
sec
sbc #1
// pow2(n-1)
pha
jsr pow2
// c = pow2(n-1)
pla
// return c+c;
asl
jmp __breturn
__b1:
lda #1
__breturn:
// }
tsx
sta STACK_BASE+OFFSET_STACK_RETURN,x
rts
}

View File

@ -0,0 +1,38 @@
@begin: scope:[] from
[0] phi()
to:@1
@1: scope:[] from @begin
[1] phi()
[2] call main
to:@end
@end: scope:[] from @1
[3] phi()
(void()) main()
main: scope:[main] from @1
[4] stackpush(byte) ← (byte) 6
[5] callexecute pow2
[6] (byte~) main::$0 ← stackpull(byte)
[7] *((const nomodify byte*) SCREEN) ← (byte~) main::$0
to:main::@return
main::@return: scope:[main] from main
[8] return
to:@return
__stackcall (byte()) pow2((byte) pow2::n)
pow2: scope:[pow2] from
[9] (byte) pow2::n#0 ← stackidx(byte,(const byte) pow2::OFFSET_STACK_N)
[10] if((byte) pow2::n#0==(byte) 0) goto pow2::@return
to:pow2::@1
pow2::@1: scope:[pow2] from pow2
[11] (byte~) pow2::$1 ← (byte) pow2::n#0 - (byte) 1
[12] stackpush(byte) ← (byte~) pow2::$1
[13] callexecute pow2
[14] (byte) pow2::c#0 ← stackpull(byte)
[15] (byte) pow2::return#1 ← (byte) pow2::c#0 + (byte) pow2::c#0
to:pow2::@return
pow2::@return: scope:[pow2] from pow2 pow2::@1
[16] (byte) pow2::return#2 ← phi( pow2/(byte) 1 pow2::@1/(byte) pow2::return#1 )
[17] stackidx(byte,(const byte) pow2::OFFSET_STACK_RETURN) ← (byte) pow2::return#2
[18] return
to:@return

View File

@ -0,0 +1,564 @@
Culled Empty Block (label) @1
Culled Empty Block (label) pow2::@4
Culled Empty Block (label) pow2::@2
Culled Empty Block (label) pow2::@5
Culled Empty Block (label) pow2::@6
Calling convention STACK_CALL adding prepare/execute/finalize for (byte~) main::$0 ← call pow2 (number) 6
Calling convention STACK_CALL adding prepare/execute/finalize for (byte~) pow2::$2 ← call pow2 (number~) pow2::$1
Calling convention STACK_CALL replacing param((byte) pow2::n) with stackidx(byte,(const byte) pow2::OFFSET_STACK_N)
Calling convention STACK_CALL adding stack return stackidx(byte,pow2::OFFSET_STACK_RETURN) ← pow2::return
Calling convention STACK_CALL adding stack pull main::$0 ← stackpull(byte)
Calling convention STACK_CALL adding stack pull pow2::$2 ← stackpull(byte)
Calling convention STACK_CALL adding stack push stackpush(byte) ← 6
Calling convention STACK_CALL adding stack push stackpush(byte) ← pow2::$1
CONTROL FLOW GRAPH SSA
@begin: scope:[] from
to:@2
(void()) main()
main: scope:[main] from @2
stackpush(byte) ← (number) 6
callexecute pow2
(byte~) main::$0 ← stackpull(byte)
*((const nomodify byte*) SCREEN) ← (byte~) main::$0
to:main::@return
main::@return: scope:[main] from main
return
to:@return
__stackcall (byte()) pow2((byte) pow2::n)
pow2: scope:[pow2] from
(byte) pow2::n#0 ← stackidx(byte,(const byte) pow2::OFFSET_STACK_N)
(bool~) pow2::$0 ← (byte) pow2::n#0 == (number) 0
if((bool~) pow2::$0) goto pow2::@1
to:pow2::@3
pow2::@1: scope:[pow2] from pow2
(byte) pow2::return#0 ← (number) 1
to:pow2::@return
pow2::@3: scope:[pow2] from pow2
(byte) pow2::n#1 ← phi( pow2/(byte) pow2::n#0 )
(number~) pow2::$1 ← (byte) pow2::n#1 - (number) 1
stackpush(byte) ← (number~) pow2::$1
callexecute pow2
(byte~) pow2::$2 ← stackpull(byte)
(byte) pow2::c#0 ← (byte~) pow2::$2
(byte~) pow2::$3 ← (byte) pow2::c#0 + (byte) pow2::c#0
(byte) pow2::return#1 ← (byte~) pow2::$3
to:pow2::@return
pow2::@return: scope:[pow2] from pow2::@1 pow2::@3
(byte) pow2::return#2 ← phi( pow2::@1/(byte) pow2::return#0 pow2::@3/(byte) pow2::return#1 )
stackidx(byte,(const byte) pow2::OFFSET_STACK_RETURN) ← (byte) pow2::return#2
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
(const nomodify byte*) SCREEN = (byte*)(number) $400
(const word) STACK_BASE = (word) $103
(void()) main()
(byte~) main::$0
(label) main::@return
__stackcall (byte()) pow2((byte) pow2::n)
(bool~) pow2::$0
(number~) pow2::$1
(byte~) pow2::$2
(byte~) pow2::$3
(label) pow2::@1
(label) pow2::@3
(label) pow2::@return
(const byte) pow2::OFFSET_STACK_N = (byte) 0
(const byte) pow2::OFFSET_STACK_RETURN = (byte) 0
(byte) pow2::c
(byte) pow2::c#0
(byte) pow2::n
(byte) pow2::n#0
(byte) pow2::n#1
(byte) pow2::return
(byte) pow2::return#0
(byte) pow2::return#1
(byte) pow2::return#2
Adding number conversion cast (unumber) 6 in stackpush(byte) ← (number) 6
Adding number conversion cast (unumber) 0 in (bool~) pow2::$0 ← (byte) pow2::n#0 == (number) 0
Adding number conversion cast (unumber) 1 in (byte) pow2::return#0 ← (number) 1
Adding number conversion cast (unumber) 1 in (number~) pow2::$1 ← (byte) pow2::n#1 - (number) 1
Adding number conversion cast (unumber) pow2::$1 in (number~) pow2::$1 ← (byte) pow2::n#1 - (unumber)(number) 1
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast stackpush(byte) ← (unumber)(number) 6
Inlining cast (byte) pow2::return#0 ← (unumber)(number) 1
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (byte*) 1024
Simplifying constant integer cast 6
Simplifying constant integer cast 0
Simplifying constant integer cast 1
Simplifying constant integer cast 1
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (byte) 6
Finalized unsigned number type (byte) 0
Finalized unsigned number type (byte) 1
Finalized unsigned number type (byte) 1
Successful SSA optimization PassNFinalizeNumberTypeConversions
Inferred type updated to byte in (unumber~) pow2::$1 ← (byte) pow2::n#1 - (byte) 1
Alias pow2::n#0 = pow2::n#1
Alias pow2::c#0 = pow2::$2
Alias pow2::return#1 = pow2::$3
Successful SSA optimization Pass2AliasElimination
Simple Condition (bool~) pow2::$0 [7] if((byte) pow2::n#0==(byte) 0) goto pow2::@1
Successful SSA optimization Pass2ConditionalJumpSimplification
Constant (const byte) pow2::return#0 = 1
Successful SSA optimization Pass2ConstantIdentification
Inlining constant with var siblings (const byte) pow2::return#0
Constant inlined pow2::return#0 = (byte) 1
Successful SSA optimization Pass2ConstantInlining
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
Adding NOP phi() at start of pow2::@1
CALL GRAPH
Calls in [] to main:2
Calls in [main] to pow2:6
Calls in [pow2] to pow2:14
Created 1 initial phi equivalence classes
Coalesced [17] pow2::return#3 ← pow2::return#1
Coalesced down to 1 phi equivalence classes
Culled Empty Block (label) @3
Culled Empty Block (label) pow2::@1
Renumbering block @2 to @1
Renumbering block pow2::@3 to pow2::@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()
(void()) main()
main: scope:[main] from @1
[4] stackpush(byte) ← (byte) 6
[5] callexecute pow2
[6] (byte~) main::$0 ← stackpull(byte)
[7] *((const nomodify byte*) SCREEN) ← (byte~) main::$0
to:main::@return
main::@return: scope:[main] from main
[8] return
to:@return
__stackcall (byte()) pow2((byte) pow2::n)
pow2: scope:[pow2] from
[9] (byte) pow2::n#0 ← stackidx(byte,(const byte) pow2::OFFSET_STACK_N)
[10] if((byte) pow2::n#0==(byte) 0) goto pow2::@return
to:pow2::@1
pow2::@1: scope:[pow2] from pow2
[11] (byte~) pow2::$1 ← (byte) pow2::n#0 - (byte) 1
[12] stackpush(byte) ← (byte~) pow2::$1
[13] callexecute pow2
[14] (byte) pow2::c#0 ← stackpull(byte)
[15] (byte) pow2::return#1 ← (byte) pow2::c#0 + (byte) pow2::c#0
to:pow2::@return
pow2::@return: scope:[pow2] from pow2 pow2::@1
[16] (byte) pow2::return#2 ← phi( pow2/(byte) 1 pow2::@1/(byte) pow2::return#1 )
[17] stackidx(byte,(const byte) pow2::OFFSET_STACK_RETURN) ← (byte) pow2::return#2
[18] return
to:@return
VARIABLE REGISTER WEIGHTS
(void()) main()
(byte~) main::$0 22.0
__stackcall (byte()) pow2((byte) pow2::n)
(byte~) pow2::$1 22.0
(byte) pow2::c
(byte) pow2::c#0 33.0
(byte) pow2::n
(byte) pow2::n#0 16.5
(byte) pow2::return
(byte) pow2::return#1 22.0
(byte) pow2::return#2 22.0
Initial phi equivalence classes
[ pow2::return#2 pow2::return#1 ]
Added variable main::$0 to live range equivalence class [ main::$0 ]
Added variable pow2::n#0 to live range equivalence class [ pow2::n#0 ]
Added variable pow2::$1 to live range equivalence class [ pow2::$1 ]
Added variable pow2::c#0 to live range equivalence class [ pow2::c#0 ]
Complete equivalence classes
[ pow2::return#2 pow2::return#1 ]
[ main::$0 ]
[ pow2::n#0 ]
[ pow2::$1 ]
[ pow2::c#0 ]
Allocated zp[1]:2 [ pow2::return#2 pow2::return#1 ]
Allocated zp[1]:3 [ main::$0 ]
Allocated zp[1]:4 [ pow2::n#0 ]
Allocated zp[1]:5 [ pow2::$1 ]
Allocated zp[1]:6 [ pow2::c#0 ]
INITIAL ASM
Target platform is c64basic / MOS6502X
// File Comments
// Test a procedure with calling convention stack
// Recursion that works (no local variables)
// Upstart
.pc = $801 "Basic"
:BasicUpstart(__bbegin)
.pc = $80d "Program"
// Global Constants & labels
.label SCREEN = $400
.const STACK_BASE = $103
// @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: {
.label __0 = 3
// [4] stackpush(byte) ← (byte) 6 -- _stackpushbyte_=vbuc1
lda #6
pha
// [5] callexecute pow2 -- jsr
jsr pow2
// [6] (byte~) main::$0 ← stackpull(byte) -- vbuz1=_stackpullbyte_
pla
sta.z __0
// [7] *((const nomodify byte*) SCREEN) ← (byte~) main::$0 -- _deref_pbuc1=vbuz1
lda.z __0
sta SCREEN
jmp __breturn
// main::@return
__breturn:
// [8] return
rts
}
// pow2
// pow2(byte zp(4) n)
pow2: {
.const OFFSET_STACK_N = 0
.const OFFSET_STACK_RETURN = 0
.label __1 = 5
.label n = 4
.label c = 6
.label return = 2
// [9] (byte) pow2::n#0 ← stackidx(byte,(const byte) pow2::OFFSET_STACK_N) -- vbuz1=_stackidxbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_N,x
sta.z n
// [10] if((byte) pow2::n#0==(byte) 0) goto pow2::@return -- vbuz1_eq_0_then_la1
lda.z n
cmp #0
beq __breturn_from_pow2
jmp __b1
// pow2::@1
__b1:
// [11] (byte~) pow2::$1 ← (byte) pow2::n#0 - (byte) 1 -- vbuz1=vbuz2_minus_1
ldx.z n
dex
stx.z __1
// [12] stackpush(byte) ← (byte~) pow2::$1 -- _stackpushbyte_=vbuz1
lda.z __1
pha
// [13] callexecute pow2 -- jsr
jsr pow2
// [14] (byte) pow2::c#0 ← stackpull(byte) -- vbuz1=_stackpullbyte_
pla
sta.z c
// [15] (byte) pow2::return#1 ← (byte) pow2::c#0 + (byte) pow2::c#0 -- vbuz1=vbuz2_plus_vbuz2
lda.z c
asl
sta.z return
// [16] phi from pow2::@1 to pow2::@return [phi:pow2::@1->pow2::@return]
__breturn_from___b1:
// [16] phi (byte) pow2::return#2 = (byte) pow2::return#1 [phi:pow2::@1->pow2::@return#0] -- register_copy
jmp __breturn
// [16] phi from pow2 to pow2::@return [phi:pow2->pow2::@return]
__breturn_from_pow2:
// [16] phi (byte) pow2::return#2 = (byte) 1 [phi:pow2->pow2::@return#0] -- vbuz1=vbuc1
lda #1
sta.z return
jmp __breturn
// pow2::@return
__breturn:
// [17] stackidx(byte,(const byte) pow2::OFFSET_STACK_RETURN) ← (byte) pow2::return#2 -- _stackidxbyte_vbuc1=vbuz1
lda.z return
tsx
sta STACK_BASE+OFFSET_STACK_RETURN,x
// [18] return
rts
}
// File Data
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [4] stackpush(byte) ← (byte) 6 [ ] ( main:2 [ ] { } ) always clobbers reg byte a
Statement [6] (byte~) main::$0 ← stackpull(byte) [ main::$0 ] ( main:2 [ main::$0 ] { } ) always clobbers reg byte a
Statement [9] (byte) pow2::n#0 ← stackidx(byte,(const byte) pow2::OFFSET_STACK_N) [ pow2::n#0 ] ( main:2::pow2:5 [ pow2::n#0 ] { } ) always clobbers reg byte a reg byte x
Statement [14] (byte) pow2::c#0 ← stackpull(byte) [ pow2::c#0 ] ( main:2::pow2:5 [ pow2::c#0 ] { } ) always clobbers reg byte a
Statement [15] (byte) pow2::return#1 ← (byte) pow2::c#0 + (byte) pow2::c#0 [ pow2::return#1 ] ( main:2::pow2:5 [ pow2::return#1 ] { } ) always clobbers reg byte a
Statement [17] stackidx(byte,(const byte) pow2::OFFSET_STACK_RETURN) ← (byte) pow2::return#2 [ ] ( main:2::pow2:5 [ ] { } ) always clobbers reg byte x
Potential registers zp[1]:2 [ pow2::return#2 pow2::return#1 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:3 [ main::$0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:4 [ pow2::n#0 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:5 [ pow2::$1 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:6 [ pow2::c#0 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
REGISTER UPLIFT SCOPES
Uplift Scope [pow2] 44: zp[1]:2 [ pow2::return#2 pow2::return#1 ] 33: zp[1]:6 [ pow2::c#0 ] 22: zp[1]:5 [ pow2::$1 ] 16.5: zp[1]:4 [ pow2::n#0 ]
Uplift Scope [main] 22: zp[1]:3 [ main::$0 ]
Uplift Scope []
Uplifting [pow2] best 99 combination reg byte a [ pow2::return#2 pow2::return#1 ] reg byte a [ pow2::c#0 ] reg byte a [ pow2::$1 ] reg byte a [ pow2::n#0 ]
Limited combination testing to 100 combinations of 256 possible.
Uplifting [main] best 93 combination reg byte a [ main::$0 ]
Uplifting [] best 93 combination
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
// Test a procedure with calling convention stack
// Recursion that works (no local variables)
// Upstart
.pc = $801 "Basic"
:BasicUpstart(__bbegin)
.pc = $80d "Program"
// Global Constants & labels
.label SCREEN = $400
.const STACK_BASE = $103
// @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: {
// [4] stackpush(byte) ← (byte) 6 -- _stackpushbyte_=vbuc1
lda #6
pha
// [5] callexecute pow2 -- jsr
jsr pow2
// [6] (byte~) main::$0 ← stackpull(byte) -- vbuaa=_stackpullbyte_
pla
// [7] *((const nomodify byte*) SCREEN) ← (byte~) main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
jmp __breturn
// main::@return
__breturn:
// [8] return
rts
}
// pow2
// pow2(byte register(A) n)
pow2: {
.const OFFSET_STACK_N = 0
.const OFFSET_STACK_RETURN = 0
// [9] (byte) pow2::n#0 ← stackidx(byte,(const byte) pow2::OFFSET_STACK_N) -- vbuaa=_stackidxbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_N,x
// [10] if((byte) pow2::n#0==(byte) 0) goto pow2::@return -- vbuaa_eq_0_then_la1
cmp #0
beq __breturn_from_pow2
jmp __b1
// pow2::@1
__b1:
// [11] (byte~) pow2::$1 ← (byte) pow2::n#0 - (byte) 1 -- vbuaa=vbuaa_minus_1
sec
sbc #1
// [12] stackpush(byte) ← (byte~) pow2::$1 -- _stackpushbyte_=vbuaa
pha
// [13] callexecute pow2 -- jsr
jsr pow2
// [14] (byte) pow2::c#0 ← stackpull(byte) -- vbuaa=_stackpullbyte_
pla
// [15] (byte) pow2::return#1 ← (byte) pow2::c#0 + (byte) pow2::c#0 -- vbuaa=vbuaa_plus_vbuaa
asl
// [16] phi from pow2::@1 to pow2::@return [phi:pow2::@1->pow2::@return]
__breturn_from___b1:
// [16] phi (byte) pow2::return#2 = (byte) pow2::return#1 [phi:pow2::@1->pow2::@return#0] -- register_copy
jmp __breturn
// [16] phi from pow2 to pow2::@return [phi:pow2->pow2::@return]
__breturn_from_pow2:
// [16] phi (byte) pow2::return#2 = (byte) 1 [phi:pow2->pow2::@return#0] -- vbuaa=vbuc1
lda #1
jmp __breturn
// pow2::@return
__breturn:
// [17] stackidx(byte,(const byte) pow2::OFFSET_STACK_RETURN) ← (byte) pow2::return#2 -- _stackidxbyte_vbuc1=vbuaa
tsx
sta STACK_BASE+OFFSET_STACK_RETURN,x
// [18] return
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __b1
Removing instruction jmp __bend
Removing instruction jmp __breturn
Removing instruction jmp __b1
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 __b1:
Removing instruction __breturn_from___b1:
Succesful ASM optimization Pass5UnusedLabelElimination
Updating BasicUpstart to call main directly
Removing instruction jsr main
Succesful ASM optimization Pass5SkipBegin
Relabelling long label __breturn_from_pow2 to __b1
Succesful ASM optimization Pass5RelabelLongLabels
Removing instruction __bbegin:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
(label) @1
(label) @begin
(label) @end
(const nomodify byte*) SCREEN = (byte*) 1024
(const word) STACK_BASE = (word) $103
(void()) main()
(byte~) main::$0 reg byte a 22.0
(label) main::@return
__stackcall (byte()) pow2((byte) pow2::n)
(byte~) pow2::$1 reg byte a 22.0
(label) pow2::@1
(label) pow2::@return
(const byte) pow2::OFFSET_STACK_N = (byte) 0
(const byte) pow2::OFFSET_STACK_RETURN = (byte) 0
(byte) pow2::c
(byte) pow2::c#0 reg byte a 33.0
(byte) pow2::n
(byte) pow2::n#0 reg byte a 16.5
(byte) pow2::return
(byte) pow2::return#1 reg byte a 22.0
(byte) pow2::return#2 reg byte a 22.0
reg byte a [ pow2::return#2 pow2::return#1 ]
reg byte a [ main::$0 ]
reg byte a [ pow2::n#0 ]
reg byte a [ pow2::$1 ]
reg byte a [ pow2::c#0 ]
FINAL ASSEMBLER
Score: 72
// File Comments
// Test a procedure with calling convention stack
// Recursion that works (no local variables)
// Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
// Global Constants & labels
.label SCREEN = $400
.const STACK_BASE = $103
// @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: {
// pow2(6)
// [4] stackpush(byte) ← (byte) 6 -- _stackpushbyte_=vbuc1
lda #6
pha
// [5] callexecute pow2 -- jsr
jsr pow2
// [6] (byte~) main::$0 ← stackpull(byte) -- vbuaa=_stackpullbyte_
pla
// *SCREEN = pow2(6)
// [7] *((const nomodify byte*) SCREEN) ← (byte~) main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// main::@return
// }
// [8] return
rts
}
// pow2
// pow2(byte register(A) n)
pow2: {
.const OFFSET_STACK_N = 0
.const OFFSET_STACK_RETURN = 0
// [9] (byte) pow2::n#0 ← stackidx(byte,(const byte) pow2::OFFSET_STACK_N) -- vbuaa=_stackidxbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_N,x
// if (n == 0)
// [10] if((byte) pow2::n#0==(byte) 0) goto pow2::@return -- vbuaa_eq_0_then_la1
cmp #0
beq __b1
// pow2::@1
// n-1
// [11] (byte~) pow2::$1 ← (byte) pow2::n#0 - (byte) 1 -- vbuaa=vbuaa_minus_1
sec
sbc #1
// pow2(n-1)
// [12] stackpush(byte) ← (byte~) pow2::$1 -- _stackpushbyte_=vbuaa
pha
// [13] callexecute pow2 -- jsr
jsr pow2
// c = pow2(n-1)
// [14] (byte) pow2::c#0 ← stackpull(byte) -- vbuaa=_stackpullbyte_
pla
// return c+c;
// [15] (byte) pow2::return#1 ← (byte) pow2::c#0 + (byte) pow2::c#0 -- vbuaa=vbuaa_plus_vbuaa
asl
// [16] phi from pow2::@1 to pow2::@return [phi:pow2::@1->pow2::@return]
// [16] phi (byte) pow2::return#2 = (byte) pow2::return#1 [phi:pow2::@1->pow2::@return#0] -- register_copy
jmp __breturn
// [16] phi from pow2 to pow2::@return [phi:pow2->pow2::@return]
__b1:
// [16] phi (byte) pow2::return#2 = (byte) 1 [phi:pow2->pow2::@return#0] -- vbuaa=vbuc1
lda #1
// pow2::@return
__breturn:
// }
// [17] stackidx(byte,(const byte) pow2::OFFSET_STACK_RETURN) ← (byte) pow2::return#2 -- _stackidxbyte_vbuc1=vbuaa
tsx
sta STACK_BASE+OFFSET_STACK_RETURN,x
// [18] return
rts
}
// File Data

View File

@ -0,0 +1,27 @@
(label) @1
(label) @begin
(label) @end
(const nomodify byte*) SCREEN = (byte*) 1024
(const word) STACK_BASE = (word) $103
(void()) main()
(byte~) main::$0 reg byte a 22.0
(label) main::@return
__stackcall (byte()) pow2((byte) pow2::n)
(byte~) pow2::$1 reg byte a 22.0
(label) pow2::@1
(label) pow2::@return
(const byte) pow2::OFFSET_STACK_N = (byte) 0
(const byte) pow2::OFFSET_STACK_RETURN = (byte) 0
(byte) pow2::c
(byte) pow2::c#0 reg byte a 33.0
(byte) pow2::n
(byte) pow2::n#0 reg byte a 16.5
(byte) pow2::return
(byte) pow2::return#1 reg byte a 22.0
(byte) pow2::return#2 reg byte a 22.0
reg byte a [ pow2::return#2 pow2::return#1 ]
reg byte a [ main::$0 ]
reg byte a [ pow2::n#0 ]
reg byte a [ pow2::$1 ]
reg byte a [ pow2::c#0 ]