From 5e1c05dc2e7d07fd646f7607ae3c134148cd2440 Mon Sep 17 00:00:00 2001 From: Jesper Gravgaard Date: Fri, 20 Sep 2019 14:19:43 +0200 Subject: [PATCH] Added stack clean-up. Added parameter stack fetch type. #316 --- .../AsmFragmentInstanceSpecFactory.java | 4 +- .../kickc/model/values/ParamStackValue.java | 17 ++++++- .../kickc/passes/Pass4CodeGeneration.java | 13 +++++- .../passes/PassNCallingConventionStack.java | 4 +- .../procedure-callingconvention-stack-0.asm | 2 + .../procedure-callingconvention-stack-0.cfg | 4 +- .../procedure-callingconvention-stack-0.log | 44 +++++++++++-------- 7 files changed, 61 insertions(+), 27 deletions(-) diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java index 34c025d60..e5f2e4adf 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java @@ -357,8 +357,8 @@ public class AsmFragmentInstanceSpecFactory { bind(name, value); return name; } else if(value instanceof ParamStackValue) { - // TODO: Handle different parameter types! - return "_stackbyte_"+bind(((ParamStackValue) value).getStackOffset()); + ParamStackValue paramStackValue = (ParamStackValue) value; + return "_stack"+paramStackValue.getValueType().getTypeName()+"_"+bind(paramStackValue.getStackOffset()); } else if(value instanceof ParamStackPush) { return "_push"+((ParamStackPush) value).getType().getTypeName()+"_"; } diff --git a/src/main/java/dk/camelot64/kickc/model/values/ParamStackValue.java b/src/main/java/dk/camelot64/kickc/model/values/ParamStackValue.java index 6b2ee4a3e..191989505 100644 --- a/src/main/java/dk/camelot64/kickc/model/values/ParamStackValue.java +++ b/src/main/java/dk/camelot64/kickc/model/values/ParamStackValue.java @@ -1,6 +1,7 @@ package dk.camelot64.kickc.model.values; import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.types.SymbolType; /** The value passed into a function for a specific parameter using the stack. */ public class ParamStackValue implements RValue { @@ -8,8 +9,12 @@ public class ParamStackValue implements RValue { /** The constant holding the stack offset of the parameter. */ private ConstantRef stackOffset; - public ParamStackValue(ConstantRef stackOffset) { + /** The type of the value to fetch from the stack. */ + private SymbolType valueType; + + public ParamStackValue(ConstantRef stackOffset, SymbolType valueType) { this.stackOffset = stackOffset; + this.valueType = valueType; } public ConstantRef getStackOffset() { @@ -20,9 +25,17 @@ public class ParamStackValue implements RValue { this.stackOffset = stackOffset; } + public SymbolType getValueType() { + return valueType; + } + + public void setValueType(SymbolType valueType) { + this.valueType = valueType; + } + @Override public String toString(Program program) { - return "paramstack("+stackOffset+")"; + return "paramstack("+valueType.getTypeName()+","+stackOffset+")"; } } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index 0d02dd5f6..6e9d7f232 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -757,8 +757,19 @@ public class Pass4CodeGeneration { generateAsm(asm, asmFragmentInstanceSpecFactory); } } - asm.addInstruction("jsr", AsmAddressingMode.ABS, call.getProcedure().getFullName(), false); + // Clean up the stack + if(Procedure.CallingConvension.STACK_CALL.equals(procedure.getCallingConvension())) { + int parameterBytes = 0; + for(RValue parameter : call.getParameters()) { + 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); + } + } } else if(statement instanceof StatementReturn) { Procedure.InterruptType interruptType = null; ScopeRef scope = block.getScope(); diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNCallingConventionStack.java b/src/main/java/dk/camelot64/kickc/passes/PassNCallingConventionStack.java index d3b57920b..ca51b1711 100644 --- a/src/main/java/dk/camelot64/kickc/passes/PassNCallingConventionStack.java +++ b/src/main/java/dk/camelot64/kickc/passes/PassNCallingConventionStack.java @@ -4,6 +4,7 @@ import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.iterator.ProgramValueIterator; import dk.camelot64.kickc.model.symbols.*; import dk.camelot64.kickc.model.types.SymbolType; +import dk.camelot64.kickc.model.types.SymbolTypeInference; import dk.camelot64.kickc.model.values.*; import java.util.HashMap; @@ -42,8 +43,9 @@ public class PassNCallingConventionStack extends Pass2SsaOptimization { // Convert ParamValues to calling-convention specific param-value ParamValue paramValue = (ParamValue) programValue.get(); VariableRef parameterRef = paramValue.getParameter(); + SymbolType parameterType = SymbolTypeInference.inferType(getScope(), paramValue.getParameter()); if(offsetConstants.containsKey(parameterRef)) { - ParamStackValue paramStackValue = new ParamStackValue(offsetConstants.get(parameterRef)); + ParamStackValue paramStackValue = new ParamStackValue(offsetConstants.get(parameterRef), parameterType); programValue.set(paramStackValue); getLog().append("Calling convention " + Procedure.CallingConvension.STACK_CALL + " replacing " + paramValue.toString(getProgram()) + " with " + paramStackValue.toString(getProgram())); } diff --git a/src/test/ref/procedure-callingconvention-stack-0.asm b/src/test/ref/procedure-callingconvention-stack-0.asm index 211784683..8ebcf3476 100644 --- a/src/test/ref/procedure-callingconvention-stack-0.asm +++ b/src/test/ref/procedure-callingconvention-stack-0.asm @@ -10,6 +10,8 @@ main: { lda #7 pha jsr plus + pla + pla sty SCREEN rts } diff --git a/src/test/ref/procedure-callingconvention-stack-0.cfg b/src/test/ref/procedure-callingconvention-stack-0.cfg index 3e2769e61..592ca8b0f 100644 --- a/src/test/ref/procedure-callingconvention-stack-0.cfg +++ b/src/test/ref/procedure-callingconvention-stack-0.cfg @@ -20,8 +20,8 @@ main::@return: scope:[main] from main __stackcall (byte()) plus((byte) plus::a , (byte) plus::b) plus: scope:[plus] from - [8] (byte) plus::a#0 ← paramstack(plus::OFFSET_STACK_A) - [9] (byte) plus::b#0 ← paramstack(plus::OFFSET_STACK_B) + [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 diff --git a/src/test/ref/procedure-callingconvention-stack-0.log b/src/test/ref/procedure-callingconvention-stack-0.log index cd5c7ade4..759e8ceae 100644 --- a/src/test/ref/procedure-callingconvention-stack-0.log +++ b/src/test/ref/procedure-callingconvention-stack-0.log @@ -89,8 +89,8 @@ 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(plus::OFFSET_STACK_A) -Calling convention STACK_CALL replacing param((byte) plus::b) with paramstack(plus::OFFSET_STACK_B) +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 @@ -115,8 +115,8 @@ main::@return: scope:[main] from main __stackcall (byte()) plus((byte) plus::a , (byte) plus::b) plus: scope:[plus] from - [8] (byte) plus::a#0 ← paramstack(plus::OFFSET_STACK_A) - [9] (byte) plus::b#0 ← paramstack(plus::OFFSET_STACK_B) + [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 @@ -187,6 +187,8 @@ main: { lda #7 pha jsr plus + pla + pla // [6] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuz1 lda.z _0 sta SCREEN @@ -204,11 +206,11 @@ plus: { .label a = 3 .label b = 4 .label return = 5 - // [8] (byte) plus::a#0 ← paramstack(plus::OFFSET_STACK_A) -- vbuz1=_stackbyte_vbuc1 + // [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackbyte_vbuc1 tsx lda STACK_BASE+OFFSET_STACK_A,x sta.z a - // [9] (byte) plus::b#0 ← paramstack(plus::OFFSET_STACK_B) -- vbuz1=_stackbyte_vbuc1 + // [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuz1=_stackbyte_vbuc1 tsx lda STACK_BASE+OFFSET_STACK_B,x sta.z b @@ -227,16 +229,16 @@ plus: { 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(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 [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(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 [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(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(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 [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 , @@ -248,11 +250,11 @@ Uplift Scope [plus] 4: zp ZP_BYTE:4 [ plus::b#0 ] 2: zp ZP_BYTE:3 [ plus::a#0 ] Uplift Scope [main] 0.5: zp ZP_BYTE:2 [ main::$0 ] Uplift Scope [] -Uplifting [plus] best 73 combination reg byte a [ plus::b#0 ] zp ZP_BYTE:3 [ plus::a#0 ] reg byte a [ plus::return#0 ] -Uplifting [main] best 70 combination reg byte y [ main::$0 ] -Uplifting [] best 70 combination +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 70 combination zp 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 @@ -289,6 +291,8 @@ main: { lda #7 pha jsr plus + pla + pla // [6] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuyy sty SCREEN jmp breturn @@ -303,11 +307,11 @@ plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 1 .label a = 2 - // [8] (byte) plus::a#0 ← paramstack(plus::OFFSET_STACK_A) -- vbuz1=_stackbyte_vbuc1 + // [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackbyte_vbuc1 tsx lda STACK_BASE+OFFSET_STACK_A,x sta.z a - // [9] (byte) plus::b#0 ← paramstack(plus::OFFSET_STACK_B) -- vbuaa=_stackbyte_vbuc1 + // [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackbyte_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 @@ -370,7 +374,7 @@ reg byte a [ plus::return#0 ] FINAL ASSEMBLER -Score: 52 +Score: 60 // File Comments // Test a procedure with calling convention stack @@ -397,6 +401,8 @@ main: { lda #7 pha jsr plus + pla + pla // SCREEN[0] = plus('0', 7) // [6] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuyy sty SCREEN @@ -411,11 +417,11 @@ plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 1 .label a = 2 - // [8] (byte) plus::a#0 ← paramstack(plus::OFFSET_STACK_A) -- vbuz1=_stackbyte_vbuc1 + // [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackbyte_vbuc1 tsx lda STACK_BASE+OFFSET_STACK_A,x sta.z a - // [9] (byte) plus::b#0 ← paramstack(plus::OFFSET_STACK_B) -- vbuaa=_stackbyte_vbuc1 + // [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackbyte_vbuc1 tsx lda STACK_BASE+OFFSET_STACK_B,x // return a+b;