1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2025-02-08 12:30:58 +00:00

Renamed stack-manipulating fragments. Added fragment-based stack clean-up. #316

This commit is contained in:
jespergravgaard 2019-09-20 16:27:50 +02:00
parent 5e1c05dc2e
commit bcddd821ff
31 changed files with 1287 additions and 37 deletions

View File

@ -0,0 +1 @@
pla

View File

@ -0,0 +1,2 @@
pla
pla

View File

@ -0,0 +1,3 @@
pla
pla
pla

View File

@ -0,0 +1,4 @@
pla
pla
pla
pla

View File

@ -0,0 +1,5 @@
pla
pla
pla
pla
pla

View File

@ -0,0 +1,6 @@
pla
pla
pla
pla
pla
pla

View File

@ -0,0 +1,7 @@
pla
pla
pla
pla
pla
pla
pla

View File

@ -0,0 +1,8 @@
pla
pla
pla
pla
pla
pla
pla
pla

View File

@ -0,0 +1,4 @@
lda #>{c1}
pha
lda #<{c1}
pha

View File

@ -0,0 +1,5 @@
tsx
lda STACK_BASE+{c1},x
sta {z1}
lda STACK_BASE+{c1}+1,x
sta {z1}+1

View File

@ -0,0 +1,4 @@
tsx
txa
axs #-3
txs

View File

@ -0,0 +1,4 @@
tsx
txa
axs #-4
txs

View File

@ -0,0 +1,4 @@
tsx
txa
axs #-5
txs

View File

@ -0,0 +1,4 @@
tsx
txa
axs #-6
txs

View File

@ -0,0 +1,4 @@
tsx
txa
axs #-7
txs

View File

@ -0,0 +1,4 @@
tsx
txa
axs #-8
txs

View File

@ -358,9 +358,9 @@ public class AsmFragmentInstanceSpecFactory {
return name;
} else if(value instanceof ParamStackValue) {
ParamStackValue paramStackValue = (ParamStackValue) value;
return "_stack"+paramStackValue.getValueType().getTypeName()+"_"+bind(paramStackValue.getStackOffset());
return "_stackget"+paramStackValue.getValueType().getTypeName()+"_"+bind(paramStackValue.getStackOffset());
} else if(value instanceof ParamStackPush) {
return "_push"+((ParamStackPush) value).getType().getTypeName()+"_";
return "_stackpush"+((ParamStackPush) value).getType().getTypeName()+"_";
}
throw new RuntimeException("Binding of value type not supported " + value.toString(program));
}

View File

