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

Added constant for return value offset on stack. Added padding when return value is larger than parameters. Improved calculations of stack frame sizes. #316

This commit is contained in:
Jesper Gravgaard 2019-09-23 17:53:23 +02:00
parent 2b36c5c18b
commit 665b9adbfb
29 changed files with 939 additions and 115 deletions

View File

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

View File

@ -0,0 +1 @@
pha

View File

@ -0,0 +1,2 @@
pha
pha

View File

@ -0,0 +1,4 @@
pla
sta {z1}
pla
sta {z1}+1

View File

@ -163,7 +163,7 @@ public class AsmFragmentInstanceSpecFactory {
signature.append(((ConstantInteger) rValue2).getValue());
} else if(
rValue2 instanceof ConstantInteger &&
((((ConstantInteger) rValue2).getValue()) % 8 == 0) &&
((((ConstantInteger) rValue2).getValue()) % 8 == 0) &&
operator != null &&
(operator.getOperator().equals(">>") || operator.getOperator().equals("<<"))) {
signature.append(((ConstantInteger) rValue2).getValue());
@ -313,7 +313,7 @@ public class AsmFragmentInstanceSpecFactory {
if(bindPointer.contains("deref")) {
// Special handling of nested derefs - add parenthesis!
return "_deref_" + "(" + bindPointer + ")";
} else {
} else {
return "_deref_" + bindPointer;
}
} else if(value instanceof PointerDereferenceIndexed) {
@ -323,7 +323,7 @@ public class AsmFragmentInstanceSpecFactory {
if(bindPointer.contains("deref")) {
// Special handling of nested derefs - add parenthesis!
bindValue.append("(").append(bindPointer).append(")");
} else {
} else {
bindValue.append(bindPointer);
}
bindValue.append("_derefidx_");
@ -358,11 +358,17 @@ public class AsmFragmentInstanceSpecFactory {
return name;
} else if(value instanceof StackIdxValue) {
StackIdxValue stackIdxValue = (StackIdxValue) value;
return "_stackidx"+ stackIdxValue.getValueType().getTypeName()+"_"+bind(stackIdxValue.getStackOffset());
SymbolType type = stackIdxValue.getValueType();
String typeShortName = Operators.getCastUnary(type).getAsmOperator().replace("_", "");
return "_stackidx" + typeShortName + "_" + bind(stackIdxValue.getStackOffset());
} else if(value instanceof StackPushValue) {
return "_stackpush"+((StackPushValue) value).getType().getTypeName()+"_";
SymbolType type = ((StackPushValue) value).getType();
String typeShortName = Operators.getCastUnary(type).getAsmOperator().replace("_", "");
return "_stackpush" + typeShortName + "_";
} else if(value instanceof StackPullValue) {
return "_stackpull"+((StackPullValue) value).getType().getTypeName()+"_";
SymbolType type = ((StackPullValue) value).getType();
String typeShortName = Operators.getCastUnary(type).getAsmOperator().replace("_", "");
return "_stackpull" + typeShortName + "_";
}
throw new RuntimeException("Binding of value type not supported " + value.toString(program));
}

View File

@ -0,0 +1,131 @@
package dk.camelot64.kickc.model;
import dk.camelot64.kickc.model.symbols.*;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.values.ConstantInteger;
import dk.camelot64.kickc.model.values.ConstantRef;
/**
* Utility methods for {@link dk.camelot64.kickc.model.symbols.Procedure.CallingConvension#STACK_CALL}
*/
public class CallingConventionStack {
/**
* Get the constant variable containing the (byte) index of the return value on the stack
*
* @param procedure The procedure
* @return The return value stack offset constant
*/
public static ConstantRef getReturnOffsetConstant(Procedure procedure) {
String returnOffsetConstantName = "OFFSET_STACK_RETURN";
ConstantVar returnOffsetConstant = procedure.getConstant(returnOffsetConstantName);
if(returnOffsetConstant == null) {
// Constant not found - create it
long returnByteOffset = getReturnByteOffset(procedure);
returnOffsetConstant = new ConstantVar(returnOffsetConstantName, procedure, SymbolType.BYTE, new ConstantInteger(returnByteOffset & 0xff, SymbolType.BYTE), Scope.SEGMENT_DATA_DEFAULT);
procedure.add(returnOffsetConstant);
}
return returnOffsetConstant.getRef();
}
/**
* Get the constant variable containing the (byte) index of a parameter on the stack
*
* @param procedure The procedure
* @param parameter The parameter
* @return The constant variable
*/
public static ConstantRef getParameterOffsetConstant(Procedure procedure, Variable parameter) {
String paramOffsetConstantName = getParameterOffsetConstantName(parameter.getName());
ConstantVar paramOffsetConstant = procedure.getConstant(paramOffsetConstantName);
if(paramOffsetConstant == null) {
// Constant not found - create it
long paramByteOffset = getParameterByteOffset(procedure, parameter);
paramOffsetConstant = new ConstantVar(paramOffsetConstantName, procedure, SymbolType.BYTE, new ConstantInteger(paramByteOffset & 0xff, SymbolType.BYTE), Scope.SEGMENT_DATA_DEFAULT);
procedure.add(paramOffsetConstant);
}
return paramOffsetConstant.getRef();
}
/**
* Get the name of the constant variable containing the (byte) offset of a specific parameter on the stack
*
* @param parameterName The name of the struct member
* @return The name of the constant
*/
private static String getParameterOffsetConstantName(String parameterName) {
return "OFFSET_STACK_" + parameterName.toUpperCase();
}
/**
* Get the number of bytes that needed on the stack to pass parameters/return value to/from a procedure
*
* @param procedure The procedure to find the stack frame size for
* @return The byte size of the stack frame
*/
public static long getStackFrameByteSize(Procedure procedure) {
long byteSize = getParametersByteSize(procedure);
if(procedure.getReturnType() != null) {
int returnBytes = procedure.getReturnType().getSizeBytes();
if(returnBytes > byteSize) byteSize = returnBytes;
}
return byteSize;
}
/**
* Get the number of bytes needed on the stack to store the parameters from a procedure
* @param procedure The procedure
* @return The byte size of parameters
*/
public static long getParametersByteSize(Procedure procedure) {
long byteSize = 0;
for(Variable procedureParameter : procedure.getParameters()) {
byteSize += procedureParameter.getType().getSizeBytes();
}
return byteSize;
}
/**
* Get the number of bytes that a parameter is offset on the stack
*
* @param parameter The parameter to find offset for
* @return The byte offset of the start of the member data
*/
public static long getParameterByteOffset(Procedure procedure, Variable parameter) {
long byteOffset = 0;
for(Variable procedureParameter : procedure.getParameters()) {
if(parameter.equals(procedureParameter)) {
break;
} else {
// TODO: Consider how passing a struct should handle inline arrays byteOffset += SymbolTypeStruct.getMemberSizeBytes(parameter.getType(), programScope);
byteOffset += procedureParameter.getType().getSizeBytes();
}
}
return byteOffset;
}
/**
* Get the number of bytes that the return value is offset on the stack
*
* @param procedure The procedure
* @return The byte offset of the return value
*/
private static long getReturnByteOffset(Procedure procedure) {
return getStackFrameByteSize(procedure) - procedure.getReturnType().getSizeBytes();
}
/**
* Get he global STACK_BASE constant. Create it if it does not exist.
*
* @param programScope The program scope
* @return The reference to the global constant
*/
public static ConstantRef getStackBaseConstant(ProgramScope programScope) {
long STACK_BASE_ADDRESS = 0x103L;
ConstantVar stackBase = new ConstantVar("STACK_BASE", programScope, SymbolType.WORD, new ConstantInteger(STACK_BASE_ADDRESS, SymbolType.WORD), Scope.SEGMENT_DATA_DEFAULT);
programScope.add(stackBase);
return stackBase.getRef();
}
}

View File

@ -765,6 +765,17 @@ public class Pass4CodeGeneration {
ensureEncoding(asm, asmFragmentInstanceSpecFactory);
generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec());
}
// Push additional bytes if needed
long stackFrameByteSize = CallingConventionStack.getStackFrameByteSize(procedure);
long parametersByteSize = CallingConventionStack.getParametersByteSize(procedure);
if(stackFrameByteSize > parametersByteSize) {
// Add padding to the stack to make room for the return value
String pushSignature = "_stackpushbyte_" + (stackFrameByteSize - parametersByteSize);
AsmFragmentInstanceSpec pushFragmentInstanceSpec = new AsmFragmentInstanceSpec(program, pushSignature, new LinkedHashMap<>(), block.getScope());
asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo));
generateAsm(asm, pushFragmentInstanceSpec);
}
asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo));
}
} else if(statement instanceof StatementCallExecute) {
@ -778,21 +789,16 @@ public class Pass4CodeGeneration {
StatementCallFinalize call = (StatementCallFinalize) statement;
Procedure procedure = getScope().getProcedure(call.getProcedure());
if(Procedure.CallingConvension.STACK_CALL.equals(procedure.getCallingConvension())) {
// Find parameter/return stack size
int parameterBytes = 0;
for(Variable parameter : procedure.getParameters()) {
parameterBytes += parameter.getType().getSizeBytes();
long stackFrameByteSize = CallingConventionStack.getStackFrameByteSize(procedure);
long returnByteSize = procedure.getReturnType()==null?0:procedure.getReturnType().getSizeBytes();
if(stackFrameByteSize > returnByteSize) {
// Clean up the stack
String pullSignature = "_stackpullbyte_" + (stackFrameByteSize-returnByteSize);
AsmFragmentInstanceSpec pullFragmentInstanceSpec = new AsmFragmentInstanceSpec(program, pullSignature, new LinkedHashMap<>(), block.getScope());
asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo));
generateAsm(asm, pullFragmentInstanceSpec);
}
int stackSizeBytes = parameterBytes;
if(call.getlValue() != null) {
SymbolType returnType = procedure.getReturnType();
stackSizeBytes -= returnType.getSizeBytes();
}
// Clean up the stack
String pullSignature = "_stackpullbyte_" + Integer.toString(stackSizeBytes);
AsmFragmentInstanceSpec pullFragmentInstanceSpec = new AsmFragmentInstanceSpec(program, pullSignature, new LinkedHashMap<>(), block.getScope());
asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo));
generateAsm(asm, pullFragmentInstanceSpec);
// Pull result from the stack
if(call.getlValue() != null) {
@ -811,28 +817,21 @@ public class Pass4CodeGeneration {
procedure = getScope().getProcedure(scope.getFullName());
}
if(procedure!=null && Procedure.CallingConvension.STACK_CALL.equals(procedure.getCallingConvension())) {
if(procedure != null && Procedure.CallingConvension.STACK_CALL.equals(procedure.getCallingConvension())) {
StatementReturn returnStatement = (StatementReturn) statement;
if(returnStatement.getValue()!=null) {
if(returnStatement.getValue() != null) {
// Store return value on stack
SymbolType returnType = procedure.getReturnType();
// Find parameter/return stack size
int parameterBytes = 0;
for(Variable parameter : procedure.getParameters()) {
parameterBytes += parameter.getType().getSizeBytes();
}
int returnOffset = parameterBytes - returnType.getSizeBytes();
// TODO: Put the return stack offset into a named constant (look at PassNCallingConventionStack)
ConstantValue returnValueStackOffset = new ConstantInteger((long)returnOffset, SymbolType.BYTE);
AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(new StackIdxValue(returnValueStackOffset, returnType), returnStatement.getValue(), program, block.getScope());
ConstantRef returnOffsetConstant = CallingConventionStack.getReturnOffsetConstant(procedure);
AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(new StackIdxValue(returnOffsetConstant, returnType), returnStatement.getValue(), program, block.getScope());
asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo));
ensureEncoding(asm, asmFragmentInstanceSpecFactory);
generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec());
}
}
if(procedure==null || procedure.getInterruptType() == null) {
if(procedure == null || procedure.getInterruptType() == null) {
asm.addInstruction("rts", AsmAddressingMode.NON, null, false);
} else {
generateInterruptExit(asm, statement, procedure.getInterruptType());

View File

@ -1,5 +1,6 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.CallingConventionStack;
import dk.camelot64.kickc.model.ControlFlowBlock;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.iterator.ProgramValueIterator;
@ -26,15 +27,27 @@ public class PassNCallingConventionStack extends Pass2SsaOptimization {
Map<VariableRef, ConstantRef> offsetConstants = new HashMap<>();
// Introduce STACK_OFFSET constants
boolean createStackBase = false;
for(Procedure procedure : getScope().getAllProcedures(true)) {
if(Procedure.CallingConvension.STACK_CALL.equals(procedure.getCallingConvension())) {
// Introduce the parameter offsets
for(Variable parameter : procedure.getParameters()) {
ConstantRef parameterOffsetConstant = getParameterOffsetConstant(procedure, parameter, getScope());
ConstantRef parameterOffsetConstant = CallingConventionStack.getParameterOffsetConstant(procedure, parameter);
offsetConstants.put(parameter.getRef(), parameterOffsetConstant);
createStackBase = true;
}
// Introduce the return value offset
if(procedure.getReturnType() != null) {
CallingConventionStack.getReturnOffsetConstant(procedure);
createStackBase = true;
}
}
}
// Add global STACK_BASE constant
if(createStackBase)
CallingConventionStack.getStackBaseConstant(getScope());
// Transform STACK_CALL calls to call-prepare, call-execute, call-finalize
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
ListIterator<Statement> stmtIt = block.getStatements().listIterator();
@ -49,17 +62,13 @@ public class PassNCallingConventionStack extends Pass2SsaOptimization {
stmtIt.add(new StatementCallPrepare(procedureRef, call.getParameters(), call.getSource(), call.getComments()));
stmtIt.add(new StatementCallExecute(procedureRef, call.getSource(), call.getComments()));
stmtIt.add(new StatementCallFinalize(call.getlValue(), procedureRef, call.getSource(), call.getComments()));
getLog().append("Calling convention " + Procedure.CallingConvension.STACK_CALL + " adding prepare/execute/finalize for "+call.toString(getProgram(), false) );
getLog().append("Calling convention " + Procedure.CallingConvension.STACK_CALL + " adding prepare/execute/finalize for " + call.toString(getProgram(), false));
}
}
}
}
if(offsetConstants.size() > 0) {
// Add global STACK_BASE constant
long STACK_BASE = 0x103L;
getScope().add(new ConstantVar("STACK_BASE", getScope(), SymbolType.WORD, new ConstantInteger(STACK_BASE, SymbolType.WORD), Scope.SEGMENT_DATA_DEFAULT));
// Convert ParamValue to StackIdxValue
ProgramValueIterator.execute(getGraph(), (programValue, currentStmt, stmtIt, currentBlock) -> {
if(programValue.get() instanceof ParamValue) {
@ -78,53 +87,4 @@ public class PassNCallingConventionStack extends Pass2SsaOptimization {
return false;
}
/**
* Get the constant variable containing the (byte) index of a parameter on the stack
*
* @param procedure The procedure
* @param procedure The parameter
* @param programScope The program scope (used for finding/adding the constant).
* @return The constant variable
*/
public static ConstantRef getParameterOffsetConstant(Procedure procedure, Variable parameter, ProgramScope programScope) {
String paramOffsetConstantName = getParameterOffsetConstantName(parameter.getName());
ConstantVar paramOffsetConstant = procedure.getConstant(paramOffsetConstantName);
if(paramOffsetConstant == null) {
// Constant not found - create it
long paramByteOffset = getParameterByteOffset(procedure, parameter, programScope);
paramOffsetConstant = new ConstantVar(paramOffsetConstantName, procedure, SymbolType.BYTE, new ConstantInteger(paramByteOffset & 0xff, SymbolType.BYTE), Scope.SEGMENT_DATA_DEFAULT);
procedure.add(paramOffsetConstant);
}
return paramOffsetConstant.getRef();
}
/**
* Get the name of the constant variable containing the (byte) offset of a specific parameter on the stack
*
* @param parameterName The name of the struct member
* @return The name of the constant
*/
private static String getParameterOffsetConstantName(String parameterName) {
return "OFFSET_STACK_" + parameterName.toUpperCase();
}
/**
* Get the number of bytes that a parameter is offset on the stack
*
* @param parameter The parameter to find offset for
* @return The byte offset of the start of the member data
*/
public static long getParameterByteOffset(Procedure procedure, Variable parameter, ProgramScope programScope) {
long byteOffset = 0;
for(Variable procedureParameter : procedure.getParameters()) {
if(parameter.equals(procedureParameter)) {
break;
} else {
// TODO: Consider hos passing a struct should handle inline arrays byteOffset += SymbolTypeStruct.getMemberSizeBytes(parameter.getType(), programScope);
byteOffset += parameter.getType().getSizeBytes();
}
}
return byteOffset;
}
}

View File

@ -35,6 +35,11 @@ public class TestPrograms {
public TestPrograms() {
}
@Test
public void testProcedureCallingConventionStack5() throws IOException, URISyntaxException {
compileAndCompare("procedure-callingconvention-stack-5"); //, log().verboseCreateSsa().verboseParse().verboseStatementSequence());
}
@Test
public void testProcedureCallingConventionStack4() throws IOException, URISyntaxException {
compileAndCompare("procedure-callingconvention-stack-4"); //, log().verboseCreateSsa().verboseParse().verboseStatementSequence());

View File

@ -0,0 +1,15 @@
// Test a procedure with calling convention stack
// Return value larger than parameter
const int* SCREEN = 0x0400;
void main(void) {
SCREEN[0] = next();
SCREEN[1] = next();
}
int current = 48;
int __stackcall next() {
return current++;
}

View File

@ -19,6 +19,7 @@ main: {
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.const OFFSET_STACK_RETURN = 1
.label a = 2
tsx
lda STACK_BASE+OFFSET_STACK_A,x
@ -28,6 +29,6 @@ plus: {
clc
adc.z a
tsx
sta STACK_BASE+1,x
sta STACK_BASE+OFFSET_STACK_RETURN,x
rts
}

View File

@ -214,6 +214,7 @@ main: {
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.const OFFSET_STACK_RETURN = 1
.label a = 3
.label b = 4
.label return = 5
@ -237,7 +238,7 @@ plus: {
// [13] return (byte) plus::return#0 -- _stackidxbyte_vbuc1=vbuz1
lda.z return
tsx
sta STACK_BASE+1,x
sta STACK_BASE+OFFSET_STACK_RETURN,x
rts
}
// File Data
@ -330,6 +331,7 @@ main: {
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.const OFFSET_STACK_RETURN = 1
.label a = 2
// [10] (byte) plus::a#0 ← stackidx(byte,(const byte) plus::OFFSET_STACK_A) -- vbuz1=_stackidxbyte_vbuc1
tsx
@ -347,7 +349,7 @@ plus: {
// [13] return (byte) plus::return#0
// [13] return (byte) plus::return#0 -- _stackidxbyte_vbuc1=vbuaa
tsx
sta STACK_BASE+1,x
sta STACK_BASE+OFFSET_STACK_RETURN,x
rts
}
// File Data
@ -387,6 +389,7 @@ __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
(const byte) plus::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 1
(byte) plus::a
(byte) plus::a#0 a zp ZP_BYTE:2 2.0
(byte) plus::b
@ -450,6 +453,7 @@ main: {
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.const OFFSET_STACK_RETURN = 1
.label a = 2
// [10] (byte) plus::a#0 ← stackidx(byte,(const byte) plus::OFFSET_STACK_A) -- vbuz1=_stackidxbyte_vbuc1
tsx
@ -467,7 +471,7 @@ plus: {
// [13] return (byte) plus::return#0
// [13] return (byte) plus::return#0 -- _stackidxbyte_vbuc1=vbuaa
tsx
sta STACK_BASE+1,x
sta STACK_BASE+OFFSET_STACK_RETURN,x
rts
}
// File Data

View File

@ -11,6 +11,7 @@ __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
(const byte) plus::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 1
(byte) plus::a
(byte) plus::a#0 a zp ZP_BYTE:2 2.0
(byte) plus::b

View File

@ -19,6 +19,7 @@ main: {
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.const OFFSET_STACK_RETURN = 1
.label a = 2
tsx
lda STACK_BASE+OFFSET_STACK_A,x
@ -28,6 +29,6 @@ plus: {
clc
adc.z a
tsx
sta STACK_BASE+1,x
sta STACK_BASE+OFFSET_STACK_RETURN,x
rts
}

View File

@ -214,6 +214,7 @@ main: {
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.const OFFSET_STACK_RETURN = 1
.label a = 3
.label b = 4
.label return = 5
@ -237,7 +238,7 @@ plus: {
// [13] return (byte) plus::return#0 -- _stackidxbyte_vbuc1=vbuz1
lda.z return
tsx
sta STACK_BASE+1,x
sta STACK_BASE+OFFSET_STACK_RETURN,x
rts
}
// File Data
@ -330,6 +331,7 @@ main: {
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.const OFFSET_STACK_RETURN = 1
.label a = 2
// [10] (byte) plus::a#0 ← stackidx(byte,(const byte) plus::OFFSET_STACK_A) -- vbuz1=_stackidxbyte_vbuc1
tsx
@ -347,7 +349,7 @@ plus: {
// [13] return (byte) plus::return#0
// [13] return (byte) plus::return#0 -- _stackidxbyte_vbuc1=vbuaa
tsx
sta STACK_BASE+1,x
sta STACK_BASE+OFFSET_STACK_RETURN,x
rts
}
// File Data
@ -387,6 +389,7 @@ __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
(const byte) plus::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 1
(byte) plus::a
(byte) plus::a#0 a zp ZP_BYTE:2 2.0
(byte) plus::b
@ -450,6 +453,7 @@ main: {
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.const OFFSET_STACK_RETURN = 1
.label a = 2
// [10] (byte) plus::a#0 ← stackidx(byte,(const byte) plus::OFFSET_STACK_A) -- vbuz1=_stackidxbyte_vbuc1
tsx
@ -467,7 +471,7 @@ plus: {
// [13] return (byte) plus::return#0
// [13] return (byte) plus::return#0 -- _stackidxbyte_vbuc1=vbuaa
tsx
sta STACK_BASE+1,x
sta STACK_BASE+OFFSET_STACK_RETURN,x
rts
}
// File Data

View File

@ -11,6 +11,7 @@ __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
(const byte) plus::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 1
(byte) plus::a
(byte) plus::a#0 a zp ZP_BYTE:2 2.0
(byte) plus::b

View File

@ -31,6 +31,7 @@ main: {
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 2
.const OFFSET_STACK_RETURN = 2
.label a = 2
.label b = 4
.label return = 2
@ -53,8 +54,8 @@ plus: {
sta.z return+1
tsx
lda.z return
sta STACK_BASE+2,x
sta STACK_BASE+OFFSET_STACK_RETURN,x
lda.z return+1
sta STACK_BASE+2+1,x
sta STACK_BASE+OFFSET_STACK_RETURN+1,x
rts
}

View File

@ -240,6 +240,7 @@ main: {
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 2
.const OFFSET_STACK_RETURN = 2
.label a = 4
.label b = 6
.label return = 8
@ -270,9 +271,9 @@ plus: {
// [13] return (word) plus::return#0 -- _stackidxword_vbuc1=vwuz1
tsx
lda.z return
sta STACK_BASE+2,x
sta STACK_BASE+OFFSET_STACK_RETURN,x
lda.z return+1
sta STACK_BASE+2+1,x
sta STACK_BASE+OFFSET_STACK_RETURN+1,x
rts
}
// File Data
@ -371,6 +372,7 @@ main: {
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 2
.const OFFSET_STACK_RETURN = 2
.label a = 2
.label b = 4
.label return = 2
@ -401,9 +403,9 @@ plus: {
// [13] return (word) plus::return#0 -- _stackidxword_vbuc1=vwuz1
tsx
lda.z return
sta STACK_BASE+2,x
sta STACK_BASE+OFFSET_STACK_RETURN,x
lda.z return+1
sta STACK_BASE+2+1,x
sta STACK_BASE+OFFSET_STACK_RETURN+1,x
rts
}
// File Data
@ -443,6 +445,7 @@ __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
(const byte) plus::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 2
(word) plus::a
(word) plus::a#0 a zp ZP_WORD:2 2.0
(word) plus::b
@ -516,6 +519,7 @@ main: {
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 2
.const OFFSET_STACK_RETURN = 2
.label a = 2
.label b = 4
.label return = 2
@ -546,9 +550,9 @@ plus: {
// [13] return (word) plus::return#0 -- _stackidxword_vbuc1=vwuz1
tsx
lda.z return
sta STACK_BASE+2,x
sta STACK_BASE+OFFSET_STACK_RETURN,x
lda.z return+1
sta STACK_BASE+2+1,x
sta STACK_BASE+OFFSET_STACK_RETURN+1,x
rts
}
// File Data

View File

@ -11,6 +11,7 @@ __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
(const byte) plus::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 2
(word) plus::a
(word) plus::a#0 a zp ZP_WORD:2 2.0
(word) plus::b

View File

@ -34,6 +34,7 @@ main: {
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 2
.const OFFSET_STACK_RETURN = 2
.label a = 2
.label b = 4
.label return = 2
@ -56,8 +57,8 @@ plus: {
sta.z return+1
tsx
lda.z return
sta STACK_BASE+2,x
sta STACK_BASE+OFFSET_STACK_RETURN,x
lda.z return+1
sta STACK_BASE+2+1,x
sta STACK_BASE+OFFSET_STACK_RETURN+1,x
rts
}

View File

@ -240,6 +240,7 @@ main: {
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 2
.const OFFSET_STACK_RETURN = 2
.label a = 4
.label b = 6
.label return = 8
@ -270,9 +271,9 @@ plus: {
// [13] return (word) plus::return#0 -- _stackidxword_vbuc1=vwuz1
tsx
lda.z return
sta STACK_BASE+2,x
sta STACK_BASE+OFFSET_STACK_RETURN,x
lda.z return+1
sta STACK_BASE+2+1,x
sta STACK_BASE+OFFSET_STACK_RETURN+1,x
rts
}
// File Data
@ -374,6 +375,7 @@ main: {
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 2
.const OFFSET_STACK_RETURN = 2
.label a = 2
.label b = 4
.label return = 2
@ -404,9 +406,9 @@ plus: {
// [13] return (word) plus::return#0 -- _stackidxword_vbuc1=vwuz1
tsx
lda.z return
sta STACK_BASE+2,x
sta STACK_BASE+OFFSET_STACK_RETURN,x
lda.z return+1
sta STACK_BASE+2+1,x
sta STACK_BASE+OFFSET_STACK_RETURN+1,x
rts
}
// File Data
@ -446,6 +448,7 @@ __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
(const byte) plus::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 2
(word) plus::a
(word) plus::a#0 a zp ZP_WORD:2 2.0
(word) plus::b
@ -522,6 +525,7 @@ main: {
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 2
.const OFFSET_STACK_RETURN = 2
.label a = 2
.label b = 4
.label return = 2
@ -552,9 +556,9 @@ plus: {
// [13] return (word) plus::return#0 -- _stackidxword_vbuc1=vwuz1
tsx
lda.z return
sta STACK_BASE+2,x
sta STACK_BASE+OFFSET_STACK_RETURN,x
lda.z return+1
sta STACK_BASE+2+1,x
sta STACK_BASE+OFFSET_STACK_RETURN+1,x
rts
}
// File Data

View File

@ -11,6 +11,7 @@ __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
(const byte) plus::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 2
(word) plus::a
(word) plus::a#0 a zp ZP_WORD:2 2.0
(word) plus::b

View File

@ -31,6 +31,7 @@ main: {
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.const OFFSET_STACK_RETURN = 1
.label a = 2
tsx
lda STACK_BASE+OFFSET_STACK_A,x
@ -40,6 +41,6 @@ plus: {
clc
adc.z a
tsx
sta STACK_BASE+1,x
sta STACK_BASE+OFFSET_STACK_RETURN,x
rts
}

View File

@ -349,6 +349,7 @@ main: {
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.const OFFSET_STACK_RETURN = 1
.label a = 6
.label b = 7
.label return = 8
@ -372,7 +373,7 @@ plus: {
// [18] return (byte) plus::return#0 -- _stackidxbyte_vbuc1=vbuz1
lda.z return
tsx
sta STACK_BASE+1,x
sta STACK_BASE+OFFSET_STACK_RETURN,x
rts
}
// File Data
@ -498,6 +499,7 @@ main: {
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.const OFFSET_STACK_RETURN = 1
.label a = 2
// [15] (byte) plus::a#0 ← stackidx(byte,(const byte) plus::OFFSET_STACK_A) -- vbuz1=_stackidxbyte_vbuc1
tsx
@ -515,7 +517,7 @@ plus: {
// [18] return (byte) plus::return#0
// [18] return (byte) plus::return#0 -- _stackidxbyte_vbuc1=vbuaa
tsx
sta STACK_BASE+1,x
sta STACK_BASE+OFFSET_STACK_RETURN,x
rts
}
// File Data
@ -570,6 +572,7 @@ __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
(const byte) plus::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 1
(byte) plus::a
(byte) plus::a#0 a zp ZP_BYTE:2 2.0
(byte) plus::b
@ -660,6 +663,7 @@ main: {
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.const OFFSET_STACK_RETURN = 1
.label a = 2
// [15] (byte) plus::a#0 ← stackidx(byte,(const byte) plus::OFFSET_STACK_A) -- vbuz1=_stackidxbyte_vbuc1
tsx
@ -677,7 +681,7 @@ plus: {
// [18] return (byte) plus::return#0
// [18] return (byte) plus::return#0 -- _stackidxbyte_vbuc1=vbuaa
tsx
sta STACK_BASE+1,x
sta STACK_BASE+OFFSET_STACK_RETURN,x
rts
}
// File Data

View File

@ -20,6 +20,7 @@ __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
(const byte) plus::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 1
(byte) plus::a
(byte) plus::a#0 a zp ZP_BYTE:2 2.0
(byte) plus::b

View File

@ -0,0 +1,46 @@
// Test a procedure with calling convention stack
// Return value larger than parameter
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
.const SIZEOF_SIGNED_WORD = 2
.label SCREEN = $400
.const STACK_BASE = $103
.label current = 2
main: {
.label _0 = 2
.label _1 = 4
pha
pha
jsr next
pla
sta.z _0
pla
sta.z _0+1
lda.z _0
sta SCREEN
lda.z _0+1
sta SCREEN+1
pha
pha
jsr next
pla
sta.z _1
pla
sta.z _1+1
lda.z _1
sta SCREEN+1*SIZEOF_SIGNED_WORD
lda.z _1+1
sta SCREEN+1*SIZEOF_SIGNED_WORD+1
rts
}
next: {
.const OFFSET_STACK_RETURN = 0
.label return = 2
tsx
lda.z return
sta STACK_BASE+OFFSET_STACK_RETURN,x
lda.z return+1
sta STACK_BASE+OFFSET_STACK_RETURN+1,x
rts
}

View File

@ -0,0 +1,34 @@
@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] callprepare next
[6] callexecute next
[7] (signed word~) main::$0 ← callfinalize next
[8] *((const signed word*) SCREEN#0) ← (signed word~) main::$0
[9] callprepare next
[10] callexecute next
[11] (signed word~) main::$1 ← callfinalize next
[12] *((const signed word*) SCREEN#0+(byte) 1*(const byte) SIZEOF_SIGNED_WORD) ← (signed word~) main::$1
to:main::@return
main::@return: scope:[main] from main
[13] return
to:@return
__stackcall (signed word()) next()
next: scope:[next] from
[14] (signed word) current#5 ← phi( )
[15] (signed word) next::return#0 ← (signed word) current#5
to:next::@return
next::@return: scope:[next] from next
[16] return (signed word) next::return#0
to:@return

View File

@ -0,0 +1,565 @@
Fixing pointer array-indexing *((signed word*) SCREEN + (number) 0)
Fixing pointer array-indexing *((signed word*) SCREEN + (number) 1)
Culled Empty Block (label) next::@1
CONTROL FLOW GRAPH SSA
@begin: scope:[] from
(signed word*) SCREEN#0 ← ((signed word*)) (number) $400
to:@1
(void()) main()
main: scope:[main] from @2
(signed word) current#7 ← phi( @2/(signed word) current#8 )
(signed word~) main::$0 ← call next
(number~) main::$2 ← (number) 0 * (const byte) SIZEOF_SIGNED_WORD
*((signed word*) SCREEN#0 + (number~) main::$2) ← (signed word~) main::$0
(signed word~) main::$1 ← call next
(number~) main::$3 ← (number) 1 * (const byte) SIZEOF_SIGNED_WORD
*((signed word*) SCREEN#0 + (number~) main::$3) ← (signed word~) main::$1
to:main::@return
main::@return: scope:[main] from main
(signed word) current#4 ← phi( main/(signed word) current#7 )
(signed word) current#0 ← (signed word) current#4
return
to:@return
@1: scope:[] from @begin
(signed word) current#1 ← (number) $30
to:@2
__stackcall (signed word()) next()
next: scope:[next] from
(signed word) current#5 ← phi( )
(signed word) next::return#0 ← (signed word) current#5
(signed word) current#2 ← ++ (signed word) current#5
to:next::@return
next::@return: scope:[next] from next
(signed word) next::return#1 ← phi( next/(signed word) next::return#0 )
return (signed word) next::return#1
to:@return
@2: scope:[] from @1
(signed word) current#8 ← phi( @1/(signed word) current#1 )
call main
to:@3
@3: scope:[] from @2
(signed word) current#6 ← phi( @2/(signed word) current#0 )
(signed word) current#3 ← (signed word) current#6
to:@end
@end: scope:[] from @3
SYMBOL TABLE SSA
(label) @1
(label) @2
(label) @3
(label) @begin
(label) @end
(signed word*) SCREEN
(signed word*) SCREEN#0
(const byte) SIZEOF_SIGNED_WORD = (byte) 2
(signed word) current
(signed word) current#0
(signed word) current#1
(signed word) current#2
(signed word) current#3
(signed word) current#4
(signed word) current#5
(signed word) current#6
(signed word) current#7
(signed word) current#8
(void()) main()
(signed word~) main::$0
(signed word~) main::$1
(number~) main::$2
(number~) main::$3
(label) main::@return
__stackcall (signed word()) next()
(label) next::@return
(signed word) next::return
(signed word) next::return#0
(signed word) next::return#1
Adding number conversion cast (unumber) 0 in (number~) main::$2 ← (number) 0 * (const byte) SIZEOF_SIGNED_WORD
Adding number conversion cast (unumber) main::$2 in (number~) main::$2 ← (unumber)(number) 0 * (const byte) SIZEOF_SIGNED_WORD
Adding number conversion cast (unumber) 1 in (number~) main::$3 ← (number) 1 * (const byte) SIZEOF_SIGNED_WORD
Adding number conversion cast (unumber) main::$3 in (number~) main::$3 ← (unumber)(number) 1 * (const byte) SIZEOF_SIGNED_WORD
Adding number conversion cast (snumber) $30 in (signed word) current#1 ← (number) $30
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast (signed word*) SCREEN#0 ← (signed word*)(number) $400
Inlining cast (signed word) current#1 ← (snumber)(number) $30
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (signed word*) 1024
Simplifying constant integer cast 0
Simplifying constant integer cast 1
Simplifying constant integer cast $30
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (byte) 0
Finalized unsigned number type (byte) 1
Finalized signed number type (signed byte) $30
Successful SSA optimization PassNFinalizeNumberTypeConversions
Inferred type updated to byte in (unumber~) main::$2 ← (byte) 0 * (const byte) SIZEOF_SIGNED_WORD
Inferred type updated to byte in (unumber~) main::$3 ← (byte) 1 * (const byte) SIZEOF_SIGNED_WORD
Alias (signed word) current#0 = (signed word) current#4 (signed word) current#7
Alias (signed word) next::return#0 = (signed word) next::return#1
Alias (signed word) current#1 = (signed word) current#8
Alias (signed word) current#3 = (signed word) current#6
Successful SSA optimization Pass2AliasElimination
Identical Phi Values (signed word) current#0 (signed word) current#1
Identical Phi Values (signed word) current#3 (signed word) current#0
Successful SSA optimization Pass2IdenticalPhiElimination
Constant right-side identified [3] (byte~) main::$2 ← (byte) 0 * (const byte) SIZEOF_SIGNED_WORD
Constant right-side identified [6] (byte~) main::$3 ← (byte) 1 * (const byte) SIZEOF_SIGNED_WORD
Successful SSA optimization Pass2ConstantRValueConsolidation
Constant (const signed word*) SCREEN#0 = (signed word*) 1024
Constant (const byte) main::$2 = 0*SIZEOF_SIGNED_WORD
Constant (const byte) main::$3 = 1*SIZEOF_SIGNED_WORD
Constant (const signed word) current#1 = $30
Successful SSA optimization Pass2ConstantIdentification
Simplifying constant evaluating to zero (byte) 0*(const byte) SIZEOF_SIGNED_WORD in
Successful SSA optimization PassNSimplifyConstantZero
Simplifying expression containing zero SCREEN#0 in [4] *((const signed word*) SCREEN#0 + (const byte) main::$2) ← (signed word~) main::$0
Successful SSA optimization PassNSimplifyExpressionWithZero
Eliminating unused variable (signed word) current#2 and assignment [7] (signed word) current#2 ← ++ (signed word) current#5
Eliminating unused constant (const byte) main::$2
Eliminating unused constant (const signed word) current#1
Successful SSA optimization PassNEliminateUnusedVars
Constant inlined main::$3 = (byte) 1*(const byte) SIZEOF_SIGNED_WORD
Successful SSA optimization Pass2ConstantInlining
Consolidated array index constant in *(SCREEN#0+1*SIZEOF_SIGNED_WORD)
Successful SSA optimization Pass2ConstantAdditionElimination
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @1
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:3
Calls in [main] to next:7 next:9
Created 1 initial phi equivalence classes
Coalesced down to 1 phi equivalence classes
Culled Empty Block (label) @1
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 adding prepare/execute/finalize for [5] (signed word~) main::$0 ← call next
Calling convention STACK_CALL adding prepare/execute/finalize for [7] (signed word~) main::$1 ← call next
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] callprepare next
[6] callexecute next
[7] (signed word~) main::$0 ← callfinalize next
[8] *((const signed word*) SCREEN#0) ← (signed word~) main::$0
[9] callprepare next
[10] callexecute next
[11] (signed word~) main::$1 ← callfinalize next
[12] *((const signed word*) SCREEN#0+(byte) 1*(const byte) SIZEOF_SIGNED_WORD) ← (signed word~) main::$1
to:main::@return
main::@return: scope:[main] from main
[13] return
to:@return
__stackcall (signed word()) next()
next: scope:[next] from
[14] (signed word) current#5 ← phi( )
[15] (signed word) next::return#0 ← (signed word) current#5
to:next::@return
next::@return: scope:[next] from next
[16] return (signed word) next::return#0
to:@return
VARIABLE REGISTER WEIGHTS
(signed word*) SCREEN
(signed word) current
(signed word) current#5 2.0
(void()) main()
(signed word~) main::$0 2.0
(signed word~) main::$1 2.0
__stackcall (signed word()) next()
(signed word) next::return
(signed word) next::return#0 2.0
Initial phi equivalence classes
[ current#5 ]
Added variable main::$0 to zero page equivalence class [ main::$0 ]
Added variable main::$1 to zero page equivalence class [ main::$1 ]
Added variable next::return#0 to zero page equivalence class [ next::return#0 ]
Complete equivalence classes
[ current#5 ]
[ main::$0 ]
[ main::$1 ]
[ next::return#0 ]
Allocated zp ZP_WORD:2 [ current#5 ]
Allocated zp ZP_WORD:4 [ main::$0 ]
Allocated zp ZP_WORD:6 [ main::$1 ]
Allocated zp ZP_WORD:8 [ next::return#0 ]
INITIAL ASM
Target platform is c64basic / MOS6502X
// File Comments
// Test a procedure with calling convention stack
// Return value larger than parameter
// Upstart
.pc = $801 "Basic"
:BasicUpstart(bbegin)
.pc = $80d "Program"
// Global Constants & labels
.const SIZEOF_SIGNED_WORD = 2
.label SCREEN = $400
.const STACK_BASE = $103
.label current = 2
// @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 = 4
.label _1 = 6
// [5] callprepare next
// [5] callprepare next -- _stackpushbyte_2
pha
pha
// [5] callprepare next
// [6] callexecute next -- jsr
jsr next
// [7] (signed word~) main::$0 ← callfinalize next
// [7] (signed word~) main::$0 ← callfinalize next -- vwsz1=_stackpullsword_
pla
sta.z _0
pla
sta.z _0+1
// [8] *((const signed word*) SCREEN#0) ← (signed word~) main::$0 -- _deref_pwsc1=vwsz1
lda.z _0
sta SCREEN
lda.z _0+1
sta SCREEN+1
// [9] callprepare next
// [9] callprepare next -- _stackpushbyte_2
pha
pha
// [9] callprepare next
// [10] callexecute next -- jsr
jsr next
// [11] (signed word~) main::$1 ← callfinalize next
// [11] (signed word~) main::$1 ← callfinalize next -- vwsz1=_stackpullsword_
pla
sta.z _1
pla
sta.z _1+1
// [12] *((const signed word*) SCREEN#0+(byte) 1*(const byte) SIZEOF_SIGNED_WORD) ← (signed word~) main::$1 -- _deref_pwsc1=vwsz1
lda.z _1
sta SCREEN+1*SIZEOF_SIGNED_WORD
lda.z _1+1
sta SCREEN+1*SIZEOF_SIGNED_WORD+1
jmp breturn
// main::@return
breturn:
// [13] return
rts
}
// next
next: {
.const OFFSET_STACK_RETURN = 0
.label return = 8
// [15] (signed word) next::return#0 ← (signed word) current#5 -- vwsz1=vwsz2
lda.z current
sta.z return
lda.z current+1
sta.z return+1
jmp breturn
// next::@return
breturn:
// [16] return (signed word) next::return#0
// [16] return (signed word) next::return#0 -- _stackidxsword_vbuc1=vwsz1
tsx
lda.z return
sta STACK_BASE+OFFSET_STACK_RETURN,x
lda.z return+1
sta STACK_BASE+OFFSET_STACK_RETURN+1,x
rts
}
// File Data
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [7] (signed word~) main::$0 ← callfinalize next [ main::$0 ] ( main:2 [ main::$0 ] ) always clobbers reg byte a
Statement [8] *((const signed word*) SCREEN#0) ← (signed word~) main::$0 [ ] ( main:2 [ ] ) always clobbers reg byte a
Statement [11] (signed word~) main::$1 ← callfinalize next [ main::$1 ] ( main:2 [ main::$1 ] ) always clobbers reg byte a
Statement [12] *((const signed word*) SCREEN#0+(byte) 1*(const byte) SIZEOF_SIGNED_WORD) ← (signed word~) main::$1 [ ] ( main:2 [ ] ) always clobbers reg byte a
Statement [15] (signed word) next::return#0 ← (signed word) current#5 [ next::return#0 ] ( main:2::next:6 [ next::return#0 ] main:2::next:10 [ next::return#0 ] ) always clobbers reg byte a
Statement [16] return (signed word) next::return#0 [ ] ( main:2::next:6 [ ] main:2::next:10 [ ] ) always clobbers reg byte a reg byte x
Potential registers zp ZP_WORD:2 [ current#5 ] : zp ZP_WORD:2 ,
Potential registers zp ZP_WORD:4 [ main::$0 ] : zp ZP_WORD:4 ,
Potential registers zp ZP_WORD:6 [ main::$1 ] : zp ZP_WORD:6 ,
Potential registers zp ZP_WORD:8 [ next::return#0 ] : zp ZP_WORD:8 ,
REGISTER UPLIFT SCOPES
Uplift Scope [main] 2: zp ZP_WORD:4 [ main::$0 ] 2: zp ZP_WORD:6 [ main::$1 ]
Uplift Scope [next] 2: zp ZP_WORD:8 [ next::return#0 ]
Uplift Scope [] 2: zp ZP_WORD:2 [ current#5 ]
Uplifting [main] best 140 combination zp ZP_WORD:4 [ main::$0 ] zp ZP_WORD:6 [ main::$1 ]
Uplifting [next] best 140 combination zp ZP_WORD:8 [ next::return#0 ]
Uplifting [] best 140 combination zp ZP_WORD:2 [ current#5 ]
Coalescing zero page register [ zp ZP_WORD:2 [ current#5 ] ] with [ zp ZP_WORD:8 [ next::return#0 ] ] - score: 1
Coalescing zero page register [ zp ZP_WORD:4 [ main::$0 ] ] with [ zp ZP_WORD:2 [ current#5 next::return#0 ] ]
Allocated (was zp ZP_WORD:4) zp ZP_WORD:2 [ main::$0 current#5 next::return#0 ]
Allocated (was zp ZP_WORD:6) zp ZP_WORD:4 [ main::$1 ]
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
// Test a procedure with calling convention stack
// Return value larger than parameter
// Upstart
.pc = $801 "Basic"
:BasicUpstart(bbegin)
.pc = $80d "Program"
// Global Constants & labels
.const SIZEOF_SIGNED_WORD = 2
.label SCREEN = $400
.const STACK_BASE = $103
.label current = 2
// @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
.label _1 = 4
// [5] callprepare next
// [5] callprepare next -- _stackpushbyte_2
pha
pha
// [5] callprepare next
// [6] callexecute next -- jsr
jsr next
// [7] (signed word~) main::$0 ← callfinalize next
// [7] (signed word~) main::$0 ← callfinalize next -- vwsz1=_stackpullsword_
pla
sta.z _0
pla
sta.z _0+1
// [8] *((const signed word*) SCREEN#0) ← (signed word~) main::$0 -- _deref_pwsc1=vwsz1
lda.z _0
sta SCREEN
lda.z _0+1
sta SCREEN+1
// [9] callprepare next
// [9] callprepare next -- _stackpushbyte_2
pha
pha
// [9] callprepare next
// [10] callexecute next -- jsr
jsr next
// [11] (signed word~) main::$1 ← callfinalize next
// [11] (signed word~) main::$1 ← callfinalize next -- vwsz1=_stackpullsword_
pla
sta.z _1
pla
sta.z _1+1
// [12] *((const signed word*) SCREEN#0+(byte) 1*(const byte) SIZEOF_SIGNED_WORD) ← (signed word~) main::$1 -- _deref_pwsc1=vwsz1
lda.z _1
sta SCREEN+1*SIZEOF_SIGNED_WORD
lda.z _1+1
sta SCREEN+1*SIZEOF_SIGNED_WORD+1
jmp breturn
// main::@return
breturn:
// [13] return
rts
}
// next
next: {
.const OFFSET_STACK_RETURN = 0
.label return = 2
// [15] (signed word) next::return#0 ← (signed word) current#5
jmp breturn
// next::@return
breturn:
// [16] return (signed word) next::return#0
// [16] return (signed word) next::return#0 -- _stackidxsword_vbuc1=vwsz1
tsx
lda.z return
sta STACK_BASE+OFFSET_STACK_RETURN,x
lda.z return+1
sta STACK_BASE+OFFSET_STACK_RETURN+1,x
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
(signed word*) SCREEN
(const signed word*) SCREEN#0 SCREEN = (signed word*) 1024
(const byte) SIZEOF_SIGNED_WORD SIZEOF_SIGNED_WORD = (byte) 2
(const word) STACK_BASE STACK_BASE = (word) $103
(signed word) current
(signed word) current#5 current zp ZP_WORD:2 2.0
(void()) main()
(signed word~) main::$0 $0 zp ZP_WORD:2 2.0
(signed word~) main::$1 $1 zp ZP_WORD:4 2.0
(label) main::@return
__stackcall (signed word()) next()
(label) next::@return
(const byte) next::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 0
(signed word) next::return
(signed word) next::return#0 return zp ZP_WORD:2 2.0
zp ZP_WORD:2 [ main::$0 current#5 next::return#0 ]
zp ZP_WORD:4 [ main::$1 ]
FINAL ASSEMBLER
Score: 110
// File Comments
// Test a procedure with calling convention stack
// Return value larger than parameter
// Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
// Global Constants & labels
.const SIZEOF_SIGNED_WORD = 2
.label SCREEN = $400
.const STACK_BASE = $103
.label current = 2
// @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
.label _1 = 4
// next()
// [5] callprepare next
// [5] callprepare next -- _stackpushbyte_2
pha
pha
// [5] callprepare next
// [6] callexecute next -- jsr
jsr next
// [7] (signed word~) main::$0 ← callfinalize next
// [7] (signed word~) main::$0 ← callfinalize next -- vwsz1=_stackpullsword_
pla
sta.z _0
pla
sta.z _0+1
// SCREEN[0] = next()
// [8] *((const signed word*) SCREEN#0) ← (signed word~) main::$0 -- _deref_pwsc1=vwsz1
lda.z _0
sta SCREEN
lda.z _0+1
sta SCREEN+1
// next()
// [9] callprepare next
// [9] callprepare next -- _stackpushbyte_2
pha
pha
// [9] callprepare next
// [10] callexecute next -- jsr
jsr next
// [11] (signed word~) main::$1 ← callfinalize next
// [11] (signed word~) main::$1 ← callfinalize next -- vwsz1=_stackpullsword_
pla
sta.z _1
pla
sta.z _1+1
// SCREEN[1] = next()
// [12] *((const signed word*) SCREEN#0+(byte) 1*(const byte) SIZEOF_SIGNED_WORD) ← (signed word~) main::$1 -- _deref_pwsc1=vwsz1
lda.z _1
sta SCREEN+1*SIZEOF_SIGNED_WORD
lda.z _1+1
sta SCREEN+1*SIZEOF_SIGNED_WORD+1
// main::@return
// }
// [13] return
rts
}
// next
next: {
.const OFFSET_STACK_RETURN = 0
.label return = 2
// return current++;
// [15] (signed word) next::return#0 ← (signed word) current#5
// next::@return
// }
// [16] return (signed word) next::return#0
// [16] return (signed word) next::return#0 -- _stackidxsword_vbuc1=vwsz1
tsx
lda.z return
sta STACK_BASE+OFFSET_STACK_RETURN,x
lda.z return+1
sta STACK_BASE+OFFSET_STACK_RETURN+1,x
rts
}
// File Data

View File

@ -0,0 +1,21 @@
(label) @1
(label) @begin
(label) @end
(signed word*) SCREEN
(const signed word*) SCREEN#0 SCREEN = (signed word*) 1024
(const byte) SIZEOF_SIGNED_WORD SIZEOF_SIGNED_WORD = (byte) 2
(const word) STACK_BASE STACK_BASE = (word) $103
(signed word) current
(signed word) current#5 current zp ZP_WORD:2 2.0
(void()) main()
(signed word~) main::$0 $0 zp ZP_WORD:2 2.0
(signed word~) main::$1 $1 zp ZP_WORD:4 2.0
(label) main::@return
__stackcall (signed word()) next()
(label) next::@return
(const byte) next::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 0
(signed word) next::return
(signed word) next::return#0 return zp ZP_WORD:2 2.0
zp ZP_WORD:2 [ main::$0 current#5 next::return#0 ]
zp ZP_WORD:4 [ main::$1 ]