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:
parent
3b6c14be7a
commit
5b2e993994
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
|
0
src/main/kc/include/math.h
Normal file
0
src/main/kc/include/math.h
Normal file
3
src/main/kc/include/stdio.h
Normal file
3
src/main/kc/include/stdio.h
Normal file
@ -0,0 +1,3 @@
|
||||
// Functions for performing input and output.
|
||||
|
||||
#include <printf.h>
|
@ -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
0
src/main/kc/lib/math.c
Normal 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;
|
||||
}
|
||||
|
@ -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");
|
||||
|
18
src/test/kc/procedure-callingconvention-stack-13.c
Normal file
18
src/test/kc/procedure-callingconvention-stack-13.c
Normal 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;
|
||||
}
|
||||
|
||||
}
|
46
src/test/ref/procedure-callingconvention-stack-13.asm
Normal file
46
src/test/ref/procedure-callingconvention-stack-13.asm
Normal 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
|
||||
}
|
38
src/test/ref/procedure-callingconvention-stack-13.cfg
Normal file
38
src/test/ref/procedure-callingconvention-stack-13.cfg
Normal 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
|
564
src/test/ref/procedure-callingconvention-stack-13.log
Normal file
564
src/test/ref/procedure-callingconvention-stack-13.log
Normal 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
|
||||
|
27
src/test/ref/procedure-callingconvention-stack-13.sym
Normal file
27
src/test/ref/procedure-callingconvention-stack-13.sym
Normal 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 ]
|
Loading…
Reference in New Issue
Block a user