@ -699,7 +699,7 @@ public class Pass4CodeGeneration {
StatementAssignment assignment = (StatementAssignment) statement;
AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(assignment, assignmentAlu, program);
ensureEncoding(asm, asmFragmentInstanceSpecFactory);
generateAsm(asm, asmFragmentInstanceSpecFactory);
generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec());
aluState.clear();
return;
}
@ -725,13 +725,13 @@ public class Pass4CodeGeneration {
} else {
AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(assignment, program);
ensureEncoding(asm, asmFragmentInstanceSpecFactory);
generateAsm(asm, asmFragmentInstanceSpecFactory);
generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec());
}
}
} else if(statement instanceof StatementConditionalJump) {
AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory((StatementConditionalJump) statement, block, program, getGraph());
ensureEncoding(asm, asmFragmentInstanceSpecFactory);
generateAsm(asm, asmFragmentInstanceSpecFactory);
generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec());
} else if(statement instanceof StatementCall) {
StatementCall call = (StatementCall) statement;
@ -753,9 +753,12 @@ public class Pass4CodeGeneration {
for(RValue parameter : call.getParameters()) {
SymbolType parameterType = SymbolTypeInference.inferType(program.getScope(), parameter);
AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(new ParamStackPush(parameterType), parameter, program, block.getScope());
asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo));
ensureEncoding(asm, asmFragmentInstanceSpecFactory);
generateAsm(asm, asmFragmentInstanceSpecFactory);
generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec());
}
asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo));
asm.getCurrentChunk().setFragment("jsr");
}
asm.addInstruction("jsr", AsmAddressingMode.ABS, call.getProcedure().getFullName(), false);
// Clean up the stack
@ -765,10 +768,10 @@ public class Pass4CodeGeneration {
SymbolType parameterType = SymbolTypeInference.inferType(program.getScope(), parameter);
parameterBytes += parameterType.getSizeBytes();
}
// TODO: Replace with fragment - to allow hand-coded handling of the stack pointer modifications - eg. using TSX, TXA, AXS #{}, TXS
for(int i = 0; i < parameterBytes; i++) {
asm.addInstruction("pla", AsmAddressingMode.NON, null, false);
}
String pullSignature = "_stackpullbyte_" + Integer.toString(parameterBytes);
AsmFragmentInstanceSpec pullFragmentInstanceSpec = new AsmFragmentInstanceSpec(program, pullSignature, new LinkedHashMap<>(), block.getScope());
asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo));
generateAsm(asm, pullFragmentInstanceSpec);
}
} else if(statement instanceof StatementReturn) {
Procedure.InterruptType interruptType = null;
@ -866,24 +869,23 @@ public class Pass4CodeGeneration {
* Generate ASM code for an ASM fragment instance
*
* @param asm The ASM program to generate into
* @param asmFragmentInstanceSpecFactory The ASM fragment instance specification factory
* @param fragmentInstanceSpec The ASM fragment instance specification
*/
private void generateAsm(AsmProgram asm, AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory) {
String initialSignature = asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec().getSignature();
AsmFragmentInstanceSpec asmFragmentInstanceSpec = asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec();
AsmFragmentInstance asmFragmentInstance = null;
StringBuffer fragmentVariationsTried = new StringBuffer();
while(asmFragmentInstance == null) {
private void generateAsm(AsmProgram asm, AsmFragmentInstanceSpec fragmentInstanceSpec) {
String initialSignature = fragmentInstanceSpec.getSignature();
AsmFragmentInstance fragmentInstance = null;
StringBuilder fragmentVariationsTried = new StringBuilder();
while(fragmentInstance == null) {
try {
asmFragmentInstance = program.getAsmFragmentSynthesizer().getFragmentInstance(asmFragmentInstanceSpec, program.getLog());
fragmentInstance = program.getAsmFragmentSynthesizer().getFragmentInstance(fragmentInstanceSpec, program.getLog());
} catch(AsmFragmentTemplateSynthesizer.UnknownFragmentException e) {
// Unknown fragment - keep looking through alternative ASM fragment instance specs until we have tried them all
String signature = asmFragmentInstanceSpec.getSignature();
String signature = fragmentInstanceSpec.getSignature();
fragmentVariationsTried.append(signature).append(" ");
if(asmFragmentInstanceSpec.hasNextVariation()) {
asmFragmentInstanceSpec.nextVariation();
if(fragmentInstanceSpec.hasNextVariation()) {
fragmentInstanceSpec.nextVariation();
if(program.getLog().isVerboseFragmentLog()) {
program.getLog().append("Fragment not found " + signature + ". Attempting another variation " + asmFragmentInstanceSpec.getSignature());
program.getLog().append("Fragment not found " + signature + ". Attempting another variation " + fragmentInstanceSpec.getSignature());
}
} else {
// No more variations available - fail with an error
@ -891,8 +893,8 @@ public class Pass4CodeGeneration {
}
}
}
asm.getCurrentChunk().setFragment(asmFragmentInstance.getFragmentName());
asmFragmentInstance.generate(asm);
asm.getCurrentChunk().setFragment(fragmentInstance.getFragmentName());
fragmentInstance.generate(asm);
}
/**
@ -1047,7 +1049,7 @@ public class Pass4CodeGeneration {
} else {
AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(lValue, rValue, program, scope);
ensureEncoding(asm, asmFragmentInstanceSpecFactory);
generateAsm(asm, asmFragmentInstanceSpecFactory);
generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec());
}
}
transitionSetGenerated(transition);

View File

@ -35,12 +35,15 @@ public class TestPrograms {
public TestPrograms() {
}
/*
@Test
public void testProcedureCallingConventionStack2() throws IOException, URISyntaxException {
compileAndCompare("procedure-callingconvention-stack-2"); //, log().verboseCreateSsa().verboseParse().verboseStatementSequence());
}
@Test
public void testProcedureCallingConventionStack1() throws IOException, URISyntaxException {
compileAndCompare("procedure-callingconvention-stack-1", log());
compileAndCompare("procedure-callingconvention-stack-1");
}
*/
@Test
public void testProcedureCallingConventionStack0() throws IOException, URISyntaxException {

View File

@ -0,0 +1,11 @@
// Test a procedure with calling convention stack - and enough parameters to use fast ASM for cleaning stack
const word* SCREEN = 0x0400;
void main(void) {
SCREEN[0] = plus(0x1234, 0x2345);
}
word __stackcall plus(word a, word b) {
return a+b;
}

View File

@ -181,12 +181,16 @@ bend:
// main
main: {
.label _0 = 2
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _pushbyte_=vbuc1
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #'0'
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #7
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- jsr
jsr plus
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpullbyte_2
pla
pla
// [6] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuz1
@ -206,11 +210,11 @@ plus: {
.label a = 3
.label b = 4
.label return = 5
// [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackbyte_vbuc1
// [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
// [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuz1=_stackbyte_vbuc1
// [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuz1=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
sta.z b
@ -285,12 +289,16 @@ bend_from_b1:
bend:
// main
main: {
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _pushbyte_=vbuc1
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #'0'
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #7
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- jsr
jsr plus
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpullbyte_2
pla
pla
// [6] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuyy
@ -307,11 +315,11 @@ plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.label a = 2
// [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackbyte_vbuc1
// [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
// [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackbyte_vbuc1
// [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
// [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuaa=vbuz1_plus_vbuaa
@ -395,12 +403,16 @@ Score: 60
// main
main: {
// plus('0', 7)
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _pushbyte_=vbuc1
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #'0'
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #7
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- jsr
jsr plus
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpullbyte_2
pla
pla
// SCREEN[0] = plus('0', 7)
@ -417,11 +429,11 @@ plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.label a = 2
// [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackbyte_vbuc1
// [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
// [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackbyte_vbuc1
// [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
// return a+b;

View File

@ -0,0 +1,31 @@
// Test a procedure with calling convention stack
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
.label SCREEN = $400
.const STACK_BASE = $103
main: {
lda #'0'
pha
lda #7
pha
jsr plus
pla
pla
sty SCREEN
rts
}
// plus(byte zeropage(2) a, byte register(A) b)
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.label a = 2
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
tsx
lda STACK_BASE+OFFSET_STACK_B,x
clc
adc.z a
rts
}

View File

@ -0,0 +1,29 @@
@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] phi()
[5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7
[6] *((const byte*) SCREEN#0) ← (byte~) main::$0
to:main::@return
main::@return: scope:[main] from main
[7] return
to:@return
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
plus: scope:[plus] from
[8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A)
[9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B)
[10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0
to:plus::@return
plus::@return: scope:[plus] from plus
[11] return (byte) plus::return#0
to:@return

View File

@ -0,0 +1,449 @@
Culled Empty Block (label) @1
Culled Empty Block (label) plus::@1
CONTROL FLOW GRAPH SSA
@begin: scope:[] from
(byte*) SCREEN#0 ← ((byte*)) (number) $400
to:@2
(void()) main()
main: scope:[main] from @2
(byte~) main::$0 ← call plus (byte) '0' (number) 7
*((byte*) SCREEN#0 + (number) 0) ← (byte~) main::$0
to:main::@return
main::@return: scope:[main] from main
return
to:@return
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
plus: scope:[plus] from
(byte) plus::a#0 ← param((byte) plus::a)
(byte) plus::b#0 ← param((byte) plus::b)
(byte~) plus::$0 ← (byte) plus::a#0 + (byte) plus::b#0
(byte) plus::return#0 ← (byte~) plus::$0
to:plus::@return
plus::@return: scope:[plus] from plus
(byte) plus::return#1 ← phi( plus/(byte) plus::return#0 )
return (byte) plus::return#1
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*) SCREEN
(byte*) SCREEN#0
(void()) main()
(byte~) main::$0
(label) main::@return
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
(byte~) plus::$0
(label) plus::@return
(byte) plus::a
(byte) plus::a#0
(byte) plus::b
(byte) plus::b#0
(byte) plus::return
(byte) plus::return#0
(byte) plus::return#1
Adding number conversion cast (unumber) 7 in (byte~) main::$0 ← call plus (byte) '0' (number) 7
Adding number conversion cast (unumber) 0 in *((byte*) SCREEN#0 + (number) 0) ← (byte~) main::$0
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast (byte*) SCREEN#0 ← (byte*)(number) $400
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (byte*) 1024
Simplifying constant integer cast 7
Simplifying constant integer cast 0
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (byte) 7
Finalized unsigned number type (byte) 0
Successful SSA optimization PassNFinalizeNumberTypeConversions
Alias (byte) plus::return#0 = (byte~) plus::$0 (byte) plus::return#1
Successful SSA optimization Pass2AliasElimination
Constant (const byte*) SCREEN#0 = (byte*) 1024
Successful SSA optimization Pass2ConstantIdentification
Simplifying expression containing zero SCREEN#0 in [2] *((const byte*) SCREEN#0 + (byte) 0) ← (byte~) main::$0
Successful SSA optimization PassNSimplifyExpressionWithZero
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 main
CALL GRAPH
Calls in [] to main:2
Calls in [main] to plus:6
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
Adding NOP phi() at start of main
Calling convention STACK_CALL replacing param((byte) plus::a) with paramstack(byte,plus::OFFSET_STACK_A)
Calling convention STACK_CALL replacing param((byte) plus::b) with paramstack(byte,plus::OFFSET_STACK_B)
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] phi()
[5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7
[6] *((const byte*) SCREEN#0) ← (byte~) main::$0
to:main::@return
main::@return: scope:[main] from main
[7] return
to:@return
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
plus: scope:[plus] from
[8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A)
[9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B)
[10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0
to:plus::@return
plus::@return: scope:[plus] from plus
[11] return (byte) plus::return#0
to:@return
VARIABLE REGISTER WEIGHTS
(byte*) SCREEN
(void()) main()
(byte~) main::$0 0.5
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
(byte) plus::a
(byte) plus::a#0 2.0
(byte) plus::b
(byte) plus::b#0 4.0
(byte) plus::return
(byte) plus::return#0 2.0
Initial phi equivalence classes
Added variable main::$0 to zero page equivalence class [ main::$0 ]
Added variable plus::a#0 to zero page equivalence class [ plus::a#0 ]
Added variable plus::b#0 to zero page equivalence class [ plus::b#0 ]
Added variable plus::return#0 to zero page equivalence class [ plus::return#0 ]
Complete equivalence classes
[ main::$0 ]
[ plus::a#0 ]
[ plus::b#0 ]
[ plus::return#0 ]
Allocated zp ZP_BYTE:2 [ main::$0 ]
Allocated zp ZP_BYTE:3 [ plus::a#0 ]
Allocated zp ZP_BYTE:4 [ plus::b#0 ]
Allocated zp ZP_BYTE:5 [ plus::return#0 ]
INITIAL ASM
Target platform is c64basic / MOS6502X
// File Comments
// Test a procedure with calling convention stack
// 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
// [4] phi from @1 to main [phi:@1->main]
main_from_b1:
jsr main
// [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
// @end
bend:
// main
main: {
.label _0 = 2
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #'0'
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #7
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- jsr
jsr plus
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpullbyte_2
pla
pla
// [6] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuz1
lda.z _0
sta SCREEN
jmp breturn
// main::@return
breturn:
// [7] return
rts
}
// plus
// plus(byte zeropage(3) a, byte zeropage(4) b)
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.label a = 3
.label b = 4
.label return = 5
// [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
// [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuz1=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
sta.z b
// [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuz1=vbuz2_plus_vbuz3
lda.z a
clc
adc.z b
sta.z return
jmp breturn
// plus::@return
breturn:
// [11] return (byte) plus::return#0
rts
}
// File Data
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 [ main::$0 ] ( main:2 [ main::$0 ] ) always clobbers reg byte a
Statement [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) [ plus::a#0 ] ( main:2::plus:5 [ main::$0 plus::a#0 ] ) always clobbers reg byte a reg byte x
Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ main::$0 ]
Removing always clobbered register reg byte x as potential for zp ZP_BYTE:2 [ main::$0 ]
Statement [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) [ plus::a#0 plus::b#0 ] ( main:2::plus:5 [ main::$0 plus::a#0 plus::b#0 ] ) always clobbers reg byte a reg byte x
Removing always clobbered register reg byte a as potential for zp ZP_BYTE:3 [ plus::a#0 ]
Removing always clobbered register reg byte x as potential for zp ZP_BYTE:3 [ plus::a#0 ]
Statement [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 [ plus::return#0 ] ( main:2::plus:5 [ main::$0 plus::return#0 ] ) always clobbers reg byte a
Statement [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 [ main::$0 ] ( main:2 [ main::$0 ] ) always clobbers reg byte a
Statement [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) [ plus::a#0 ] ( main:2::plus:5 [ main::$0 plus::a#0 ] ) always clobbers reg byte a reg byte x
Statement [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) [ plus::a#0 plus::b#0 ] ( main:2::plus:5 [ main::$0 plus::a#0 plus::b#0 ] ) always clobbers reg byte a reg byte x
Statement [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 [ plus::return#0 ] ( main:2::plus:5 [ main::$0 plus::return#0 ] ) always clobbers reg byte a
Potential registers zp ZP_BYTE:2 [ main::$0 ] : zp ZP_BYTE:2 , reg byte y ,
Potential registers zp ZP_BYTE:3 [ plus::a#0 ] : zp ZP_BYTE:3 , reg byte y ,
Potential registers zp ZP_BYTE:4 [ plus::b#0 ] : zp ZP_BYTE:4 , reg byte a , reg byte x , reg byte y ,
Potential registers zp ZP_BYTE:5 [ plus::return#0 ] : zp ZP_BYTE:5 , reg byte a , reg byte x , reg byte y ,
REGISTER UPLIFT SCOPES
Uplift Scope [plus] 4: zp ZP_BYTE:4 [ plus::b#0 ] 2: zp ZP_BYTE:3 [ plus::a#0 ] 2: zp ZP_BYTE:5 [ plus::return#0 ]
Uplift Scope [main] 0.5: zp ZP_BYTE:2 [ main::$0 ]
Uplift Scope []
Uplifting [plus] best 81 combination reg byte a [ plus::b#0 ] zp ZP_BYTE:3 [ plus::a#0 ] reg byte a [ plus::return#0 ]
Uplifting [main] best 78 combination reg byte y [ main::$0 ]
Uplifting [] best 78 combination
Attempting to uplift remaining variables inzp ZP_BYTE:3 [ plus::a#0 ]
Uplifting [plus] best 78 combination zp ZP_BYTE:3 [ plus::a#0 ]
Allocated (was zp ZP_BYTE:3) zp ZP_BYTE:2 [ plus::a#0 ]
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
// Test a procedure with calling convention stack
// 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
// [4] phi from @1 to main [phi:@1->main]
main_from_b1:
jsr main
// [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
// @end
bend:
// main
main: {
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #'0'
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #7
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- jsr
jsr plus
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpullbyte_2
pla
pla
// [6] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuyy
sty SCREEN
jmp breturn
// main::@return
breturn:
// [7] return
rts
}
// plus
// plus(byte zeropage(2) a, byte register(A) b)
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.label a = 2
// [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
// [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
// [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuaa=vbuz1_plus_vbuaa
clc
adc.z a
jmp breturn
// plus::@return
breturn:
// [11] return (byte) plus::return#0
rts
}
// 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 main_from_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*) SCREEN
(const byte*) SCREEN#0 SCREEN = (byte*) 1024
(const word) STACK_BASE STACK_BASE = (word) $103
(void()) main()
(byte~) main::$0 reg byte y 0.5
(label) main::@return
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
(label) plus::@return
(const byte) plus::OFFSET_STACK_A OFFSET_STACK_A = (byte) 0
(const byte) plus::OFFSET_STACK_B OFFSET_STACK_B = (byte) 1
(byte) plus::a
(byte) plus::a#0 a zp ZP_BYTE:2 2.0
(byte) plus::b
(byte) plus::b#0 reg byte a 4.0
(byte) plus::return
(byte) plus::return#0 reg byte a 2.0
reg byte y [ main::$0 ]
zp ZP_BYTE:2 [ plus::a#0 ]
reg byte a [ plus::b#0 ]
reg byte a [ plus::return#0 ]
FINAL ASSEMBLER
Score: 60
// File Comments
// Test a procedure with calling convention stack
// 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
// [4] phi from @1 to main [phi:@1->main]
// [3] phi from @1 to @end [phi:@1->@end]
// @end
// main
main: {
// plus('0', 7)
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #'0'
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #7
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- jsr
jsr plus
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpullbyte_2
pla
pla
// SCREEN[0] = plus('0', 7)
// [6] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuyy
sty SCREEN
// main::@return
// }
// [7] return
rts
}
// plus
// plus(byte zeropage(2) a, byte register(A) b)
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.label a = 2
// [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
// [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
// return a+b;
// [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuaa=vbuz1_plus_vbuaa
clc
adc.z a
// plus::@return
// }
// [11] return (byte) plus::return#0
rts
}
// File Data

View File

@ -0,0 +1,24 @@
(label) @1
(label) @begin
(label) @end
(byte*) SCREEN
(const byte*) SCREEN#0 SCREEN = (byte*) 1024
(const word) STACK_BASE STACK_BASE = (word) $103
(void()) main()
(byte~) main::$0 reg byte y 0.5
(label) main::@return
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
(label) plus::@return
(const byte) plus::OFFSET_STACK_A OFFSET_STACK_A = (byte) 0
(const byte) plus::OFFSET_STACK_B OFFSET_STACK_B = (byte) 1
(byte) plus::a
(byte) plus::a#0 a zp ZP_BYTE:2 2.0
(byte) plus::b
(byte) plus::b#0 reg byte a 4.0
(byte) plus::return
(byte) plus::return#0 reg byte a 2.0
reg byte y [ main::$0 ]
zp ZP_BYTE:2 [ plus::a#0 ]
reg byte a [ plus::b#0 ]
reg byte a [ plus::return#0 ]

View File

@ -0,0 +1,53 @@
// Test a procedure with calling convention stack - and enough parameters to use fast ASM for cleaning stack
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
.label SCREEN = $400
.const STACK_BASE = $103
main: {
.label _0 = 2
lda #>$1234
pha
lda #<$1234
pha
lda #>$2345
pha
lda #<$2345
pha
jsr plus
tsx
txa
axs #-4
txs
lda.z _0
sta SCREEN
lda.z _0+1
sta SCREEN+1
rts
}
// plus(word zeropage(4) a, word zeropage(6) b)
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 2
.label a = 4
.label b = 6
.label return = 4
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
lda STACK_BASE+OFFSET_STACK_A+1,x
sta.z a+1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
sta.z b
lda STACK_BASE+OFFSET_STACK_B+1,x
sta.z b+1
lda.z return
clc
adc.z b
sta.z return
lda.z return+1
adc.z b+1
sta.z return+1
rts
}

View File

@ -0,0 +1,29 @@
@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] phi()
[5] (word~) main::$0 ← call plus (word) $1234 (word) $2345
[6] *((const word*) SCREEN#0) ← (word~) main::$0
to:main::@return
main::@return: scope:[main] from main
[7] return
to:@return
__stackcall (word()) plus((word) plus::a , (word) plus::b)
plus: scope:[plus] from
[8] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A)
[9] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B)
[10] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0
to:plus::@return
plus::@return: scope:[plus] from plus
[11] return (word) plus::return#0
to:@return

View File

@ -0,0 +1,515 @@
Fixing pointer array-indexing *((word*) SCREEN + (number) 0)
Culled Empty Block (label) @1
Culled Empty Block (label) plus::@1
CONTROL FLOW GRAPH SSA
@begin: scope:[] from
(word*) SCREEN#0 ← ((word*)) (number) $400
to:@2
(void()) main()
main: scope:[main] from @2
(word~) main::$0 ← call plus (number) $1234 (number) $2345
(number~) main::$1 ← (number) 0 * (const byte) SIZEOF_WORD
*((word*) SCREEN#0 + (number~) main::$1) ← (word~) main::$0
to:main::@return
main::@return: scope:[main] from main
return
to:@return
__stackcall (word()) plus((word) plus::a , (word) plus::b)
plus: scope:[plus] from
(word) plus::a#0 ← param((word) plus::a)
(word) plus::b#0 ← param((word) plus::b)
(word~) plus::$0 ← (word) plus::a#0 + (word) plus::b#0
(word) plus::return#0 ← (word~) plus::$0
to:plus::@return
plus::@return: scope:[plus] from plus
(word) plus::return#1 ← phi( plus/(word) plus::return#0 )
return (word) plus::return#1
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
(word*) SCREEN
(word*) SCREEN#0
(const byte) SIZEOF_WORD = (byte) 2
(void()) main()
(word~) main::$0
(number~) main::$1
(label) main::@return
__stackcall (word()) plus((word) plus::a , (word) plus::b)
(word~) plus::$0
(label) plus::@return
(word) plus::a
(word) plus::a#0
(word) plus::b
(word) plus::b#0
(word) plus::return
(word) plus::return#0
(word) plus::return#1
Adding number conversion cast (unumber) $1234 in (word~) main::$0 ← call plus (number) $1234 (number) $2345
Adding number conversion cast (unumber) $2345 in (word~) main::$0 ← call plus (unumber)(number) $1234 (number) $2345
Adding number conversion cast (unumber) 0 in (number~) main::$1 ← (number) 0 * (const byte) SIZEOF_WORD
Adding number conversion cast (unumber) main::$1 in (number~) main::$1 ← (unumber)(number) 0 * (const byte) SIZEOF_WORD
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast (word*) SCREEN#0 ← (word*)(number) $400
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (word*) 1024
Simplifying constant integer cast $1234
Simplifying constant integer cast $2345
Simplifying constant integer cast 0
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (word) $1234
Finalized unsigned number type (word) $2345
Finalized unsigned number type (byte) 0
Successful SSA optimization PassNFinalizeNumberTypeConversions
Inferred type updated to byte in (unumber~) main::$1 ← (byte) 0 * (const byte) SIZEOF_WORD
Alias (word) plus::return#0 = (word~) plus::$0 (word) plus::return#1
Successful SSA optimization Pass2AliasElimination
Constant right-side identified [2] (byte~) main::$1 ← (byte) 0 * (const byte) SIZEOF_WORD
Successful SSA optimization Pass2ConstantRValueConsolidation
Constant (const word*) SCREEN#0 = (word*) 1024
Constant (const byte) main::$1 = 0*SIZEOF_WORD
Successful SSA optimization Pass2ConstantIdentification
Simplifying constant evaluating to zero (byte) 0*(const byte) SIZEOF_WORD in
Successful SSA optimization PassNSimplifyConstantZero
Simplifying expression containing zero SCREEN#0 in [3] *((const word*) SCREEN#0 + (const byte) main::$1) ← (word~) main::$0
Successful SSA optimization PassNSimplifyExpressionWithZero
Eliminating unused constant (const byte) main::$1
Eliminating unused constant (const byte) SIZEOF_WORD
Successful SSA optimization PassNEliminateUnusedVars
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 main
CALL GRAPH
Calls in [] to main:2
Calls in [main] to plus:6
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
Adding NOP phi() at start of main
Calling convention STACK_CALL replacing param((word) plus::a) with paramstack(word,plus::OFFSET_STACK_A)
Calling convention STACK_CALL replacing param((word) plus::b) with paramstack(word,plus::OFFSET_STACK_B)
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] phi()
[5] (word~) main::$0 ← call plus (word) $1234 (word) $2345
[6] *((const word*) SCREEN#0) ← (word~) main::$0
to:main::@return
main::@return: scope:[main] from main
[7] return
to:@return
__stackcall (word()) plus((word) plus::a , (word) plus::b)
plus: scope:[plus] from
[8] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A)
[9] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B)
[10] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0
to:plus::@return
plus::@return: scope:[plus] from plus
[11] return (word) plus::return#0
to:@return
VARIABLE REGISTER WEIGHTS
(word*) SCREEN
(void()) main()
(word~) main::$0 0.5
__stackcall (word()) plus((word) plus::a , (word) plus::b)
(word) plus::a
(word) plus::a#0 2.0
(word) plus::b
(word) plus::b#0 4.0
(word) plus::return
(word) plus::return#0 2.0
Initial phi equivalence classes
Added variable main::$0 to zero page equivalence class [ main::$0 ]
Added variable plus::a#0 to zero page equivalence class [ plus::a#0 ]
Added variable plus::b#0 to zero page equivalence class [ plus::b#0 ]
Added variable plus::return#0 to zero page equivalence class [ plus::return#0 ]
Complete equivalence classes
[ main::$0 ]
[ plus::a#0 ]
[ plus::b#0 ]
[ plus::return#0 ]
Allocated zp ZP_WORD:2 [ main::$0 ]
Allocated zp ZP_WORD:4 [ plus::a#0 ]
Allocated zp ZP_WORD:6 [ plus::b#0 ]
Allocated zp ZP_WORD:8 [ plus::return#0 ]
INITIAL ASM
Target platform is c64basic / MOS6502X
// File Comments
// Test a procedure with calling convention stack - and enough parameters to use fast ASM for cleaning stack
// 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
// [4] phi from @1 to main [phi:@1->main]
main_from_b1:
jsr main
// [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
// @end
bend:
// main
main: {
.label _0 = 2
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1
lda #>$1234
pha
lda #<$1234
pha
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1
lda #>$2345
pha
lda #<$2345
pha
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- jsr
jsr plus
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpullbyte_4
tsx
txa
axs #-4
txs
// [6] *((const word*) SCREEN#0) ← (word~) main::$0 -- _deref_pwuc1=vwuz1
lda.z _0
sta SCREEN
lda.z _0+1
sta SCREEN+1
jmp breturn
// main::@return
breturn:
// [7] return
rts
}
// plus
// plus(word zeropage(4) a, word zeropage(6) b)
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 2
.label a = 4
.label b = 6
.label return = 8
// [8] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A) -- vwuz1=_stackgetword_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
lda STACK_BASE+OFFSET_STACK_A+1,x
sta.z a+1
// [9] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B) -- vwuz1=_stackgetword_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
sta.z b
lda STACK_BASE+OFFSET_STACK_B+1,x
sta.z b+1
// [10] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0 -- vwuz1=vwuz2_plus_vwuz3
lda.z a
clc
adc.z b
sta.z return
lda.z a+1
adc.z b+1
sta.z return+1
jmp breturn
// plus::@return
breturn:
// [11] return (word) plus::return#0
rts
}
// File Data
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 [ main::$0 ] ( main:2 [ main::$0 ] ) always clobbers reg byte a reg byte x
Statement [6] *((const word*) SCREEN#0) ← (word~) main::$0 [ ] ( main:2 [ ] ) always clobbers reg byte a
Statement [8] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A) [ plus::a#0 ] ( main:2::plus:5 [ main::$0 plus::a#0 ] ) always clobbers reg byte a reg byte x
Statement [9] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B) [ plus::a#0 plus::b#0 ] ( main:2::plus:5 [ main::$0 plus::a#0 plus::b#0 ] ) always clobbers reg byte a reg byte x
Statement [10] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0 [ plus::return#0 ] ( main:2::plus:5 [ main::$0 plus::return#0 ] ) always clobbers reg byte a
Potential registers zp ZP_WORD:2 [ main::$0 ] : zp ZP_WORD:2 ,
Potential registers zp ZP_WORD:4 [ plus::a#0 ] : zp ZP_WORD:4 ,
Potential registers zp ZP_WORD:6 [ plus::b#0 ] : zp ZP_WORD:6 ,
Potential registers zp ZP_WORD:8 [ plus::return#0 ] : zp ZP_WORD:8 ,
REGISTER UPLIFT SCOPES
Uplift Scope [plus] 4: zp ZP_WORD:6 [ plus::b#0 ] 2: zp ZP_WORD:4 [ plus::a#0 ] 2: zp ZP_WORD:8 [ plus::return#0 ]
Uplift Scope [main] 0.5: zp ZP_WORD:2 [ main::$0 ]
Uplift Scope []
Uplifting [plus] best 132 combination zp ZP_WORD:6 [ plus::b#0 ] zp ZP_WORD:4 [ plus::a#0 ] zp ZP_WORD:8 [ plus::return#0 ]
Uplifting [main] best 132 combination zp ZP_WORD:2 [ main::$0 ]
Uplifting [] best 132 combination
Coalescing zero page register [ zp ZP_WORD:4 [ plus::a#0 ] ] with [ zp ZP_WORD:8 [ plus::return#0 ] ] - score: 1
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
// Test a procedure with calling convention stack - and enough parameters to use fast ASM for cleaning stack
// 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
// [4] phi from @1 to main [phi:@1->main]
main_from_b1:
jsr main
// [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
// @end
bend:
// main
main: {
.label _0 = 2
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1
lda #>$1234
pha
lda #<$1234
pha
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1
lda #>$2345
pha
lda #<$2345
pha
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- jsr
jsr plus
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpullbyte_4
tsx
txa
axs #-4
txs
// [6] *((const word*) SCREEN#0) ← (word~) main::$0 -- _deref_pwuc1=vwuz1
lda.z _0
sta SCREEN
lda.z _0+1
sta SCREEN+1
jmp breturn
// main::@return
breturn:
// [7] return
rts
}
// plus
// plus(word zeropage(4) a, word zeropage(6) b)
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 2
.label a = 4
.label b = 6
.label return = 4
// [8] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A) -- vwuz1=_stackgetword_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
lda STACK_BASE+OFFSET_STACK_A+1,x
sta.z a+1
// [9] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B) -- vwuz1=_stackgetword_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
sta.z b
lda STACK_BASE+OFFSET_STACK_B+1,x
sta.z b+1
// [10] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0 -- vwuz1=vwuz1_plus_vwuz2
lda.z return
clc
adc.z b
sta.z return
lda.z return+1
adc.z b+1
sta.z return+1
jmp breturn
// plus::@return
breturn:
// [11] return (word) plus::return#0
rts
}
// 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 main_from_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
(word*) SCREEN
(const word*) SCREEN#0 SCREEN = (word*) 1024
(const word) STACK_BASE STACK_BASE = (word) $103
(void()) main()
(word~) main::$0 $0 zp ZP_WORD:2 0.5
(label) main::@return
__stackcall (word()) plus((word) plus::a , (word) plus::b)
(label) plus::@return
(const byte) plus::OFFSET_STACK_A OFFSET_STACK_A = (byte) 0
(const byte) plus::OFFSET_STACK_B OFFSET_STACK_B = (byte) 2
(word) plus::a
(word) plus::a#0 a zp ZP_WORD:4 2.0
(word) plus::b
(word) plus::b#0 b zp ZP_WORD:6 4.0
(word) plus::return
(word) plus::return#0 return zp ZP_WORD:4 2.0
zp ZP_WORD:2 [ main::$0 ]
zp ZP_WORD:4 [ plus::a#0 plus::return#0 ]
zp ZP_WORD:6 [ plus::b#0 ]
FINAL ASSEMBLER
Score: 114
// File Comments
// Test a procedure with calling convention stack - and enough parameters to use fast ASM for cleaning stack
// 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
// [4] phi from @1 to main [phi:@1->main]
// [3] phi from @1 to @end [phi:@1->@end]
// @end
// main
main: {
.label _0 = 2
// plus(0x1234, 0x2345)
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1
lda #>$1234
pha
lda #<$1234
pha
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1
lda #>$2345
pha
lda #<$2345
pha
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- jsr
jsr plus
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpullbyte_4
tsx
txa
axs #-4
txs
// SCREEN[0] = plus(0x1234, 0x2345)
// [6] *((const word*) SCREEN#0) ← (word~) main::$0 -- _deref_pwuc1=vwuz1
lda.z _0
sta SCREEN
lda.z _0+1
sta SCREEN+1
// main::@return
// }
// [7] return
rts
}
// plus
// plus(word zeropage(4) a, word zeropage(6) b)
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 2
.label a = 4
.label b = 6
.label return = 4
// [8] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A) -- vwuz1=_stackgetword_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
lda STACK_BASE+OFFSET_STACK_A+1,x
sta.z a+1
// [9] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B) -- vwuz1=_stackgetword_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
sta.z b
lda STACK_BASE+OFFSET_STACK_B+1,x
sta.z b+1
// return a+b;
// [10] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0 -- vwuz1=vwuz1_plus_vwuz2
lda.z return
clc
adc.z b
sta.z return
lda.z return+1
adc.z b+1
sta.z return+1
// plus::@return
// }
// [11] return (word) plus::return#0
rts
}
// File Data

View File

@ -0,0 +1,23 @@
(label) @1
(label) @begin
(label) @end
(word*) SCREEN
(const word*) SCREEN#0 SCREEN = (word*) 1024
(const word) STACK_BASE STACK_BASE = (word) $103
(void()) main()
(word~) main::$0 $0 zp ZP_WORD:2 0.5
(label) main::@return
__stackcall (word()) plus((word) plus::a , (word) plus::b)
(label) plus::@return
(const byte) plus::OFFSET_STACK_A OFFSET_STACK_A = (byte) 0
(const byte) plus::OFFSET_STACK_B OFFSET_STACK_B = (byte) 2
(word) plus::a
(word) plus::a#0 a zp ZP_WORD:4 2.0
(word) plus::b
(word) plus::b#0 b zp ZP_WORD:6 4.0
(word) plus::return
(word) plus::return#0 return zp ZP_WORD:4 2.0
zp ZP_WORD:2 [ main::$0 ]
zp ZP_WORD:4 [ plus::a#0 plus::return#0 ]
zp ZP_WORD:6 [ plus::b#0 ]