Merge branch 'far-call-isolated-updates' into 'master'

Merging far calls...

See merge request camelot/kickc!47
This commit is contained in:
Jesper Balman Gravgaard 2023-04-24 04:51:51 +00:00
commit 3d03993175
95 changed files with 12010 additions and 59 deletions

7
.gitignore vendored
View File

@ -1,3 +1,5 @@
/gen
**/.vscode/*
*/*.mon
*/*.vs
*/*.brk
@ -5,11 +7,12 @@
*/*.sym
*/.tmpdirs
*/bin/
/target/
**/target/*
*/workspace.xml
**/.DS_Store
.project
.tmpdirs
**/.vscode/*.log
/.idea/*
kickc.iml
kickc.iml
/src/main/fragment/cache

View File

@ -75,6 +75,7 @@ LOCAL_RESERVE: '__zp_reserve' ;
ADDRESS: '__address' ;
ADDRESS_ZEROPAGE: '__zp' ;
ADDRESS_MAINMEM: '__mem' ;
BANK: '__bank' ;
FORM_SSA: '__ssa' ;
FORM_MA: '__ma' ;
INTRINSIC: '__intrinsic' ;

View File

@ -161,6 +161,7 @@ directive
| EXTERN #directiveExtern
| EXPORT #directiveExport
| INLINE #directiveInline
| BANK PAR_BEGIN NAME COMMA NUMBER PAR_END #directiveBank
| INTRINSIC #directiveIntrinsic
| INTERRUPT ( PAR_BEGIN NAME PAR_END )? #directiveInterrupt
| LOCAL_RESERVE PAR_BEGIN pragmaParam ( COMMA pragmaParam )* PAR_END #directiveReserveZp

View File

@ -0,0 +1,11 @@
sta $ff
lda $0
pha
lda #{c1}
sta $0
lda $ff
jsr {la1}
sta $ff
pla
sta $0
lda $ff

View File

@ -0,0 +1,11 @@
sta $ff
lda $1
pha
lda #{c1}
sta $1
lda $ff
jsr {la1}
sta $ff
pla
sta $1
lda $ff

View File

@ -0,0 +1,4 @@
jsr $FF6E
.byte <{la1}
.byte >{la1}
.byte {c1}

View File

@ -0,0 +1,4 @@
jsr $FF6E
.byte <{la1}
.byte >{la1}
.byte {c1}

View File

@ -0,0 +1 @@
jsr {la1}

View File

@ -224,6 +224,7 @@ public class Compiler {
new Pass1Procedures(program).execute();
new PassNTypeInference(program).execute();
new PassNFixIntermediateMemoryArea(program).execute();
new Pass1FixProcedureParamSegment(program).execute();
new PassNTypeIdSimplification(program).execute();
new Pass1StructTypeSizeFix(program).execute();
new Pass1PrintfIntrinsicRewrite(program).execute();

View File

@ -13,7 +13,9 @@ import dk.camelot64.kickc.model.operators.OperatorBinary;
import dk.camelot64.kickc.model.operators.OperatorUnary;
import dk.camelot64.kickc.model.operators.Operators;
import dk.camelot64.kickc.model.statements.*;
import dk.camelot64.kickc.model.symbols.Bank;
import dk.camelot64.kickc.model.symbols.Label;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.symbols.Symbol;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.types.SymbolTypeInference;
@ -41,6 +43,27 @@ final public class AsmFragmentInstanceSpecBuilder {
return new AsmFragmentInstanceSpec(program, signature, bindings, codeScope);
}
/**
* Create a fragment instance spec factory for a banked call
*
* @param toProcedure The procedure being called
* @param callingDistance The calling distance of the call
* @param program The program
* @return the fragment instance spec factory
*/
public static AsmFragmentInstanceSpec callBanked(Procedure toProcedure, Bank.CallingDistance callingDistance, Program program) {
final Bank toBank = toProcedure.getBank();
AsmFragmentBindings bindings = new AsmFragmentBindings(program);
AsmFragmentSignature signature = new AsmFragmentSignature.CallBanked(
toProcedure.getCallingConvention().getShortName(),
callingDistance.toString(),
(callingDistance.equals(Bank.CallingDistance.NEAR)?null:toBank.bankArea()));
ScopeRef codeScope = program.getScope().getRef();
bindings.bind("c1", new ConstantInteger(toBank.bankNumber()));
bindings.bind("la1", new LabelRef(toProcedure.getFullName()));
return new AsmFragmentInstanceSpec(program, signature, bindings, codeScope);
}
/**
* Create a fragment instance spec factory for an interrupt routine entry
*

View File

@ -143,6 +143,7 @@ public class AsmFragmentTemplate {
if(signature.contains("c6")) bindings.put("c6", new ConstantInteger(360L));
if(signature.contains("la1")) bindings.put("la1", new Label("@1", scope, true));
if(signature.startsWith("call_")) bindings.put("la1", new Label("@1", scope, true));
if(signature.startsWith("call_")) bindings.put("c1", new ConstantInteger(400L));
AsmFragmentInstance fragmentInstance =
new AsmFragmentInstance(new Program(), signature, ScopeRef.ROOT, this, bindings);
AsmProgram asm = new AsmProgram(targetCpu);

View File

@ -54,7 +54,7 @@ public interface AsmFragmentSignature {
}
/**
* ASM fragment signature for a conditional jump <code>if(A) goto B</code>.
* ASM fragment signature for a call
*/
class Call implements AsmFragmentSignature {
@ -70,6 +70,29 @@ public interface AsmFragmentSignature {
}
}
/**
* ASM fragment signature for a banked call
*/
class CallBanked implements AsmFragmentSignature {
final private String callingConvention;
final private String callingDistance;
final private String toBankArea;
public CallBanked(String callingConvention, String callingDistance, String toBankArea) {
this.callingConvention = callingConvention;
this.callingDistance = callingDistance;
this.toBankArea = toBankArea;
}
@Override
public String getName() {
return "call_" + callingConvention + "_" + callingDistance + ((toBankArea==null)?"":("_"+toBankArea));
}
}
/**
* ASM fragment signature for a conditional jump <code>if(A) goto B</code>.
*/

View File

@ -43,6 +43,29 @@ public class Directive {
public Inline() { super("inline"); }
}
/**
* Bank to place code into. Used for determining call distance.
*/
static public class Bank extends Directive {
private String bankArea;
private Long bankNumber;
public Bank(String bankArea, Long bankNumber) {
super("bank" );
this.bankArea = bankArea;
this.bankNumber = bankNumber;
}
public String getBankArea() {
return bankArea;
}
public Long getBankNumber() {
return bankNumber;
}
}
/** Function declared intrinsic. */
public static class Intrinsic extends Directive {
public Intrinsic() { super("intrinsic"); }

View File

@ -0,0 +1,173 @@
package dk.camelot64.kickc.model.symbols;
/**
* <p>
* Specific target computer platforms implement a memory layout that can be banked either in ram or rom.
* This class models the capability to calculate which function call implementations are banked and which not.
* <br>Since banking implementations are specific to the target computer platform,
* specific assembler fragments are available in the compiler,
* that model the implementation of the banking, depending on:
*
* <ul>
* <li>the computer target computer <b><i>platform</i></b>.</li>
* <li>the bank <b><i>method</i></b> of function call implementation at the call location.</li>
* </ul>
*
* <p>The <b></b><i>method</i></b> can implement different banking implementations,
* depending on the required function call speed,
* the banking routine availability in the kernal (rom) of the target computer <b><i>platform</i></b>
* or even using specific designed banking routines by kickc to provide alternatives for banking methods.
* The <b><i>method</i></b> and the <b><i>platform</i></b> are the key combinations that select the fragments
* in kickc to generate the banked function call implementations used by the compiler.
*
* <p><br>Your C-code can be augmented with 3 new directives, that define which function(s) will be declared as banked:
*
* <br><ul>
* <li><b>#pragma bank( <i>area</i>, <i>number</i> )</b> directive, defines for sequent functions
* the the <b><i>area</i></b> and the bank <b><i>number</i></b>.</li>
* <li>A new <b>#pragma nobank( dummy )</b> directive, resets the calculation of banking
* for the sequent functions.</li>
* <li><b>__bank( <i>area</i>, <i>number</i> )</b> directive, defines for one function a
* target bank <b><i>area</i></b> and <b><i>number</i></b>.</li>
* </ul>
* <p>Examples of bank areas are different RAM and/or ROM areas at different
* zones in the linear memory addressing space, and may or may not overlap.
* Each banking area has its own configuration in the target computer platform and
* operate independently from each other.<br>
* For example, the Commander X16 has 256 RAM bank between 0xA000 and 0xBFFF and
* 256 ROM banks from 0xC000 till 0xFFFF. Each RAM bank is configured through zero page $00
* and each ROM bank is configured through zero page $01.
* The compiler configures for you these registers, depending on the configured banking area of the function,
* when a banked call is required.</p>
*
* <p><br>There are different types of function calls that can be implemented, depending on:
* <ul>
* <li>the banked location of either or both the caller and/or the called function.</li>
* <li>the banking area of either or both the caller and the called function.</li>
* </ul>
*
* <p>The <b>types of (banked) function call implementations</b> are:
* <ul>
* <li><b>near</b> - jump to subroutine without any implementation of banking logic.</li>
* <li><b>close</b> - jump to subroutine with inline banking implementation logic.</li>
* <li><b>far</b> - jump to subroutine using a specific trampoline banking implementation logic.</li>
* </ul>
* Depending on the target platform of the system the <b>far</b> calls are likely the most complex
* and will consume the most CPU cycles. Therefore, careful design of the program flow and the
* placement of the functions in the banks need to be done, to avoid as much as possible these far calls.
*
* <p><br>The exact rules when <b>near</b>, <b>close</b> and <b>far</b> calls are implemented by the compiler,
* can be summarized in the following 6 cases:
*
* <ul>
* <li><b>case #1</b> - If the caller function is <b>not banked</b>,<br>
* and the called function is <b>not banked</b>,<br>
* then a <b>near</b> call will be implemented.</li>
* <li><b>case #2</b> - If the caller function is <b>not banked</b>,<br>
* and the called function is <b>banked</b> in any bank in any banking area</b>,<br>
* then a <b>close</b> call will be implemented.</li>
* <li><b>case #3</b> - If the caller function is <b>banked</b> in any bank in any banking area,<br>
* and the called function is <b>not banked</b>,<br>
* then a <b>near</b> call will be implemented.</li>
* <li><b>case #4</b> - If the caller function is <b>banked</b>,<br>
* and the called function is <b>banked</b>,<br>
* and both functions are <b>in the same bank</b> and <b>in the same bank area</b>,<br>
* then a <b>near</b> call will be implemented.</li>
* <li><b>case #5</b> - If the caller function is <b>banked</b>,<br>
* and the called function is <b>banked</b>,<br>
* and both functions are <b>in a different bank</b> but <b>in the same bank area</b>,<br>
* then a <b>far</b> call will be implemented.</li>
* <li><b>case #6</b> - If the caller function is <b>banked</b>,<br>
* and the called function is <b>banked</b>,<br>
* and both functions are <b>in any bank</b> but <b>in a different bank area</b>,<br>
* then the a <b>close</b> call will be implemented.</li>
* </ul>
*
* <p>The usage of <b>#pragma code_seg( <i>segment</i> )</b> directive
* is very important! The <b>#pragma code_seg( <i>segment</i> )</b> directive utilization
* in harmony with the <b>#pragma bank( <i>method</i>, <i>number</i> )</b> directive
* makes the overall banking implementation work for your program.
*
* <p>
* <ul>
* <li>KickC uses the <b>#pragma code_seg( <i>segment</i> )</b> directive to calculate the
* <b>addressing</b> of the allocation of the function code within main or banked memory.</li>
* <li>KickC uses the <b>#pragma bank( <i>method</i>, <i>number</i> )</b> directive or
* <b>__bank( <i>method</i>, <i>number</i> )</b> directive to calculate the function call implementation
* at the function calling locations!</li>
* </ul>
* <p>
* The KickC compiler contains several test cases and examples which demonstrate the usage of the banking system.
*
* @param bankArea The bankable area name.
* @param bankNumber The bank number.
*/
public record Bank(String bankArea, Long bankNumber) {
/**
* Creates a new Bank which collects the necessary data to handle banking.
* For example, on the Commander X16, RAM is banked from address 0xA000 till 0xBFFF.
* Zeropage 0x00 configures this banked RAM, with a number from 0x00 till 0xff.
* So "Banked RAM" is is a bankArea, and the bank is a configurable bank number in the bankArea.
*
* @param bankArea A bank area is a memory range that is banked on a target platform.
* @param bankNumber A bank is a number that defines a bank configuration in a bank area.
*/
public Bank {
}
/** The common/shared bank which is always visible. */
public static Bank COMMON = new Bank("", 0L);
/**
* Is this the common/shared bank which is always visible.
* @return True if this is the common bank
*/
public boolean isCommon() {
return COMMON.equals(this);
}
@Override
public String toString() {
if(isCommon()) {
return "";
} else {
return "__bank(" + this.bankArea() + ", " + this.bankNumber() + ") ";
}
}
/** The bank distance between a caller and callee, which will determine the type of call needed. */
public enum CallingDistance {
/** Caller and callee are both in the same bank or in the common bank. No bank change is needed. */
NEAR,
/** Caller is in the common bank or a different banking area. A direct bank change is needed. */
CLOSE,
/** Caller and callee are different banks of the same banking area. A trampoline bank change is needed. */
FAR;
public static CallingDistance forCall(Bank from, Bank to) {
if(to.isCommon()) {
// NEAR: call to the common bank
return NEAR;
} else if(to.equals(from)) {
// NEAR: call to the same bank in the same banking area
return NEAR;
} else if(from.isCommon()) {
// CLOSE: call from common bank to any bank
return CLOSE;
} else if(!from.bankArea().equals(to.bankArea())) {
// CLOSE: from one banking area to another
return CLOSE;
} else {
// FAR: banked to different bank in same bank area
return FAR;
}
}
@Override
public String toString() {
return name().toLowerCase();
}
}
}

View File

@ -33,14 +33,19 @@ public class Procedure extends Scope {
private List<Comment> comments;
/** Reserved zeropage addresses. */
private List<Integer> reservedZps;
/** The code segment to put the procedure into. */
private String codeSegment;
/** The code segment to put the procedure code into. When null the procedure is not assigned to the code segment. */
private String segmentCode;
/** The list of constructor procedures for this procedure. The constructor procedures are called during program initialization. */
private final List<ProcedureRef> constructorRefs;
/** Is this procedure declared as a constructor procedure. */
private boolean isConstructor;
/** The source of the procedure definition. */
private StatementSource definitionSource;
/**
* The bank that the procedure code is placed in.
* Used to decide whether to produce near, close or far call code when generating calls.
*/
private Bank bank;
/** The names of all legal intrinsic procedures. */
final public static List<String> INTRINSIC_PROCEDURES = Arrays.asList(
@ -50,27 +55,42 @@ public class Procedure extends Scope {
Pass1ByteXIntrinsicRewrite.INTRINSIC_MAKELONG4
);
public Bank getBank() {
return bank;
}
public void setBank(Bank bank) {
this.bank = Objects.requireNonNull(bank);
}
/** The method for passing parameters and return value to the procedure. */
public enum CallingConvention {
/** Parameters and return value handled through PHI-transitions. */
PHI_CALL("__phicall"),
PHI_CALL("__phicall", "phi"),
/** Parameters and return value over the stack. */
STACK_CALL("__stackcall"),
STACK_CALL("__stackcall", "stack"),
/** Parameters and return value handled through shared variables. */
VAR_CALL("__varcall"),
VAR_CALL("__varcall", "var"),
/** Intrinsic calling. Will be converted to intrinsic ASM late in the compile. */
INTRINSIC_CALL("__intrinsiccall");
INTRINSIC_CALL("__intrinsiccall", "intrinsic");
private final String name;
CallingConvention(String name) {
private final String shortName;
CallingConvention(String name, String shortName) {
this.name = name;
this.shortName = shortName;
}
public String getName() {
return name;
}
public String getShortName() {
return shortName;
}
/** Get a calling convention by name. */
public static CallingConvention getCallingConvension(String name) {
for(CallingConvention value : CallingConvention.values()) {
@ -85,13 +105,14 @@ public class Procedure extends Scope {
/** The calling convention used for this procedure. */
private CallingConvention callingConvention;
public Procedure(String name, SymbolTypeProcedure procedureType, Scope parentScope, String codeSegment, String dataSegment, CallingConvention callingConvention) {
super(name, parentScope, dataSegment);
public Procedure(String name, SymbolTypeProcedure procedureType, Scope parentScope, String segmentCode, String segmentData, CallingConvention callingConvention, Bank bank) {
super(name, parentScope, segmentData);
this.procedureType = procedureType;
this.declaredInline = false;
this.bank = Objects.requireNonNull(bank);
this.interruptType = null;
this.comments = new ArrayList<>();
this.codeSegment = codeSegment;
this.segmentCode = Objects.requireNonNull(segmentCode);
this.callingConvention = callingConvention;
this.constructorRefs = new ArrayList<>();
this.isConstructor = false;
@ -113,12 +134,12 @@ public class Procedure extends Scope {
this.callingConvention = callingConvention;
}
public String getCodeSegment() {
return codeSegment;
public String getSegmentCode() {
return segmentCode;
}
public void setCodeSegment(String codeSegment) {
this.codeSegment = codeSegment;
public void setSegmentCode(String segmentCode) {
this.segmentCode = segmentCode;
}
public List<String> getParameterNames() {
@ -262,6 +283,7 @@ public class Procedure extends Scope {
if(declaredIntrinsic) {
res.append("__intrinsic ");
}
res.append(bank.toString());
if(!callingConvention.equals(CallingConvention.PHI_CALL)) {
res.append(getCallingConvention().getName()).append(" ");
}
@ -307,13 +329,14 @@ public class Procedure extends Scope {
Objects.equals(interruptType, procedure.interruptType) &&
Objects.equals(comments, procedure.comments) &&
Objects.equals(reservedZps, procedure.reservedZps) &&
Objects.equals(codeSegment, procedure.codeSegment) &&
Objects.equals(segmentCode, procedure.segmentCode) &&
Objects.equals(constructorRefs, procedure.constructorRefs) &&
Objects.equals(bank, procedure.bank) &&
callingConvention == procedure.callingConvention;
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), procedureType, parameterNames, variableLengthParameterList, declaredInline, declaredIntrinsic, interruptType, comments, reservedZps, codeSegment, constructorRefs, isConstructor, callingConvention);
return Objects.hash(super.hashCode(), procedureType, parameterNames, variableLengthParameterList, declaredInline, declaredIntrinsic, interruptType, comments, reservedZps, segmentCode, constructorRefs, isConstructor, bank, callingConvention);
}
}

View File

@ -30,7 +30,7 @@ public abstract class Scope implements Symbol {
this.name = name;
this.parentScope = parentScope;
this.symbols = new LinkedHashMap<>();
this.segmentData = segmentData;
this.segmentData = Objects.requireNonNull(segmentData);
setFullName();
}
@ -43,6 +43,10 @@ public abstract class Scope implements Symbol {
return segmentData;
}
public void setSegmentData(String segmentData) {
this.segmentData = segmentData;
}
public HashMap<String, Symbol> getSymbols() {
return symbols;
}

View File

@ -106,6 +106,14 @@ public class CParser {
* The resource-file is copied to the output directory when compiling.
*/
public static final String PRAGMA_RESOURCE = "resource";
/**
* #pragma bank(...) changes the current bank. Functions and variables will be placed in the specified bank.
*/
public static final String PRAGMA_BANK = "bank";
/**
* #pragma nobank Changes the current bank to the default/common/shared bank.
*/
public static final String PRAGMA_NOBANK = "nobank";
/**
* The Program.

View File

@ -16,7 +16,6 @@ import dk.camelot64.kickc.parser.KickCParserBaseVisitor;
import dk.camelot64.kickc.passes.utils.SizeOfConstants;
import org.antlr.v4.runtime.BufferedTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.RuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
@ -131,7 +130,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
Procedure initProc = program.getScope().getLocalProcedure(SymbolRef.INIT_PROC_NAME);
if(initProc == null) {
// Create the _init() procedure
initProc = new Procedure(SymbolRef.INIT_PROC_NAME, new SymbolTypeProcedure(SymbolType.VOID, new ArrayList<>()), program.getScope(), Scope.SEGMENT_CODE_DEFAULT, Scope.SEGMENT_DATA_DEFAULT, Procedure.CallingConvention.PHI_CALL);
initProc = new Procedure(SymbolRef.INIT_PROC_NAME, new SymbolTypeProcedure(SymbolType.VOID, new ArrayList<>()), program.getScope(), Scope.SEGMENT_CODE_DEFAULT, Scope.SEGMENT_DATA_DEFAULT, Procedure.CallingConvention.PHI_CALL, Bank.COMMON);
initProc.setDeclaredInline(true);
initProc.setParameters(new ArrayList<>());
program.getScope().add(initProc);
@ -188,7 +187,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
// Add the _start() procedure to the program
{
program.setStartProcedure(new ProcedureRef(SymbolRef.START_PROC_NAME));
final Procedure startProcedure = new Procedure(SymbolRef.START_PROC_NAME, new SymbolTypeProcedure(SymbolType.VOID, new ArrayList<>()), program.getScope(), Scope.SEGMENT_CODE_DEFAULT, Scope.SEGMENT_DATA_DEFAULT, Procedure.CallingConvention.PHI_CALL);
final Procedure startProcedure = new Procedure(SymbolRef.START_PROC_NAME, new SymbolTypeProcedure(SymbolType.VOID, new ArrayList<>()), program.getScope(), Scope.SEGMENT_CODE_DEFAULT, Scope.SEGMENT_DATA_DEFAULT, Procedure.CallingConvention.PHI_CALL, Bank.COMMON);
startProcedure.setParameters(new ArrayList<>());
program.getScope().add(startProcedure);
final ProcedureCompilation startProcedureCompilation = program.createProcedureCompilation(startProcedure.getRef());
@ -289,10 +288,24 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
}
break;
case CParser.PRAGMA_CODE_SEG:
this.currentCodeSegment = pragmaParamName(pragmaParamSingle(ctx));
this.currentSegmentCode = pragmaParamName(pragmaParamSingle(ctx));
break;
case CParser.PRAGMA_DATA_SEG:
this.currentDataSegment = pragmaParamName(pragmaParamSingle(ctx));
this.currentSegmentData = pragmaParamName(pragmaParamSingle(ctx));
break;
case CParser.PRAGMA_BANK:
if(ctx.pragmaParam().size() != 2)
throw new CompileError("#pragma expects two parameters!", new StatementSource(ctx));
try {
final String pragmaBankArea = pragmaParamName(ctx.pragmaParam(0));
final Number pragmaBank = pragmaParamNumber(ctx.pragmaParam(1));
this.currentBank = new Bank(pragmaBankArea, pragmaBank.longValue());
} catch(IllegalArgumentException e) {
throw new CompileError("Illegal bank parameter " + ctx.getText(), new StatementSource(ctx));
}
break;
case CParser.PRAGMA_NOBANK:
this.currentBank = Bank.COMMON; // When the current segment is null, the procedure will not be declared as far.
break;
case CParser.PRAGMA_RESOURCE:
String resourceFileName = pragmaParamString(pragmaParamSingle(ctx));
@ -431,10 +444,13 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
private Procedure.CallingConvention currentCallingConvention;
/** The current code segment. */
private String currentCodeSegment = Scope.SEGMENT_CODE_DEFAULT;
private String currentSegmentCode = Scope.SEGMENT_CODE_DEFAULT;
/** The current data segment. */
private String currentDataSegment = Scope.SEGMENT_DATA_DEFAULT;
private String currentSegmentData = Scope.SEGMENT_DATA_DEFAULT;
/** The current far segment. If null, the sequent procedures won't be banked. */
private Bank currentBank = Bank.COMMON;
/** The current default interrupt type. */
private String currentInterruptType;
@ -491,7 +507,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
*/
private Procedure declareProcedure(boolean defineProcedure, ParserRuleContext ctx, StatementSource statementSource) {
Procedure procedure = new Procedure(varDecl.getVarName(), (SymbolTypeProcedure) varDecl.getEffectiveType(), program.getScope(), currentCodeSegment, currentDataSegment, currentCallingConvention);
Procedure procedure = new Procedure(varDecl.getVarName(), (SymbolTypeProcedure) varDecl.getEffectiveType(), program.getScope(), currentSegmentCode, currentSegmentData, currentCallingConvention, currentBank);
addDirectives(procedure, varDecl.getDeclDirectives(), statementSource);
// Check if the declaration matches any existing declaration!
final Symbol existingSymbol = program.getScope().getSymbol(procedure.getRef());
@ -543,15 +559,19 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
if(parameter.name == null)
throw new CompileError("Illegal unnamed parameter.", statementSource);
VariableBuilder varBuilder = new VariableBuilder(parameter.name, getCurrentScope(), true, false, parameter.type, null, currentDataSegment, program.getTargetPlatform().getVariableBuilderConfig());
VariableBuilder varBuilder = new VariableBuilder(parameter.name, getCurrentScope(), true, false, parameter.type, null, currentSegmentData, program.getTargetPlatform().getVariableBuilderConfig());
final Variable paramVar = varBuilder.build();
parameterList.add(paramVar);
}
procedure.setParameters(parameterList);
procedure.setCodeSegment(currentCodeSegment); // When a procedure is defined, the currentCodeSegment is to be set.
procedure.setSegmentData(currentSegmentData);
procedure.setSegmentCode(currentSegmentCode);
if(procedure.getBank().isCommon()) {
procedure.setBank(currentBank);
}
// Add return variable
if(!SymbolType.VOID.equals(procedure.getReturnType())) {
final VariableBuilder builder = new VariableBuilder("return", procedure, false, false, procedure.getReturnType(), varDecl.getDeclDirectives(), currentDataSegment, program.getTargetPlatform().getVariableBuilderConfig());
final VariableBuilder builder = new VariableBuilder("return", procedure, false, false, procedure.getReturnType(), varDecl.getDeclDirectives(), currentSegmentData, program.getTargetPlatform().getVariableBuilderConfig());
builder.build();
}
// exit the procedure
@ -1026,7 +1046,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
final List<Directive> effectiveDirectives = varDecl.getDeclDirectives();
final List<Comment> declComments = varDecl.getDeclComments();
varDecl.exitVar();
VariableBuilder varBuilder = new VariableBuilder(varName, getCurrentScope(), false, false, effectiveType, effectiveDirectives, currentDataSegment, program.getTargetPlatform().getVariableBuilderConfig());
VariableBuilder varBuilder = new VariableBuilder(varName, getCurrentScope(), false, false, effectiveType, effectiveDirectives, currentSegmentData, program.getTargetPlatform().getVariableBuilderConfig());
Variable variable = varBuilder.build();
if(isStructMember && (initializer != null))
throw new CompileError("Initializer not supported inside structs " + effectiveType.toCDecl(), declSource);
@ -1136,7 +1156,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
ConstantArrayKickAsm constantArrayKickAsm = new ConstantArrayKickAsm(((SymbolTypePointer) varDecl.getEffectiveType()).getElementType(), kasm.kickAsmCode, kasm.uses, ((SymbolTypePointer) effectiveType).getArraySpec().getArraySize());
// Add a constant variable
Scope scope = getCurrentScope();
VariableBuilder varBuilder = new VariableBuilder(varName, scope, false, false, varDecl.getEffectiveType(), varDecl.getDeclDirectives(), currentDataSegment, program.getTargetPlatform().getVariableBuilderConfig());
VariableBuilder varBuilder = new VariableBuilder(varName, scope, false, false, varDecl.getEffectiveType(), varDecl.getDeclDirectives(), currentSegmentData, program.getTargetPlatform().getVariableBuilderConfig());
Variable variable = varBuilder.build();
// Set constant value
variable.setInitValue(getConstInitValue(constantArrayKickAsm, null, statementSource));
@ -1171,6 +1191,9 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
if(directive instanceof Directive.Inline) {
procedure.setDeclaredInline(true);
procedure.setCallingConvention(Procedure.CallingConvention.PHI_CALL);
} else if(directive instanceof Directive.Bank directiveBank) {
Bank bank = new Bank(directiveBank.getBankArea(), directiveBank.getBankNumber());
procedure.setBank(bank);
} else if(directive instanceof Directive.CallingConvention) {
procedure.setCallingConvention(((Directive.CallingConvention) directive).callingConvention);
} else if(directive instanceof Directive.Interrupt) {
@ -1214,6 +1237,13 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
return new Directive.Inline();
}
@Override
public Object visitDirectiveBank(KickCParser.DirectiveBankContext ctx) {
String bankArea = ctx.NAME().getText();
Number bankNumber = NumberParser.parseLiteral(ctx.NUMBER().getText());
return new Directive.Bank(bankArea, bankNumber.longValue());
}
@Override
public Object visitDirectiveIntrinsic(KickCParser.DirectiveIntrinsicContext ctx) {
return new Directive.Intrinsic();
@ -1675,7 +1705,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
String varName = varDecl.getVarName();
Variable lValue;
if(varType != null) {
VariableBuilder varBuilder = new VariableBuilder(varName, blockScope, false, false, varType, varDecl.getDeclDirectives(), currentDataSegment, program.getTargetPlatform().getVariableBuilderConfig());
VariableBuilder varBuilder = new VariableBuilder(varName, blockScope, false, false, varType, varDecl.getDeclDirectives(), currentSegmentData, program.getTargetPlatform().getVariableBuilderConfig());
lValue = varBuilder.build();
} else {
lValue = getCurrentScope().findVariable(varName);
@ -2010,7 +2040,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
while(parentScope instanceof StructDefinition || parentScope instanceof TypeDefsScope)
parentScope = parentScope.getScope();
for(Variable member : enumDefinition.getAllConstants(false)) {
parentScope.add(Variable.createConstant(member.getLocalName(), SymbolType.BYTE, parentScope, member.getInitValue(), currentDataSegment));
parentScope.add(Variable.createConstant(member.getLocalName(), SymbolType.BYTE, parentScope, member.getInitValue(), currentSegmentData));
}
varDecl.setDeclType(SymbolType.BYTE);
return null;
@ -2045,7 +2075,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
}
}
}
currentEnum.add(Variable.createConstant(memberName, SymbolType.BYTE, getCurrentScope(), enumValue, currentDataSegment));
currentEnum.add(Variable.createConstant(memberName, SymbolType.BYTE, getCurrentScope(), enumValue, currentSegmentData));
return null;
}
@ -2227,7 +2257,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
this.visit(ctx.declType());
this.visit(ctx.declarator());
String typedefName = varDecl.getVarName();
VariableBuilder varBuilder = new VariableBuilder(typedefName, getCurrentScope(), false, false, varDecl.getEffectiveType(), varDecl.getDeclDirectives(), currentDataSegment, program.getTargetPlatform().getVariableBuilderConfig());
VariableBuilder varBuilder = new VariableBuilder(typedefName, getCurrentScope(), false, false, varDecl.getEffectiveType(), varDecl.getDeclDirectives(), currentSegmentData, program.getTargetPlatform().getVariableBuilderConfig());
varBuilder.build();
scopeStack.pop();
varDecl.exitType();
@ -2478,7 +2508,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
new SymbolTypeProcedure(SymbolType.DWORD, Arrays.asList(new SymbolType[]{SymbolType.BYTE, SymbolType.BYTE, SymbolType.BYTE, SymbolType.BYTE})),
program.getScope(),
Scope.SEGMENT_CODE_DEFAULT, Scope.SEGMENT_DATA_DEFAULT,
Procedure.CallingConvention.INTRINSIC_CALL);
Procedure.CallingConvention.INTRINSIC_CALL, Bank.COMMON);
makeword4.setDeclaredIntrinsic(true);
final Variable hihi = new Variable("hihi", Variable.Kind.PHI_MASTER, SymbolType.BYTE, makeword4, Variable.MemoryArea.ZEROPAGE_MEMORY, Scope.SEGMENT_DATA_DEFAULT, null);
makeword4.add(hihi);

View File

@ -0,0 +1,46 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.Registers;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.symbols.Scope;
import dk.camelot64.kickc.model.symbols.Variable;
import java.util.Collection;
import java.util.List;
/**
* Ensure banked procedure parameters/return values in main memory are placed in the default data segment.
* This is needed to support banking, since it is otherwise impossible to access them across bank boundaries during calls.
*/
public class Pass1FixProcedureParamSegment extends Pass2SsaOptimization {
public Pass1FixProcedureParamSegment(Program program) {
super(program);
}
@Override
public boolean step() {
final Collection<Variable> allVariables = getScope().getAllVariables(true);
for(Variable variable : allVariables) {
if(variable.isKindLoadStore() || variable.isKindPhiMaster() || variable.isKindIntermediate()) {
if(variable.getRegister() instanceof Registers.RegisterMainMem registerMainMem && registerMainMem.isAddressHardcoded())
continue;
if(variable.getDataSegment().equals(Scope.SEGMENT_DATA_DEFAULT))
continue;
final Scope scope = variable.getScope();
if(scope instanceof Procedure procedure) {
if(!procedure.getBank().isCommon()) {
List<Variable> parameters = procedure.getParameters();
if(parameters.contains(variable) || variable.getLocalName().equals("return")) {
variable.setDataSegment(Scope.SEGMENT_DATA_DEFAULT);
getLog().append("Fixing banked procedure parameter/return value to default segment " + variable.getFullName());
}
}
}
}
}
return false;
}
}

View File

@ -115,7 +115,7 @@ public class Pass4CodeGeneration {
currentScope = block.getScope();
if (block.isProcedureEntry(program)) {
Procedure procedure = block.getProcedure(program);
currentCodeSegmentName = procedure.getCodeSegment();
currentCodeSegmentName = procedure.getSegmentCode();
}
setCurrentSegment(currentCodeSegmentName, asm);
asm.startChunk(currentScope, null, block.getLabel().getFullName());
@ -219,7 +219,7 @@ public class Pass4CodeGeneration {
}
/**
* Generate a comment that describes the procedure signature and parameter transfer
* Generate a comment that describes the procedure signature and parameter transfer.
*
* @param asm The assembler program being generated
* @param procedure The procedure
@ -240,6 +240,9 @@ public class Pass4CodeGeneration {
if (i > 0) {
asm.addComment(signature.toString(), false);
}
if(!procedure.getBank().isCommon()) {
asm.addComment(" " + procedure.getBank(), false);
}
}
/**
@ -853,14 +856,15 @@ public class Pass4CodeGeneration {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.conditionalJump((StatementConditionalJump) statement, block, program), program);
} else if (statement instanceof StatementCall) {
StatementCall call = (StatementCall) statement;
Procedure procedure = getScope().getProcedure(call.getProcedure());
if (procedure.isDeclaredIntrinsic()) {
if (Pass1ByteXIntrinsicRewrite.INTRINSIC_MAKELONG4.equals(procedure.getFullName())) {
Procedure toProcedure = getScope().getProcedure(call.getProcedure());
Procedure fromProcedure = block.getProcedure(this.program); // We obtain from where the procedure is called, to validate the bank equality.
if (toProcedure.isDeclaredIntrinsic()) {
if (Pass1ByteXIntrinsicRewrite.INTRINSIC_MAKELONG4.equals(toProcedure.getFullName())) {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.makelong4(call, program), program);
} else {
throw new CompileError("Intrinsic procedure not supported " + procedure.toString(program));
throw new CompileError("Intrinsic procedure not supported " + toProcedure.toString(program));
}
} else if (Procedure.CallingConvention.PHI_CALL.equals(procedure.getCallingConvention())) {
} else if (Procedure.CallingConvention.PHI_CALL.equals(toProcedure.getCallingConvention())) {
// Generate PHI transition
if (genCallPhiEntry) {
ControlFlowBlock callSuccessor = getGraph().getCallSuccessor(block);
@ -872,17 +876,44 @@ public class Pass4CodeGeneration {
genBlockPhiTransition(asm, block, callSuccessor, block.getScope());
}
}
asm.addInstruction("jsr", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false);
} else if (Procedure.CallingConvention.STACK_CALL.equals(procedure.getCallingConvention())) {
asm.addInstruction("jsr", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false);
final Bank.CallingDistance callingDistance = Bank.CallingDistance.forCall(fromProcedure.getBank(), toProcedure.getBank());
if(Bank.CallingDistance.NEAR.equals(callingDistance)) {
asm.addInstruction("jsr", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false);
} else {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.callBanked(toProcedure, callingDistance, program), program);
}
} else if (Procedure.CallingConvention.STACK_CALL.equals(toProcedure.getCallingConvention())) {
final Bank.CallingDistance callingDistance = Bank.CallingDistance.forCall(fromProcedure.getBank(), toProcedure.getBank());
if(Bank.CallingDistance.NEAR.equals(callingDistance)) {
asm.addInstruction("jsr", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false);
} else {
throw new CompileError("Stack Call procedure not supported in banked mode " + toProcedure.toString(program));
}
}
} else if (statement instanceof StatementCallExecute) {
StatementCallExecute call = (StatementCallExecute) statement;
RValue procedureRVal = call.getProcedureRVal();
// Generate ASM for a call
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.call(call, indirectCallCount++, program), program);
if (!(procedureRVal instanceof ProcedureRef)) {
asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL);
ProcedureRef procedureRef = call.getProcedure();
if(procedureRef != null) {
ProgramScope scope = getScope();
Procedure toProcedure = scope.getProcedure(procedureRef);
Procedure fromProcedure = block.getProcedure(this.program);
final Bank.CallingDistance callingDistance = Bank.CallingDistance.forCall(fromProcedure.getBank(), toProcedure.getBank());
if(Bank.CallingDistance.NEAR.equals(callingDistance)) {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.call(call, indirectCallCount++, program), program);
} else {
throw new CompileError("Stack Call procedure not supported in banked mode " + toProcedure.toString(program));
}
RValue procedureRVal = call.getProcedureRVal();
if (!(procedureRVal instanceof ProcedureRef)) {
asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL);
}
} else {
RValue procedureRVal = call.getProcedureRVal();
// Generate ASM for a call
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.call(call, indirectCallCount++, program), program);
if (!(procedureRVal instanceof ProcedureRef)) {
asm.getCurrentChunk().setClobberOverwrite(CpuClobber.CLOBBER_ALL);
}
}
} else if (statement instanceof StatementExprSideEffect) {
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.exprSideEffect((StatementExprSideEffect) statement, program), program);

View File

@ -53,6 +53,7 @@ public abstract class Pass4MemoryCoalesce extends Pass2Base {
return
canCoalesceNotEqual(ec1, ec2) &&
canCoalesceCompatible(ec1, ec2, program) &&
canCoalesceSegments(ec1, ec2, program) &&
canCoalesceVolatile(ec1, ec2, program) &&
canCoalesceThreads(ec1, ec2, threadHeads, program) &&
canCoalesceClobber(ec1, ec2, unknownFragments, program);
@ -69,6 +70,21 @@ public abstract class Pass4MemoryCoalesce extends Pass2Base {
return !ec1.equals(ec2);
}
/**
* Determines if two live range equivalence classes are candidates for coalescing by ensuring they are not from different data segments.
* @param ec1 One equivalence class
* @param ec2 Another equivalence class
* @param program The program
* @return True if the two equivalence classes can be coalesced into one without problems.
*/
private static boolean canCoalesceSegments(LiveRangeEquivalenceClass ec1, LiveRangeEquivalenceClass ec2, Program program) {
final VariableRef variableRef1 = ec1.getVariables().get(0);
final String dataSegment1 = program.getScope().getVar(variableRef1).getDataSegment();
final VariableRef variableRef2 = ec2.getVariables().get(0);
final String dataSegment2 = program.getScope().getVar(variableRef2).getDataSegment();
return dataSegment1.equals(dataSegment2);
}
/**
* Determines if two live range equivalence classes can be coalesced without cross-thread clobber.
* This is possible if they are both only called from the same thread head.

View File

@ -767,6 +767,11 @@ public class TestProgramsFast extends TestPrograms {
compileAndCompare("examples/cx16/cx16-rasterbars.c");
}
//@Test
//public void testCx16Banking() throws IOException {
// compileAndCompare("examples/cx16/banking/cx16-banking.c");
//}
@Test
public void testMega65Camelot1536Dots() throws IOException {
compileAndCompare("examples/mega65/camelot-1536dots.c");
@ -1516,6 +1521,83 @@ public class TestProgramsFast extends TestPrograms {
compileAndCompare("declared-memory-var-0.c");
}
@Test
public void testBankedPhiCase1Near0() throws IOException {
compileAndCompare("call-banked-phi-case-1-near-0.c");
}
@Test
public void testBankedPhiCase1Near1() throws IOException {
compileAndCompare("call-banked-phi-case-1-near-1.c");
}
@Test
public void testBankedPhiCase2Close0() throws IOException {
compileAndCompare("call-banked-phi-case-2-close-0.c");
}
@Test
public void testBankedPhiCase2Close1() throws IOException {
compileAndCompare("call-banked-phi-case-2-close-1.c");
}
@Test
public void testBankedPhiCase3Near0() throws IOException {
compileAndCompare("call-banked-phi-case-3-near-0.c");
}
@Test
public void testBankedPhiCase3Near1() throws IOException {
compileAndCompare("call-banked-phi-case-3-near-1.c");
}
@Test
public void testBankedPhiCase4Near0() throws IOException {
compileAndCompare("call-banked-phi-case-4-near-0.c");
}
@Test
public void testBankedPhiCase4Near1() throws IOException {
compileAndCompare("call-banked-phi-case-4-near-1.c");
}
@Test
public void testBankedPhiCase5Far0() throws IOException {
compileAndCompare("call-banked-phi-case-5-far-0.c");
}
@Test
public void testBankedPhiCase5Far1() throws IOException {
compileAndCompare("call-banked-phi-case-5-far-1.c");
}
@Test
public void testBankedPhiCase6Close0() throws IOException {
compileAndCompare("call-banked-phi-case-6-close-0.c");
}
@Test
public void testBankedPhiCase6Close1() throws IOException {
compileAndCompare("call-banked-phi-case-6-close-1.c");
}
@Test
public void testBankedPhiMemvars() throws IOException {
compileAndCompare("call-banked-phi-memvars.c");
}
@Test
public void testBankedStackCase2Close0() throws IOException {
assertError("call-banked-stack-case-2-close-0.c", "Stack Call procedure not supported in banked mode");
}
@Test
public void testBankedStackCase5Far0() throws IOException {
assertError("call-banked-stack-case-5-far-0.c", "Stack Call procedure not supported in banked mode");
}
@Test
public void testProcedureCallingConventionStack13() throws IOException {
compileAndCompare("procedure-callingconvention-stack-13.c");

View File

@ -0,0 +1,41 @@
/**
* @file call-banked-phi-case-1-near-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be)
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #1.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
#pragma link("call-banked-phi.ld")
char* const SCREEN = (char*)0x0400;
#pragma code_seg(Code)
void main(void) {
SCREEN[0] = plus('0', 7); // near call
SCREEN[1] = plus('1', 6); // near call
}
#pragma code_seg(Code)
char plus(char a, char b) {
return a+b;
}

View File

@ -0,0 +1,46 @@
/**
* @file call-banked-phi-case-1-near-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #1
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
#pragma link("call-banked-phi.ld")
char* const SCREEN = (char*)0x0400;
#pragma code_seg(Code)
void main(void) {
SCREEN[0] = plus('0', 7); // near call
SCREEN[1] = plus('1', 6); // near call
}
#pragma code_seg(Code)
char plus(char a, char b) {
return add(a, b); // near call
}
#pragma code_seg(Code)
char add(char a, char b) {
return a+b;
}

View File

@ -0,0 +1,42 @@
/**
* @file call-banked-phi-case-2-close-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #2.
* Implementation using the __bank() directive.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
#pragma link("call-banked-phi.ld")
char* const SCREEN = (char*)0x0400;
#pragma code_seg(Code)
void main(void) {
SCREEN[0] = plus('0', 7); // close call
SCREEN[1] = plus('1', 6); // close call
}
#pragma code_seg(RAM_Bank1)
__bank(cx16_ram,1) char plus(char a, char b) {
return a+b;
}

View File

@ -0,0 +1,43 @@
/**
* @file call-banked-phi-case-2-close-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #2.
* Implementation using the #pragma bank and nobank directives.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
#pragma link("call-banked-phi.ld")
char* const SCREEN = (char*)0x0400;
#pragma code_seg(Code)
void main(void) {
SCREEN[0] = plus('0', 7); // close call
SCREEN[1] = plus('1', 6); // close call
}
#pragma code_seg(RAM_Bank1)
#pragma bank(cx16_ram, 1)
char plus(char a, char b) {
return a+b;
}

View File

@ -0,0 +1,50 @@
/**
* @file call-banked-phi-case-3-near-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #3.
* Implementation using the __bank() directive.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
#pragma link("call-banked-phi.ld")
char* const SCREEN = (char*)0x0400;
#pragma code_seg(Code)
void main(void) {
SCREEN[0] = plus('0', 7); // close call
SCREEN[1] = plus('1', 6); // close call
}
#pragma code_seg(RAM_Bank1)
__bank(cx16_ram,1) char plus(char a, char b) {
return add(a, b); // near call
}
#pragma code_seg(Code)
#pragma nobank
char add(char a, char b) {
return a+b;
}

View File

@ -0,0 +1,51 @@
/**
* @file call-banked-phi-case-3-near-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #3.
* Implementation using the #pragma bank and nobank directives.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
#pragma link("call-banked-phi.ld")
char* const SCREEN = (char*)0x0400;
#pragma code_seg(Code)
void main(void) {
SCREEN[0] = plus('0', 7); // close call
SCREEN[1] = plus('1', 6); // close call
}
#pragma code_seg(RAM_Bank1)
#pragma bank(cx16_ram, 1)
char plus(char a, char b) {
return add(a, b); // near call
}
#pragma code_seg(Code)
#pragma nobank
char add(char a, char b) {
return a+b;
}

View File

@ -0,0 +1,47 @@
/**
* @file call-banked-phi-case-4-near-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #4.
* Implementation using the __bank() directive.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
#pragma link("call-banked-phi.ld")
char* const SCREEN = (char*)0x0400;
#pragma code_seg(Code)
void main(void) {
SCREEN[0] = plus('0', 7); // close call
SCREEN[1] = plus('1', 6); // close call
}
#pragma code_seg(RAM_Bank1)
char __bank(cx16_ram, 1) plus(char a, char b) {
return add(a, b); // near call
}
#pragma code_seg(RAM_Bank1)
char __bank(cx16_ram, 1) add(char a, char b) {
return a+b;
}

View File

@ -0,0 +1,49 @@
/**
* @file call-banked-phi-case-4-near-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #4.
* Implementation using the #pragma bank and nobank directives.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
#pragma link("call-banked-phi.ld")
char* const SCREEN = (char*)0x0400;
#pragma code_seg(Code)
void main(void) {
SCREEN[0] = plus('0', 7); // close call
SCREEN[1] = plus('1', 6); // close call
}
#pragma code_seg(RAM_Bank1)
#pragma bank(cx16_ram, 1)
char plus(char a, char b) {
return add(a, b); // near call
}
#pragma code_seg(RAM_Bank1)
#pragma bank(cx16_ram, 1)
char add(char a, char b) {
return a+b;
}

View File

@ -0,0 +1,47 @@
/**
* @file call-banked-phi-case-5-far-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #5.
* Implementation using the __bank() directive.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
#pragma link("call-banked-phi.ld")
char* const SCREEN = (char*)0x0400;
#pragma code_seg(Code)
void main(void) {
SCREEN[0] = plus('0', 7); // close call
SCREEN[1] = plus('1', 6); // close call
}
#pragma code_seg(RAM_Bank1)
__bank(cx16_ram, 1) char plus(char a, char b) {
return add(a, b); // far call
}
#pragma code_seg(RAM_Bank2)
__bank(cx16_ram, 2) char add(char a, char b) {
return a+b;
}

View File

@ -0,0 +1,49 @@
/**
* @file call-banked-phi-case-5-far-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #5.
* Implementation using the #pragma bank and nobank directives.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
#pragma link("call-banked-phi.ld")
char* const SCREEN = (char*)0x0400;
#pragma code_seg(Code)
void main(void) {
SCREEN[0] = plus('0', 7); // close call
SCREEN[1] = plus('1', 6); // close call
}
#pragma code_seg(RAM_Bank1)
#pragma bank(cx16_ram, 1)
char plus(char a, char b) {
return add(a, b); // far call
}
#pragma code_seg(RAM_Bank2)
#pragma bank(cx16_ram, 2)
char add(char a, char b) {
return a+b;
}

View File

@ -0,0 +1,47 @@
/**
* @file call-banked-phi-case-6-close-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #6.
* Implementation using the __bank() directive.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
#pragma link("call-banked-phi.ld")
char* const SCREEN = (char*)0x0400;
#pragma code_seg(Code)
void main(void) {
SCREEN[0] = plus('0', 7); // close call
SCREEN[1] = plus('1', 6); // close call
}
#pragma code_seg(RAM_Bank1)
__bank(cx16_ram, 1) char plus(char a, char b) {
return add(a, b); // close call
}
#pragma code_seg(ROM_Bank1)
__bank(cx16_rom, 1) char add(char a, char b) {
return a+b;
}

View File

@ -0,0 +1,49 @@
/**
* @file call-banked-phi-case-6-close-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #6.
* Implementation using the #pragma bank and nobank directives.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
#pragma link("call-banked-phi.ld")
char* const SCREEN = (char*)0x0400;
#pragma code_seg(Code)
void main(void) {
SCREEN[0] = plus('0', 7); // close call
SCREEN[1] = plus('1', 6); // close call
}
#pragma code_seg(RAM_Bank1)
#pragma bank(cx16_ram, 1)
char plus(char a, char b) {
return add(a, b); // close call
}
#pragma code_seg(ROM_Bank1)
#pragma bank(cx16_rom, 1)
char add(char a, char b) {
return a+b;
}

View File

@ -0,0 +1,30 @@
/**
* Test banked calls with memory variables.
* The parameters & return should end up in the shared/common bank.
*/
#pragma link("call-banked-phi.ld")
#pragma var_model(mem)
int* const SCREEN = (int*)0x0400;
#pragma code_seg(Code)
#pragma nobank
void main(void) {
for(char i=0;i<5; i++) {
SCREEN[i] = plus(100, (int)i);
SCREEN[10+i] = plus(200, (int)i);
}
}
#pragma code_seg(RAM_Bank1)
#pragma data_seg(RAM_Bank1)
#pragma bank(cx16_ram, 1)
int plus(int a, int b) {
int r = 2;
r += a;
r += b;
r += a;
r += b;
return r;
}

View File

@ -0,0 +1,13 @@
.file [name="%O", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=%P]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(%E)
.segment Code
.segment Data

View File

@ -0,0 +1,42 @@
/**
* @file call-banked-stack-case-2-close-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #2
* The compiler will throw an error because the far call is a __stackcall
* and this is not (yet) implemented and supported.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
#pragma link("call-banked-stack.ld")
char* const SCREEN = (char*)0x0400;
#pragma code_seg(Code)
void main(void) {
SCREEN[0] = plus('0', 7); // close stack call
}
#pragma code_seg(Bank1)
__stackcall __bank(cx16_ram, 1) char plus(char a, char b) {
return a+b;
}

View File

@ -0,0 +1,47 @@
/**
* @file call-banked-stack-case-5-far-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #5.
* The compiler will throw an error because the far call is a __stackcall
* and this is not (yet) implemented and supported.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
#pragma link("call-banked-phi.ld")
char* const SCREEN = (char*)0x0400;
#pragma code_seg(Code)
void main(void) {
SCREEN[0] = plus('0', 7); // close call
}
#pragma code_seg(RAM_Bank1)
__bank(cx16_ram, 1) char plus(char a, char b) {
return min(a, b); // far call
}
#pragma code_seg(RAM_Bank2)
__stackcall __bank(cx16_ram, 2) char min(char a, char b) {
return a+b;
}

View File

@ -0,0 +1,13 @@
.file [name="%O", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=%P]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(%E)
.segment Code
.segment Data

View File

@ -0,0 +1,164 @@
/**
* @file cx16-banking-1.c
* @author your name (you@domain.com)
* @brief This program demonstrates a simple example of a banked call.
* @version 0.1
* @date 2023-04-05
*
* @copyright Copyright (c) 2023
*
*/
// The linker specification of the different segments.
#pragma target(cx16)
#pragma link("cx16-banking.ld")
#pragma var_model(mem)
#include <cx16.h>
#include <conio.h>
#include <printf.h>
// The target computer platform is the Commander X16,
// which implements banking in ram between 0xA0000 and 0xBFFF,
// and in ram between 0xC000 and 0xFFFF.
#pragma target(cx16)
// Functional code
#pragma code_seg(Bank1) // The sequent functions will be addressed specified by segment bank1 in the linker.
#pragma bank(cx16_ram, 1) // The sequent functions will be banked using call method ram in bank number 1.
char add_a(char a) {
printf("add_a(%02x), ", bank_get_bram());
return a+1;
}
char add_c(char a) {
printf("add_c(%02x), ", bank_get_bram());
return add_a(a)+1; // Non banked call in ram bank 1.
}
char add_d(char a) {
printf("add_d(%02x), ",bank_get_bram());
return mul_a(a)+1; // Banked call fram ram bank 1 to ram bank 2.
}
char add_e(char a) {
printf("add_e(%02x), ",bank_get_bram());
return mul_b(a)+1; // Banked call fram ram bank 1 to ram bank 2.
}
char add_f(char a) {
printf("add_f(%02x), ",bank_get_bram());
return add_m(a)+1; // Non banked call fram ram bank 1 to main memory.
}
#pragma code_seg(Bank2) // The sequent functions will be addressed specified by segment bank2 in the linker.
#pragma bank(cx16_ram, 2) // The sequent functions will be banked using call method ram in bank number 2.
char mul_a(char m) {
printf("mul_a(%02x), ",bank_get_bram());
return m * 2;
}
char mul_c(char m) {
printf("mul_c(%02x), ",bank_get_bram());
return add_a(m)*2; // Banked call fram ram bank 2 to ram bank 1.
}
char mul_d(char m) {
printf("mul_d(%02x), ",bank_get_bram());
return mul_a(m)*2; // Non banked call in ram bank 2.
}
char mul_e(char a) {
printf("mul_e(%02x), ",bank_get_bram());
return mul_b(a)*2; // Non Banked call in ram bank 2.
}
char mul_f(char m) {
printf("mul_f(%02x), ",bank_get_bram());
return add_m(m)*2; // Non banked call fram ram bank 2 to main memory.
}
#pragma nobank // The sequent functions will consider no banking calculations anymore.
#pragma code_seg(Bank1) // The sequent functions will be addressed specified by segment bank1 in the linker.
// The __bank directive declares this function to be banked using call method ram in bank number 1 of banked ram.
char __bank(cx16_ram, 1) add_b(char a) {
printf("add_b(%02x), ",bank_get_bram());
return a+1;
}
#pragma code_seg(Bank2) // The sequent functions will be addressed specified by segment bank1 in the linker.
// The __bank directive declares this function to be banked using call method ram in bank number 2 of banked ram.
char __bank(cx16_ram, 2) mul_b(char m) {
printf("mul_b(%02x), ",bank_get_bram());
return m*2;
}
#pragma code_seg(Code) // The sequent functions will be addressed in the default main memory location (segment Code).
// Allocated in main memory.
char add_m(char a) {
printf("add_m(%02x), ",bank_get_bram());
return add_e(a)+1; // Banked call to ram in bank 1 fram main memory.
}
// Allocated in main memory.
char mul_m(char m) {
printf("mul_m(%02x), ",bank_get_bram());
return mul_e(m)*2; // Banked call to ram in bank 2 fram main memory.
}
// Practically this means that the main() function is placed in main memory ...
void load_bank(char bank, char *file) {
bank_set_bram(bank);
cbm_k_setnam(file);
cbm_k_setlfs(1,8,2);
cbm_k_load((char*)0xA000, 0);
cbm_k_close(1);
}
void main(void) {
clrscr();
load_bank(1, "BANK1.BIN");
load_bank(2, "BANK2.BIN");
bank_set_bram(0);
asm{.byte $db}
printf("result = %u\n", add_a(1)); // Banked call to ram in bank 1 fram main memory.
asm{.byte $db}
printf("result = %u\n", add_b(1)); // Banked call to ram in bank 1 fram main memory.
asm{.byte $db}
printf("result = %u\n", add_c(1)); // Banked call to ram in bank 1 fram main memory.
asm{.byte $db}
printf("result = %u\n", add_d(1)); // Banked call to ram in bank 1 fram main memory.
asm{.byte $db}
printf("result = %u\n", add_e(1)); // Banked call to ram in bank 1 fram main memory.
asm{.byte $db}
printf("result = %u\n", add_f(1)); // Banked call to ram in bank 1 fram main memory.
asm{.byte $db}
printf("result = %u\n", mul_a(1)); // Banked call to ram in bank 2 fram main memory.
asm{.byte $db}
printf("result = %u\n", mul_b(1)); // Banked call to ram in bank 2 fram main memory.
asm{.byte $db}
printf("result = %u\n", mul_c(1)); // Banked call to ram in bank 2 fram main memory.
asm{.byte $db}
printf("result = %u\n", mul_d(1)); // Banked call to ram in bank 2 fram main memory.
asm{.byte $db}
printf("result = %u\n", mul_e(1)); // banked call to ram in bank 2 fram main memory.
asm{.byte $db}
printf("result = %u\n", mul_f(1)); // banked call to ram in bank 2 fram main memory.
asm{.byte $db}
printf("result = %u\n", add_m(1)); // Near call in main memory fram main memory.
asm{.byte $db}
printf("result = %u\n", mul_m(1)); // Near call in main memory fram main memory.
}

View File

@ -0,0 +1,14 @@
.file [name="%O", type="prg", segments="Program"]
.file [name="BANK1.BIN", type="bin", segments="Bank1"]
.file [name="BANK2.BIN", type="bin", segments="Bank2"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=%P]
.segmentdef Data [startAfter="Code"]
.segmentdef Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segment Basic
:BasicUpstart(%E)
.segment Code
.segment Data

View File

@ -0,0 +1,70 @@
/**
* @file call-banked-phi-case-1-near-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be)
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #1.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
.file [name="call-banked-phi-case-1-near-0.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
.label SCREEN = $400
.segment Code
main: {
// plus('0', 7)
lda #7
ldx #'0'
jsr plus
// plus('0', 7)
// SCREEN[0] = plus('0', 7)
sta SCREEN
// plus('1', 6)
lda #6
ldx #'1'
jsr plus
// plus('1', 6)
// SCREEN[1] = plus('1', 6)
// near call
sta SCREEN+1
// }
rts
}
// __register(A) char plus(__register(X) char a, __register(A) char b)
plus: {
// a+b
stx.z $ff
clc
adc.z $ff
// }
rts
}

View File

@ -0,0 +1,30 @@
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] plus::return#2 = plus::a#2 + plus::b#2
to:plus::@return
plus::@return: scope:[plus] from plus
[12] return
to:@return

View File

@ -0,0 +1,462 @@
Loading link script "call-banked-phi.ld"
CONTROL FLOW GRAPH SSA
void main()
main: scope:[main] from __start
plus::a#0 = '0'
plus::b#0 = 7
call plus
plus::return#0 = plus::return#3
to:main::@1
main::@1: scope:[main] from main
plus::return#4 = phi( main/plus::return#0 )
main::$0 = plus::return#4
SCREEN[0] = main::$0
plus::a#1 = '1'
plus::b#1 = 6
call plus
plus::return#1 = plus::return#3
to:main::@2
main::@2: scope:[main] from main::@1
plus::return#5 = phi( main::@1/plus::return#1 )
main::$1 = plus::return#5
SCREEN[1] = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
return
to:@return
char plus(char a , char b)
plus: scope:[plus] from main main::@1
plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
plus::$0 = plus::a#2 + plus::b#2
plus::return#2 = plus::$0
to:plus::@return
plus::@return: scope:[plus] from plus
plus::return#6 = phi( plus/plus::return#2 )
plus::return#3 = plus::return#6
return
to:@return
void __start()
__start: scope:[__start] from
call main
to:__start::@1
__start::@1: scope:[__start] from __start
to:__start::@return
__start::@return: scope:[__start] from __start::@1
return
to:@return
SYMBOL TABLE SSA
__constant char * const SCREEN = (char *)$400
void __start()
void main()
char main::$0
char main::$1
char plus(char a , char b)
char plus::$0
char plus::a
char plus::a#0
char plus::a#1
char plus::a#2
char plus::b
char plus::b#0
char plus::b#1
char plus::b#2
char plus::return
char plus::return#0
char plus::return#1
char plus::return#2
char plus::return#3
char plus::return#4
char plus::return#5
char plus::return#6
Adding number conversion cast (unumber) 7 in plus::b#0 = 7
Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
Adding number conversion cast (unumber) 6 in plus::b#1 = 6
Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast plus::b#0 = (unumber)7
Inlining cast plus::b#1 = (unumber)6
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (char *) 1024
Simplifying constant integer cast 7
Simplifying constant integer cast 0
Simplifying constant integer cast 6
Simplifying constant integer cast 1
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (char) 7
Finalized unsigned number type (char) 0
Finalized unsigned number type (char) 6
Finalized unsigned number type (char) 1
Successful SSA optimization PassNFinalizeNumberTypeConversions
Alias plus::return#0 = plus::return#4
Alias plus::return#1 = plus::return#5
Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
Successful SSA optimization Pass2AliasElimination
Constant plus::a#0 = '0'
Constant plus::b#0 = 7
Constant plus::a#1 = '1'
Constant plus::b#1 = 6
Successful SSA optimization Pass2ConstantIdentification
Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
Successful SSA optimization PassNSimplifyExpressionWithZero
Removing unused procedure __start
Removing unused procedure block __start
Removing unused procedure block __start::@1
Removing unused procedure block __start::@return
Successful SSA optimization PassNEliminateEmptyStart
Inlining constant with var siblings plus::a#0
Inlining constant with var siblings plus::b#0
Inlining constant with var siblings plus::a#1
Inlining constant with var siblings plus::b#1
Constant inlined plus::b#1 = 6
Constant inlined plus::b#0 = 7
Constant inlined plus::a#1 = '1'
Constant inlined plus::a#0 = '0'
Successful SSA optimization Pass2ConstantInlining
Consolidated array index constant in *(SCREEN+1)
Successful SSA optimization Pass2ConstantAdditionElimination
Adding NOP phi() at start of main
CALL GRAPH
Calls in [main] to plus:1 plus:5
Created 2 initial phi equivalence classes
Coalesced down to 2 phi equivalence classes
Adding NOP phi() at start of main
FINAL CONTROL FLOW GRAPH
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] plus::return#2 = plus::a#2 + plus::b#2
to:plus::@return
plus::@return: scope:[plus] from plus
[12] return
to:@return
VARIABLE REGISTER WEIGHTS
void main()
char main::$0 // 4.0
char main::$1 // 4.0
char plus(char a , char b)
char plus::a
char plus::a#2 // 11.0
char plus::b
char plus::b#2 // 11.0
char plus::return
char plus::return#0 // 4.0
char plus::return#1 // 4.0
char plus::return#2 // 3.75
Initial phi equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
Added variable main::$0 to live range equivalence class [ main::$0 ]
Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
Added variable main::$1 to live range equivalence class [ main::$1 ]
Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
Complete equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
[ plus::return#0 ]
[ main::$0 ]
[ plus::return#1 ]
[ main::$1 ]
[ plus::return#2 ]
Allocated zp[1]:2 [ plus::a#2 ]
Allocated zp[1]:3 [ plus::b#2 ]
Allocated zp[1]:4 [ plus::return#0 ]
Allocated zp[1]:5 [ main::$0 ]
Allocated zp[1]:6 [ plus::return#1 ]
Allocated zp[1]:7 [ main::$1 ]
Allocated zp[1]:8 [ plus::return#2 ]
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [11] plus::return#2 = plus::a#2 + plus::b#2 [ plus::return#2 ] ( plus:1 [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } plus:5 [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Potential registers zp[1]:2 [ plus::a#2 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:3 [ plus::b#2 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:4 [ plus::return#0 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:5 [ main::$0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:6 [ plus::return#1 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:7 [ main::$1 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:8 [ plus::return#2 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y ,
REGISTER UPLIFT SCOPES
Uplift Scope [plus] 11: zp[1]:2 [ plus::a#2 ] 11: zp[1]:3 [ plus::b#2 ] 4: zp[1]:4 [ plus::return#0 ] 4: zp[1]:6 [ plus::return#1 ] 3.75: zp[1]:8 [ plus::return#2 ]
Uplift Scope [main] 4: zp[1]:5 [ main::$0 ] 4: zp[1]:7 [ main::$1 ]
Uplift Scope []
Uplifting [plus] best 81 combination reg byte x [ plus::a#2 ] reg byte a [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:8 [ plus::return#2 ]
Limited combination testing to 100 combinations of 1024 possible.
Uplifting [main] best 69 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
Uplifting [] best 69 combination
Attempting to uplift remaining variables inzp[1]:8 [ plus::return#2 ]
Uplifting [plus] best 60 combination reg byte a [ plus::return#2 ]
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
/**
* @file call-banked-phi-case-1-near-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be)
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #1.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-1-near-0.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
plus_from_main:
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuaa=vbuc1
lda #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- vbuxx=vbuc1
ldx #'0'
jsr plus
// [2] plus::return#0 = plus::return#2
jmp __b1
// main::@1
__b1:
// [3] main::$0 = plus::return#0
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
plus_from___b1:
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuaa=vbuc1
lda #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- vbuxx=vbuc1
ldx #'1'
jsr plus
// [6] plus::return#1 = plus::return#2
jmp __b2
// main::@2
__b2:
// [7] main::$1 = plus::return#1
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// near call
sta SCREEN+1
jmp __breturn
// main::@return
__breturn:
// [9] return
rts
}
// plus
// __register(A) char plus(__register(X) char a, __register(A) char b)
plus: {
// [11] plus::return#2 = plus::a#2 + plus::b#2 -- vbuaa=vbuxx_plus_vbuaa
stx.z $ff
clc
adc.z $ff
jmp __breturn
// plus::@return
__breturn:
// [12] return
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __b1
Removing instruction jmp __b2
Removing instruction jmp __breturn
Removing instruction jmp __breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction plus_from_main:
Removing instruction __b1:
Removing instruction plus_from___b1:
Removing instruction __b2:
Removing instruction __breturn:
Removing instruction __breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
__constant char * const SCREEN = (char *) 1024
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte x 11.0
char plus::b
char plus::b#2 // reg byte a 11.0
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte a 3.75
reg byte x [ plus::a#2 ]
reg byte a [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
reg byte a [ plus::return#2 ]
FINAL ASSEMBLER
Score: 48
// File Comments
/**
* @file call-banked-phi-case-1-near-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be)
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #1.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-1-near-0.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// plus('0', 7)
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuaa=vbuc1
lda #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- vbuxx=vbuc1
ldx #'0'
jsr plus
// plus('0', 7)
// [2] plus::return#0 = plus::return#2
// main::@1
// [3] main::$0 = plus::return#0
// SCREEN[0] = plus('0', 7)
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// plus('1', 6)
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuaa=vbuc1
lda #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- vbuxx=vbuc1
ldx #'1'
jsr plus
// plus('1', 6)
// [6] plus::return#1 = plus::return#2
// main::@2
// [7] main::$1 = plus::return#1
// SCREEN[1] = plus('1', 6)
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// near call
sta SCREEN+1
// main::@return
// }
// [9] return
rts
}
// plus
// __register(A) char plus(__register(X) char a, __register(A) char b)
plus: {
// a+b
// [11] plus::return#2 = plus::a#2 + plus::b#2 -- vbuaa=vbuxx_plus_vbuaa
stx.z $ff
clc
adc.z $ff
// plus::@return
// }
// [12] return
rts
}
// File Data

View File

@ -0,0 +1,21 @@
__constant char * const SCREEN = (char *) 1024
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte x 11.0
char plus::b
char plus::b#2 // reg byte a 11.0
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte a 3.75
reg byte x [ plus::a#2 ]
reg byte a [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
reg byte a [ plus::return#2 ]

View File

@ -0,0 +1,78 @@
/**
* @file call-banked-phi-case-1-near-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #1
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
.file [name="call-banked-phi-case-1-near-1.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
.label SCREEN = $400
.segment Code
main: {
// plus('0', 7)
lda #7
ldx #'0'
jsr plus
// plus('0', 7)
// SCREEN[0] = plus('0', 7)
sta SCREEN
// plus('1', 6)
lda #6
ldx #'1'
jsr plus
// plus('1', 6)
// SCREEN[1] = plus('1', 6)
// near call
sta SCREEN+1
// }
rts
}
// __register(A) char plus(__register(X) char a, __register(A) char b)
plus: {
// add(a, b)
stx.z add.a
jsr add
// }
rts
}
// __register(A) char add(__zp(2) char a, __register(A) char b)
add: {
.label a = 2
// a+b
clc
adc.z a
// }
rts
}

View File

@ -0,0 +1,44 @@
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] add::a#0 = plus::a#2
[12] add::b#0 = plus::b#2
[13] call add
[14] add::return#0 = add::return#1
to:plus::@1
plus::@1: scope:[plus] from plus
[15] plus::return#2 = add::return#0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
[16] return
to:@return
char add(char a , char b)
add: scope:[add] from plus
[17] add::return#1 = add::a#0 + add::b#0
to:add::@return
add::@return: scope:[add] from add
[18] return
to:@return

View File

@ -0,0 +1,599 @@
Loading link script "call-banked-phi.ld"
CONTROL FLOW GRAPH SSA
void main()
main: scope:[main] from __start
plus::a#0 = '0'
plus::b#0 = 7
call plus
plus::return#0 = plus::return#3
to:main::@1
main::@1: scope:[main] from main
plus::return#4 = phi( main/plus::return#0 )
main::$0 = plus::return#4
SCREEN[0] = main::$0
plus::a#1 = '1'
plus::b#1 = 6
call plus
plus::return#1 = plus::return#3
to:main::@2
main::@2: scope:[main] from main::@1
plus::return#5 = phi( main::@1/plus::return#1 )
main::$1 = plus::return#5
SCREEN[1] = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
return
to:@return
char plus(char a , char b)
plus: scope:[plus] from main main::@1
plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
add::a#0 = plus::a#2
add::b#0 = plus::b#2
call add
add::return#0 = add::return#2
to:plus::@1
plus::@1: scope:[plus] from plus
add::return#3 = phi( plus/add::return#0 )
plus::$0 = add::return#3
plus::return#2 = plus::$0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
plus::return#6 = phi( plus::@1/plus::return#2 )
plus::return#3 = plus::return#6
return
to:@return
char add(char a , char b)
add: scope:[add] from plus
add::b#1 = phi( plus/add::b#0 )
add::a#1 = phi( plus/add::a#0 )
add::$0 = add::a#1 + add::b#1
add::return#1 = add::$0
to:add::@return
add::@return: scope:[add] from add
add::return#4 = phi( add/add::return#1 )
add::return#2 = add::return#4
return
to:@return
void __start()
__start: scope:[__start] from
call main
to:__start::@1
__start::@1: scope:[__start] from __start
to:__start::@return
__start::@return: scope:[__start] from __start::@1
return
to:@return
SYMBOL TABLE SSA
__constant char * const SCREEN = (char *)$400
void __start()
char add(char a , char b)
char add::$0
char add::a
char add::a#0
char add::a#1
char add::b
char add::b#0
char add::b#1
char add::return
char add::return#0
char add::return#1
char add::return#2
char add::return#3
char add::return#4
void main()
char main::$0
char main::$1
char plus(char a , char b)
char plus::$0
char plus::a
char plus::a#0
char plus::a#1
char plus::a#2
char plus::b
char plus::b#0
char plus::b#1
char plus::b#2
char plus::return
char plus::return#0
char plus::return#1
char plus::return#2
char plus::return#3
char plus::return#4
char plus::return#5
char plus::return#6
Adding number conversion cast (unumber) 7 in plus::b#0 = 7
Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
Adding number conversion cast (unumber) 6 in plus::b#1 = 6
Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast plus::b#0 = (unumber)7
Inlining cast plus::b#1 = (unumber)6
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (char *) 1024
Simplifying constant integer cast 7
Simplifying constant integer cast 0
Simplifying constant integer cast 6
Simplifying constant integer cast 1
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (char) 7
Finalized unsigned number type (char) 0
Finalized unsigned number type (char) 6
Finalized unsigned number type (char) 1
Successful SSA optimization PassNFinalizeNumberTypeConversions
Alias plus::return#0 = plus::return#4
Alias plus::return#1 = plus::return#5
Alias add::return#0 = add::return#3
Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
Alias add::return#1 = add::$0 add::return#4 add::return#2
Successful SSA optimization Pass2AliasElimination
Identical Phi Values add::a#1 add::a#0
Identical Phi Values add::b#1 add::b#0
Successful SSA optimization Pass2IdenticalPhiElimination
Constant plus::a#0 = '0'
Constant plus::b#0 = 7
Constant plus::a#1 = '1'
Constant plus::b#1 = 6
Successful SSA optimization Pass2ConstantIdentification
Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
Successful SSA optimization PassNSimplifyExpressionWithZero
Removing unused procedure __start
Removing unused procedure block __start
Removing unused procedure block __start::@1
Removing unused procedure block __start::@return
Successful SSA optimization PassNEliminateEmptyStart
Inlining constant with var siblings plus::a#0
Inlining constant with var siblings plus::b#0
Inlining constant with var siblings plus::a#1
Inlining constant with var siblings plus::b#1
Constant inlined plus::b#1 = 6
Constant inlined plus::b#0 = 7
Constant inlined plus::a#1 = '1'
Constant inlined plus::a#0 = '0'
Successful SSA optimization Pass2ConstantInlining
Consolidated array index constant in *(SCREEN+1)
Successful SSA optimization Pass2ConstantAdditionElimination
Adding NOP phi() at start of main
CALL GRAPH
Calls in [main] to plus:1 plus:5
Calls in [plus] to add:13
Created 2 initial phi equivalence classes
Coalesced down to 2 phi equivalence classes
Adding NOP phi() at start of main
FINAL CONTROL FLOW GRAPH
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] add::a#0 = plus::a#2
[12] add::b#0 = plus::b#2
[13] call add
[14] add::return#0 = add::return#1
to:plus::@1
plus::@1: scope:[plus] from plus
[15] plus::return#2 = add::return#0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
[16] return
to:@return
char add(char a , char b)
add: scope:[add] from plus
[17] add::return#1 = add::a#0 + add::b#0
to:add::@return
add::@return: scope:[add] from add
[18] return
to:@return
VARIABLE REGISTER WEIGHTS
char add(char a , char b)
char add::a
char add::a#0 // 56.0
char add::b
char add::b#0 // 112.0
char add::return
char add::return#0 // 22.0
char add::return#1 // 37.33333333333333
void main()
char main::$0 // 4.0
char main::$1 // 4.0
char plus(char a , char b)
char plus::a
char plus::a#2 // 11.0
char plus::b
char plus::b#2 // 5.5
char plus::return
char plus::return#0 // 4.0
char plus::return#1 // 4.0
char plus::return#2 // 3.75
Initial phi equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
Added variable main::$0 to live range equivalence class [ main::$0 ]
Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
Added variable main::$1 to live range equivalence class [ main::$1 ]
Added variable add::a#0 to live range equivalence class [ add::a#0 ]
Added variable add::b#0 to live range equivalence class [ add::b#0 ]
Added variable add::return#0 to live range equivalence class [ add::return#0 ]
Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
Added variable add::return#1 to live range equivalence class [ add::return#1 ]
Complete equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
[ plus::return#0 ]
[ main::$0 ]
[ plus::return#1 ]
[ main::$1 ]
[ add::a#0 ]
[ add::b#0 ]
[ add::return#0 ]
[ plus::return#2 ]
[ add::return#1 ]
Allocated zp[1]:2 [ add::b#0 ]
Allocated zp[1]:3 [ add::a#0 ]
Allocated zp[1]:4 [ add::return#1 ]
Allocated zp[1]:5 [ add::return#0 ]
Allocated zp[1]:6 [ plus::a#2 ]
Allocated zp[1]:7 [ plus::b#2 ]
Allocated zp[1]:8 [ plus::return#0 ]
Allocated zp[1]:9 [ main::$0 ]
Allocated zp[1]:10 [ plus::return#1 ]
Allocated zp[1]:11 [ main::$1 ]
Allocated zp[1]:12 [ plus::return#2 ]
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y ,
REGISTER UPLIFT SCOPES
Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ]
Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ]
Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ]
Uplift Scope []
Uplifting [add] best 129 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte a [ add::return#1 ] reg byte a [ add::return#0 ]
Limited combination testing to 100 combinations of 256 possible.
Uplifting [plus] best 99 combination reg byte x [ plus::a#2 ] reg byte a [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ]
Limited combination testing to 100 combinations of 1024 possible.
Uplifting [main] best 87 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
Uplifting [] best 87 combination
Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ]
Uplifting [add] best 87 combination zp[1]:3 [ add::a#0 ]
Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ]
Uplifting [plus] best 78 combination reg byte a [ plus::return#2 ]
Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ]
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
/**
* @file call-banked-phi-case-1-near-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #1
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-1-near-1.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
plus_from_main:
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuaa=vbuc1
lda #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- vbuxx=vbuc1
ldx #'0'
jsr plus
// [2] plus::return#0 = plus::return#2
jmp __b1
// main::@1
__b1:
// [3] main::$0 = plus::return#0
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
plus_from___b1:
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuaa=vbuc1
lda #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- vbuxx=vbuc1
ldx #'1'
jsr plus
// [6] plus::return#1 = plus::return#2
jmp __b2
// main::@2
__b2:
// [7] main::$1 = plus::return#1
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// near call
sta SCREEN+1
jmp __breturn
// main::@return
__breturn:
// [9] return
rts
}
// plus
// __register(A) char plus(__register(X) char a, __register(A) char b)
plus: {
// [11] add::a#0 = plus::a#2 -- vbuz1=vbuxx
stx.z add.a
// [12] add::b#0 = plus::b#2
// [13] call add
jsr add
// [14] add::return#0 = add::return#1
jmp __b1
// plus::@1
__b1:
// [15] plus::return#2 = add::return#0
jmp __breturn
// plus::@return
__breturn:
// [16] return
rts
}
// add
// __register(A) char add(__zp(2) char a, __register(A) char b)
add: {
.label a = 2
// [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
clc
adc.z a
jmp __breturn
// add::@return
__breturn:
// [18] return
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __b1
Removing instruction jmp __b2
Removing instruction jmp __breturn
Removing instruction jmp __b1
Removing instruction jmp __breturn
Removing instruction jmp __breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction __breturn:
Succesful ASM optimization Pass5RedundantLabelElimination
Removing instruction plus_from_main:
Removing instruction __b1:
Removing instruction plus_from___b1:
Removing instruction __b2:
Removing instruction __breturn:
Removing instruction __b1:
Removing instruction __breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
__constant char * const SCREEN = (char *) 1024
char add(char a , char b)
char add::a
char add::a#0 // a zp[1]:2 56.0
char add::b
char add::b#0 // reg byte a 112.0
char add::return
char add::return#0 // reg byte a 22.0
char add::return#1 // reg byte a 37.33333333333333
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte x 11.0
char plus::b
char plus::b#2 // reg byte a 5.5
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte a 3.75
reg byte x [ plus::a#2 ]
reg byte a [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
zp[1]:2 [ add::a#0 ]
reg byte a [ add::b#0 ]
reg byte a [ add::return#0 ]
reg byte a [ plus::return#2 ]
reg byte a [ add::return#1 ]
FINAL ASSEMBLER
Score: 60
// File Comments
/**
* @file call-banked-phi-case-1-near-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #1
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-1-near-1.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// plus('0', 7)
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuaa=vbuc1
lda #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- vbuxx=vbuc1
ldx #'0'
jsr plus
// plus('0', 7)
// [2] plus::return#0 = plus::return#2
// main::@1
// [3] main::$0 = plus::return#0
// SCREEN[0] = plus('0', 7)
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// plus('1', 6)
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuaa=vbuc1
lda #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- vbuxx=vbuc1
ldx #'1'
jsr plus
// plus('1', 6)
// [6] plus::return#1 = plus::return#2
// main::@2
// [7] main::$1 = plus::return#1
// SCREEN[1] = plus('1', 6)
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// near call
sta SCREEN+1
// main::@return
// }
// [9] return
rts
}
// plus
// __register(A) char plus(__register(X) char a, __register(A) char b)
plus: {
// add(a, b)
// [11] add::a#0 = plus::a#2 -- vbuz1=vbuxx
stx.z add.a
// [12] add::b#0 = plus::b#2
// [13] call add
jsr add
// [14] add::return#0 = add::return#1
// plus::@1
// [15] plus::return#2 = add::return#0
// plus::@return
// }
// [16] return
rts
}
// add
// __register(A) char add(__zp(2) char a, __register(A) char b)
add: {
.label a = 2
// a+b
// [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
clc
adc.z a
// add::@return
// }
// [18] return
rts
}
// File Data

View File

@ -0,0 +1,33 @@
__constant char * const SCREEN = (char *) 1024
char add(char a , char b)
char add::a
char add::a#0 // a zp[1]:2 56.0
char add::b
char add::b#0 // reg byte a 112.0
char add::return
char add::return#0 // reg byte a 22.0
char add::return#1 // reg byte a 37.33333333333333
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte x 11.0
char plus::b
char plus::b#2 // reg byte a 5.5
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte a 3.75
reg byte x [ plus::a#2 ]
reg byte a [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
zp[1]:2 [ add::a#0 ]
reg byte a [ add::b#0 ]
reg byte a [ add::return#0 ]
reg byte a [ plus::return#2 ]
reg byte a [ add::return#1 ]

View File

@ -0,0 +1,96 @@
/**
* @file call-banked-phi-case-2-close-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #2.
* Implementation using the __bank() directive.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
.file [name="call-banked-phi-case-2-close-0.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
.label SCREEN = $400
.segment Code
main: {
// plus('0', 7)
ldx #7
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('0', 7)
txa
// SCREEN[0] = plus('0', 7)
sta SCREEN
// plus('1', 6)
ldx #6
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('1', 6)
txa
// SCREEN[1] = plus('1', 6)
// close call
sta SCREEN+1
// }
rts
}
.segment RAM_Bank1
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// a+b
stx.z $ff
clc
adc.z $ff
tax
// }
rts
}

View File

@ -0,0 +1,30 @@
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] plus::return#2 = plus::a#2 + plus::b#2
to:plus::@return
plus::@return: scope:[plus] from plus
[12] return
to:@return

View File

@ -0,0 +1,520 @@
Loading link script "call-banked-phi.ld"
CONTROL FLOW GRAPH SSA
void main()
main: scope:[main] from __start
plus::a#0 = '0'
plus::b#0 = 7
call plus
plus::return#0 = plus::return#3
to:main::@1
main::@1: scope:[main] from main
plus::return#4 = phi( main/plus::return#0 )
main::$0 = plus::return#4
SCREEN[0] = main::$0
plus::a#1 = '1'
plus::b#1 = 6
call plus
plus::return#1 = plus::return#3
to:main::@2
main::@2: scope:[main] from main::@1
plus::return#5 = phi( main::@1/plus::return#1 )
main::$1 = plus::return#5
SCREEN[1] = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
plus::$0 = plus::a#2 + plus::b#2
plus::return#2 = plus::$0
to:plus::@return
plus::@return: scope:[plus] from plus
plus::return#6 = phi( plus/plus::return#2 )
plus::return#3 = plus::return#6
return
to:@return
void __start()
__start: scope:[__start] from
call main
to:__start::@1
__start::@1: scope:[__start] from __start
to:__start::@return
__start::@return: scope:[__start] from __start::@1
return
to:@return
SYMBOL TABLE SSA
__constant char * const SCREEN = (char *)$400
void __start()
void main()
char main::$0
char main::$1
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::$0
char plus::a
char plus::a#0
char plus::a#1
char plus::a#2
char plus::b
char plus::b#0
char plus::b#1
char plus::b#2
char plus::return
char plus::return#0
char plus::return#1
char plus::return#2
char plus::return#3
char plus::return#4
char plus::return#5
char plus::return#6
Adding number conversion cast (unumber) 7 in plus::b#0 = 7
Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
Adding number conversion cast (unumber) 6 in plus::b#1 = 6
Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast plus::b#0 = (unumber)7
Inlining cast plus::b#1 = (unumber)6
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (char *) 1024
Simplifying constant integer cast 7
Simplifying constant integer cast 0
Simplifying constant integer cast 6
Simplifying constant integer cast 1
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (char) 7
Finalized unsigned number type (char) 0
Finalized unsigned number type (char) 6
Finalized unsigned number type (char) 1
Successful SSA optimization PassNFinalizeNumberTypeConversions
Alias plus::return#0 = plus::return#4
Alias plus::return#1 = plus::return#5
Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
Successful SSA optimization Pass2AliasElimination
Constant plus::a#0 = '0'
Constant plus::b#0 = 7
Constant plus::a#1 = '1'
Constant plus::b#1 = 6
Successful SSA optimization Pass2ConstantIdentification
Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
Successful SSA optimization PassNSimplifyExpressionWithZero
Removing unused procedure __start
Removing unused procedure block __start
Removing unused procedure block __start::@1
Removing unused procedure block __start::@return
Successful SSA optimization PassNEliminateEmptyStart
Inlining constant with var siblings plus::a#0
Inlining constant with var siblings plus::b#0
Inlining constant with var siblings plus::a#1
Inlining constant with var siblings plus::b#1
Constant inlined plus::b#1 = 6
Constant inlined plus::b#0 = 7
Constant inlined plus::a#1 = '1'
Constant inlined plus::a#0 = '0'
Successful SSA optimization Pass2ConstantInlining
Consolidated array index constant in *(SCREEN+1)
Successful SSA optimization Pass2ConstantAdditionElimination
Adding NOP phi() at start of main
CALL GRAPH
Calls in [main] to plus:1 plus:5
Created 2 initial phi equivalence classes
Coalesced down to 2 phi equivalence classes
Adding NOP phi() at start of main
FINAL CONTROL FLOW GRAPH
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] plus::return#2 = plus::a#2 + plus::b#2
to:plus::@return
plus::@return: scope:[plus] from plus
[12] return
to:@return
VARIABLE REGISTER WEIGHTS
void main()
char main::$0 // 4.0
char main::$1 // 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // 11.0
char plus::b
char plus::b#2 // 11.0
char plus::return
char plus::return#0 // 4.0
char plus::return#1 // 4.0
char plus::return#2 // 3.75
Initial phi equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
Added variable main::$0 to live range equivalence class [ main::$0 ]
Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
Added variable main::$1 to live range equivalence class [ main::$1 ]
Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
Complete equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
[ plus::return#0 ]
[ main::$0 ]
[ plus::return#1 ]
[ main::$1 ]
[ plus::return#2 ]
Allocated zp[1]:2 [ plus::a#2 ]
Allocated zp[1]:3 [ plus::b#2 ]
Allocated zp[1]:4 [ plus::return#0 ]
Allocated zp[1]:5 [ main::$0 ]
Allocated zp[1]:6 [ plus::return#1 ]
Allocated zp[1]:7 [ main::$1 ]
Allocated zp[1]:8 [ plus::return#2 ]
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Removing always clobbered register reg byte a as potential for zp[1]:8 [ plus::return#2 ]
Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [11] plus::return#2 = plus::a#2 + plus::b#2 [ plus::return#2 ] ( plus:1 [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } plus:5 [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [11] plus::return#2 = plus::a#2 + plus::b#2 [ plus::return#2 ] ( plus:1 [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } plus:5 [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Potential registers zp[1]:2 [ plus::a#2 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:3 [ plus::b#2 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:4 [ plus::return#0 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:5 [ main::$0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:6 [ plus::return#1 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:7 [ main::$1 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:8 [ plus::return#2 ] : zp[1]:8 , reg byte x , reg byte y ,
REGISTER UPLIFT SCOPES
Uplift Scope [plus] 11: zp[1]:2 [ plus::a#2 ] 11: zp[1]:3 [ plus::b#2 ] 4: zp[1]:4 [ plus::return#0 ] 4: zp[1]:6 [ plus::return#1 ] 3.75: zp[1]:8 [ plus::return#2 ]
Uplift Scope [main] 4: zp[1]:5 [ main::$0 ] 4: zp[1]:7 [ main::$1 ]
Uplift Scope []
Uplifting [plus] best 141 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:8 [ plus::return#2 ]
Limited combination testing to 100 combinations of 768 possible.
Uplifting [main] best 129 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
Uplifting [] best 129 combination
Attempting to uplift remaining variables inzp[1]:8 [ plus::return#2 ]
Uplifting [plus] best 126 combination reg byte x [ plus::return#2 ]
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
/**
* @file call-banked-phi-case-2-close-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #2.
* Implementation using the __bank() directive.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-2-close-0.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
plus_from_main:
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
ldx #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
txa
jmp __b1
// main::@1
__b1:
// [3] main::$0 = plus::return#0
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
plus_from___b1:
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
ldx #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
txa
jmp __b2
// main::@2
__b2:
// [7] main::$1 = plus::return#1
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// close call
sta SCREEN+1
jmp __breturn
// main::@return
__breturn:
// [9] return
rts
}
.segment RAM_Bank1
// plus
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// [11] plus::return#2 = plus::a#2 + plus::b#2 -- vbuxx=vbuaa_plus_vbuxx
stx.z $ff
clc
adc.z $ff
tax
jmp __breturn
// plus::@return
__breturn:
// [12] return
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __b1
Removing instruction jmp __b2
Removing instruction jmp __breturn
Removing instruction jmp __breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction plus_from_main:
Removing instruction __b1:
Removing instruction plus_from___b1:
Removing instruction __b2:
Removing instruction __breturn:
Removing instruction __breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
__constant char * const SCREEN = (char *) 1024
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte a 11.0
char plus::b
char plus::b#2 // reg byte x 11.0
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte x 3.75
reg byte a [ plus::a#2 ]
reg byte x [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
reg byte x [ plus::return#2 ]
FINAL ASSEMBLER
Score: 114
// File Comments
/**
* @file call-banked-phi-case-2-close-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #2.
* Implementation using the __bank() directive.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-2-close-0.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// plus('0', 7)
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
ldx #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('0', 7)
// [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
txa
// main::@1
// [3] main::$0 = plus::return#0
// SCREEN[0] = plus('0', 7)
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// plus('1', 6)
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
ldx #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('1', 6)
// [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
txa
// main::@2
// [7] main::$1 = plus::return#1
// SCREEN[1] = plus('1', 6)
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// close call
sta SCREEN+1
// main::@return
// }
// [9] return
rts
}
.segment RAM_Bank1
// plus
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// a+b
// [11] plus::return#2 = plus::a#2 + plus::b#2 -- vbuxx=vbuaa_plus_vbuxx
stx.z $ff
clc
adc.z $ff
tax
// plus::@return
// }
// [12] return
rts
}
// File Data

View File

@ -0,0 +1,21 @@
__constant char * const SCREEN = (char *) 1024
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte a 11.0
char plus::b
char plus::b#2 // reg byte x 11.0
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte x 3.75
reg byte a [ plus::a#2 ]
reg byte x [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
reg byte x [ plus::return#2 ]

View File

@ -0,0 +1,96 @@
/**
* @file call-banked-phi-case-2-close-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #2.
* Implementation using the #pragma bank and nobank directives.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
.file [name="call-banked-phi-case-2-close-1.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
.label SCREEN = $400
.segment Code
main: {
// plus('0', 7)
ldx #7
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('0', 7)
txa
// SCREEN[0] = plus('0', 7)
sta SCREEN
// plus('1', 6)
ldx #6
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('1', 6)
txa
// SCREEN[1] = plus('1', 6)
// close call
sta SCREEN+1
// }
rts
}
.segment RAM_Bank1
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// a+b
stx.z $ff
clc
adc.z $ff
tax
// }
rts
}

View File

@ -0,0 +1,30 @@
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] plus::return#2 = plus::a#2 + plus::b#2
to:plus::@return
plus::@return: scope:[plus] from plus
[12] return
to:@return

View File

@ -0,0 +1,520 @@
Loading link script "call-banked-phi.ld"
CONTROL FLOW GRAPH SSA
void main()
main: scope:[main] from __start
plus::a#0 = '0'
plus::b#0 = 7
call plus
plus::return#0 = plus::return#3
to:main::@1
main::@1: scope:[main] from main
plus::return#4 = phi( main/plus::return#0 )
main::$0 = plus::return#4
SCREEN[0] = main::$0
plus::a#1 = '1'
plus::b#1 = 6
call plus
plus::return#1 = plus::return#3
to:main::@2
main::@2: scope:[main] from main::@1
plus::return#5 = phi( main::@1/plus::return#1 )
main::$1 = plus::return#5
SCREEN[1] = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
plus::$0 = plus::a#2 + plus::b#2
plus::return#2 = plus::$0
to:plus::@return
plus::@return: scope:[plus] from plus
plus::return#6 = phi( plus/plus::return#2 )
plus::return#3 = plus::return#6
return
to:@return
void __start()
__start: scope:[__start] from
call main
to:__start::@1
__start::@1: scope:[__start] from __start
to:__start::@return
__start::@return: scope:[__start] from __start::@1
return
to:@return
SYMBOL TABLE SSA
__constant char * const SCREEN = (char *)$400
void __start()
void main()
char main::$0
char main::$1
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::$0
char plus::a
char plus::a#0
char plus::a#1
char plus::a#2
char plus::b
char plus::b#0
char plus::b#1
char plus::b#2
char plus::return
char plus::return#0
char plus::return#1
char plus::return#2
char plus::return#3
char plus::return#4
char plus::return#5
char plus::return#6
Adding number conversion cast (unumber) 7 in plus::b#0 = 7
Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
Adding number conversion cast (unumber) 6 in plus::b#1 = 6
Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast plus::b#0 = (unumber)7
Inlining cast plus::b#1 = (unumber)6
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (char *) 1024
Simplifying constant integer cast 7
Simplifying constant integer cast 0
Simplifying constant integer cast 6
Simplifying constant integer cast 1
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (char) 7
Finalized unsigned number type (char) 0
Finalized unsigned number type (char) 6
Finalized unsigned number type (char) 1
Successful SSA optimization PassNFinalizeNumberTypeConversions
Alias plus::return#0 = plus::return#4
Alias plus::return#1 = plus::return#5
Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
Successful SSA optimization Pass2AliasElimination
Constant plus::a#0 = '0'
Constant plus::b#0 = 7
Constant plus::a#1 = '1'
Constant plus::b#1 = 6
Successful SSA optimization Pass2ConstantIdentification
Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
Successful SSA optimization PassNSimplifyExpressionWithZero
Removing unused procedure __start
Removing unused procedure block __start
Removing unused procedure block __start::@1
Removing unused procedure block __start::@return
Successful SSA optimization PassNEliminateEmptyStart
Inlining constant with var siblings plus::a#0
Inlining constant with var siblings plus::b#0
Inlining constant with var siblings plus::a#1
Inlining constant with var siblings plus::b#1
Constant inlined plus::b#1 = 6
Constant inlined plus::b#0 = 7
Constant inlined plus::a#1 = '1'
Constant inlined plus::a#0 = '0'
Successful SSA optimization Pass2ConstantInlining
Consolidated array index constant in *(SCREEN+1)
Successful SSA optimization Pass2ConstantAdditionElimination
Adding NOP phi() at start of main
CALL GRAPH
Calls in [main] to plus:1 plus:5
Created 2 initial phi equivalence classes
Coalesced down to 2 phi equivalence classes
Adding NOP phi() at start of main
FINAL CONTROL FLOW GRAPH
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] plus::return#2 = plus::a#2 + plus::b#2
to:plus::@return
plus::@return: scope:[plus] from plus
[12] return
to:@return
VARIABLE REGISTER WEIGHTS
void main()
char main::$0 // 4.0
char main::$1 // 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // 11.0
char plus::b
char plus::b#2 // 11.0
char plus::return
char plus::return#0 // 4.0
char plus::return#1 // 4.0
char plus::return#2 // 3.75
Initial phi equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
Added variable main::$0 to live range equivalence class [ main::$0 ]
Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
Added variable main::$1 to live range equivalence class [ main::$1 ]
Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
Complete equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
[ plus::return#0 ]
[ main::$0 ]
[ plus::return#1 ]
[ main::$1 ]
[ plus::return#2 ]
Allocated zp[1]:2 [ plus::a#2 ]
Allocated zp[1]:3 [ plus::b#2 ]
Allocated zp[1]:4 [ plus::return#0 ]
Allocated zp[1]:5 [ main::$0 ]
Allocated zp[1]:6 [ plus::return#1 ]
Allocated zp[1]:7 [ main::$1 ]
Allocated zp[1]:8 [ plus::return#2 ]
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Removing always clobbered register reg byte a as potential for zp[1]:8 [ plus::return#2 ]
Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [11] plus::return#2 = plus::a#2 + plus::b#2 [ plus::return#2 ] ( plus:1 [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } plus:5 [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [11] plus::return#2 = plus::a#2 + plus::b#2 [ plus::return#2 ] ( plus:1 [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } plus:5 [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Potential registers zp[1]:2 [ plus::a#2 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:3 [ plus::b#2 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:4 [ plus::return#0 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:5 [ main::$0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:6 [ plus::return#1 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:7 [ main::$1 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:8 [ plus::return#2 ] : zp[1]:8 , reg byte x , reg byte y ,
REGISTER UPLIFT SCOPES
Uplift Scope [plus] 11: zp[1]:2 [ plus::a#2 ] 11: zp[1]:3 [ plus::b#2 ] 4: zp[1]:4 [ plus::return#0 ] 4: zp[1]:6 [ plus::return#1 ] 3.75: zp[1]:8 [ plus::return#2 ]
Uplift Scope [main] 4: zp[1]:5 [ main::$0 ] 4: zp[1]:7 [ main::$1 ]
Uplift Scope []
Uplifting [plus] best 141 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:8 [ plus::return#2 ]
Limited combination testing to 100 combinations of 768 possible.
Uplifting [main] best 129 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
Uplifting [] best 129 combination
Attempting to uplift remaining variables inzp[1]:8 [ plus::return#2 ]
Uplifting [plus] best 126 combination reg byte x [ plus::return#2 ]
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
/**
* @file call-banked-phi-case-2-close-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #2.
* Implementation using the #pragma bank and nobank directives.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-2-close-1.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
plus_from_main:
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
ldx #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
txa
jmp __b1
// main::@1
__b1:
// [3] main::$0 = plus::return#0
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
plus_from___b1:
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
ldx #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
txa
jmp __b2
// main::@2
__b2:
// [7] main::$1 = plus::return#1
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// close call
sta SCREEN+1
jmp __breturn
// main::@return
__breturn:
// [9] return
rts
}
.segment RAM_Bank1
// plus
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// [11] plus::return#2 = plus::a#2 + plus::b#2 -- vbuxx=vbuaa_plus_vbuxx
stx.z $ff
clc
adc.z $ff
tax
jmp __breturn
// plus::@return
__breturn:
// [12] return
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __b1
Removing instruction jmp __b2
Removing instruction jmp __breturn
Removing instruction jmp __breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction plus_from_main:
Removing instruction __b1:
Removing instruction plus_from___b1:
Removing instruction __b2:
Removing instruction __breturn:
Removing instruction __breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
__constant char * const SCREEN = (char *) 1024
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte a 11.0
char plus::b
char plus::b#2 // reg byte x 11.0
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte x 3.75
reg byte a [ plus::a#2 ]
reg byte x [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
reg byte x [ plus::return#2 ]
FINAL ASSEMBLER
Score: 114
// File Comments
/**
* @file call-banked-phi-case-2-close-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #2.
* Implementation using the #pragma bank and nobank directives.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-2-close-1.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// plus('0', 7)
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
ldx #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('0', 7)
// [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
txa
// main::@1
// [3] main::$0 = plus::return#0
// SCREEN[0] = plus('0', 7)
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// plus('1', 6)
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
ldx #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('1', 6)
// [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
txa
// main::@2
// [7] main::$1 = plus::return#1
// SCREEN[1] = plus('1', 6)
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// close call
sta SCREEN+1
// main::@return
// }
// [9] return
rts
}
.segment RAM_Bank1
// plus
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// a+b
// [11] plus::return#2 = plus::a#2 + plus::b#2 -- vbuxx=vbuaa_plus_vbuxx
stx.z $ff
clc
adc.z $ff
tax
// plus::@return
// }
// [12] return
rts
}
// File Data

View File

@ -0,0 +1,21 @@
__constant char * const SCREEN = (char *) 1024
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte a 11.0
char plus::b
char plus::b#2 // reg byte x 11.0
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte x 3.75
reg byte a [ plus::a#2 ]
reg byte x [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
reg byte x [ plus::return#2 ]

View File

@ -0,0 +1,106 @@
/**
* @file call-banked-phi-case-3-near-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #3.
* Implementation using the __bank() directive.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
.file [name="call-banked-phi-case-3-near-0.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
.label SCREEN = $400
.segment Code
main: {
// plus('0', 7)
ldx #7
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('0', 7)
txa
// SCREEN[0] = plus('0', 7)
sta SCREEN
// plus('1', 6)
ldx #6
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('1', 6)
txa
// SCREEN[1] = plus('1', 6)
// close call
sta SCREEN+1
// }
rts
}
.segment RAM_Bank1
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// add(a, b)
sta.z add.a
txa
jsr add
tax
// }
rts
}
.segment Code
// __register(A) char add(__zp(2) char a, __register(A) char b)
add: {
.label a = 2
// a+b
clc
adc.z a
// }
rts
}

View File

@ -0,0 +1,44 @@
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] add::a#0 = plus::a#2
[12] add::b#0 = plus::b#2
[13] call add
[14] add::return#0 = add::return#1
to:plus::@1
plus::@1: scope:[plus] from plus
[15] plus::return#2 = add::return#0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
[16] return
to:@return
char add(char a , char b)
add: scope:[add] from plus
[17] add::return#1 = add::a#0 + add::b#0
to:add::@return
add::@return: scope:[add] from add
[18] return
to:@return

View File

@ -0,0 +1,660 @@
Loading link script "call-banked-phi.ld"
CONTROL FLOW GRAPH SSA
void main()
main: scope:[main] from __start
plus::a#0 = '0'
plus::b#0 = 7
call plus
plus::return#0 = plus::return#3
to:main::@1
main::@1: scope:[main] from main
plus::return#4 = phi( main/plus::return#0 )
main::$0 = plus::return#4
SCREEN[0] = main::$0
plus::a#1 = '1'
plus::b#1 = 6
call plus
plus::return#1 = plus::return#3
to:main::@2
main::@2: scope:[main] from main::@1
plus::return#5 = phi( main::@1/plus::return#1 )
main::$1 = plus::return#5
SCREEN[1] = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
add::a#0 = plus::a#2
add::b#0 = plus::b#2
call add
add::return#0 = add::return#2
to:plus::@1
plus::@1: scope:[plus] from plus
add::return#3 = phi( plus/add::return#0 )
plus::$0 = add::return#3
plus::return#2 = plus::$0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
plus::return#6 = phi( plus::@1/plus::return#2 )
plus::return#3 = plus::return#6
return
to:@return
char add(char a , char b)
add: scope:[add] from plus
add::b#1 = phi( plus/add::b#0 )
add::a#1 = phi( plus/add::a#0 )
add::$0 = add::a#1 + add::b#1
add::return#1 = add::$0
to:add::@return
add::@return: scope:[add] from add
add::return#4 = phi( add/add::return#1 )
add::return#2 = add::return#4
return
to:@return
void __start()
__start: scope:[__start] from
call main
to:__start::@1
__start::@1: scope:[__start] from __start
to:__start::@return
__start::@return: scope:[__start] from __start::@1
return
to:@return
SYMBOL TABLE SSA
__constant char * const SCREEN = (char *)$400
void __start()
char add(char a , char b)
char add::$0
char add::a
char add::a#0
char add::a#1
char add::b
char add::b#0
char add::b#1
char add::return
char add::return#0
char add::return#1
char add::return#2
char add::return#3
char add::return#4
void main()
char main::$0
char main::$1
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::$0
char plus::a
char plus::a#0
char plus::a#1
char plus::a#2
char plus::b
char plus::b#0
char plus::b#1
char plus::b#2
char plus::return
char plus::return#0
char plus::return#1
char plus::return#2
char plus::return#3
char plus::return#4
char plus::return#5
char plus::return#6
Adding number conversion cast (unumber) 7 in plus::b#0 = 7
Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
Adding number conversion cast (unumber) 6 in plus::b#1 = 6
Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast plus::b#0 = (unumber)7
Inlining cast plus::b#1 = (unumber)6
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (char *) 1024
Simplifying constant integer cast 7
Simplifying constant integer cast 0
Simplifying constant integer cast 6
Simplifying constant integer cast 1
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (char) 7
Finalized unsigned number type (char) 0
Finalized unsigned number type (char) 6
Finalized unsigned number type (char) 1
Successful SSA optimization PassNFinalizeNumberTypeConversions
Alias plus::return#0 = plus::return#4
Alias plus::return#1 = plus::return#5
Alias add::return#0 = add::return#3
Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
Alias add::return#1 = add::$0 add::return#4 add::return#2
Successful SSA optimization Pass2AliasElimination
Identical Phi Values add::a#1 add::a#0
Identical Phi Values add::b#1 add::b#0
Successful SSA optimization Pass2IdenticalPhiElimination
Constant plus::a#0 = '0'
Constant plus::b#0 = 7
Constant plus::a#1 = '1'
Constant plus::b#1 = 6
Successful SSA optimization Pass2ConstantIdentification
Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
Successful SSA optimization PassNSimplifyExpressionWithZero
Removing unused procedure __start
Removing unused procedure block __start
Removing unused procedure block __start::@1
Removing unused procedure block __start::@return
Successful SSA optimization PassNEliminateEmptyStart
Inlining constant with var siblings plus::a#0
Inlining constant with var siblings plus::b#0
Inlining constant with var siblings plus::a#1
Inlining constant with var siblings plus::b#1
Constant inlined plus::b#1 = 6
Constant inlined plus::b#0 = 7
Constant inlined plus::a#1 = '1'
Constant inlined plus::a#0 = '0'
Successful SSA optimization Pass2ConstantInlining
Consolidated array index constant in *(SCREEN+1)
Successful SSA optimization Pass2ConstantAdditionElimination
Adding NOP phi() at start of main
CALL GRAPH
Calls in [main] to plus:1 plus:5
Calls in [plus] to add:13
Created 2 initial phi equivalence classes
Coalesced down to 2 phi equivalence classes
Adding NOP phi() at start of main
FINAL CONTROL FLOW GRAPH
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] add::a#0 = plus::a#2
[12] add::b#0 = plus::b#2
[13] call add
[14] add::return#0 = add::return#1
to:plus::@1
plus::@1: scope:[plus] from plus
[15] plus::return#2 = add::return#0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
[16] return
to:@return
char add(char a , char b)
add: scope:[add] from plus
[17] add::return#1 = add::a#0 + add::b#0
to:add::@return
add::@return: scope:[add] from add
[18] return
to:@return
VARIABLE REGISTER WEIGHTS
char add(char a , char b)
char add::a
char add::a#0 // 56.0
char add::b
char add::b#0 // 112.0
char add::return
char add::return#0 // 22.0
char add::return#1 // 37.33333333333333
void main()
char main::$0 // 4.0
char main::$1 // 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // 11.0
char plus::b
char plus::b#2 // 5.5
char plus::return
char plus::return#0 // 4.0
char plus::return#1 // 4.0
char plus::return#2 // 3.75
Initial phi equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
Added variable main::$0 to live range equivalence class [ main::$0 ]
Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
Added variable main::$1 to live range equivalence class [ main::$1 ]
Added variable add::a#0 to live range equivalence class [ add::a#0 ]
Added variable add::b#0 to live range equivalence class [ add::b#0 ]
Added variable add::return#0 to live range equivalence class [ add::return#0 ]
Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
Added variable add::return#1 to live range equivalence class [ add::return#1 ]
Complete equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
[ plus::return#0 ]
[ main::$0 ]
[ plus::return#1 ]
[ main::$1 ]
[ add::a#0 ]
[ add::b#0 ]
[ add::return#0 ]
[ plus::return#2 ]
[ add::return#1 ]
Allocated zp[1]:2 [ add::b#0 ]
Allocated zp[1]:3 [ add::a#0 ]
Allocated zp[1]:4 [ add::return#1 ]
Allocated zp[1]:5 [ add::return#0 ]
Allocated zp[1]:6 [ plus::a#2 ]
Allocated zp[1]:7 [ plus::b#2 ]
Allocated zp[1]:8 [ plus::return#0 ]
Allocated zp[1]:9 [ main::$0 ]
Allocated zp[1]:10 [ plus::return#1 ]
Allocated zp[1]:11 [ main::$1 ]
Allocated zp[1]:12 [ plus::return#2 ]
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ]
Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y ,
Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y ,
REGISTER UPLIFT SCOPES
Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ]
Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ]
Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ]
Uplift Scope []
Uplifting [add] best 189 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte a [ add::return#1 ] reg byte a [ add::return#0 ]
Limited combination testing to 100 combinations of 256 possible.
Uplifting [plus] best 161 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ]
Limited combination testing to 100 combinations of 768 possible.
Uplifting [main] best 149 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
Uplifting [] best 149 combination
Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ]
Uplifting [add] best 149 combination zp[1]:3 [ add::a#0 ]
Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ]
Uplifting [plus] best 146 combination reg byte x [ plus::return#2 ]
Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ]
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
/**
* @file call-banked-phi-case-3-near-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #3.
* Implementation using the __bank() directive.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-3-near-0.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
plus_from_main:
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
ldx #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
txa
jmp __b1
// main::@1
__b1:
// [3] main::$0 = plus::return#0
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
plus_from___b1:
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
ldx #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
txa
jmp __b2
// main::@2
__b2:
// [7] main::$1 = plus::return#1
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// close call
sta SCREEN+1
jmp __breturn
// main::@return
__breturn:
// [9] return
rts
}
.segment RAM_Bank1
// plus
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
sta.z add.a
// [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
txa
// [13] call add
jsr add
// [14] add::return#0 = add::return#1
jmp __b1
// plus::@1
__b1:
// [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
tax
jmp __breturn
// plus::@return
__breturn:
// [16] return
rts
}
.segment Code
// add
// __register(A) char add(__zp(2) char a, __register(A) char b)
add: {
.label a = 2
// [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
clc
adc.z a
jmp __breturn
// add::@return
__breturn:
// [18] return
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __b1
Removing instruction jmp __b2
Removing instruction jmp __breturn
Removing instruction jmp __b1
Removing instruction jmp __breturn
Removing instruction jmp __breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction plus_from_main:
Removing instruction __b1:
Removing instruction plus_from___b1:
Removing instruction __b2:
Removing instruction __breturn:
Removing instruction __b1:
Removing instruction __breturn:
Removing instruction __breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
__constant char * const SCREEN = (char *) 1024
char add(char a , char b)
char add::a
char add::a#0 // a zp[1]:2 56.0
char add::b
char add::b#0 // reg byte a 112.0
char add::return
char add::return#0 // reg byte a 22.0
char add::return#1 // reg byte a 37.33333333333333
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte a 11.0
char plus::b
char plus::b#2 // reg byte x 5.5
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte x 3.75
reg byte a [ plus::a#2 ]
reg byte x [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
zp[1]:2 [ add::a#0 ]
reg byte a [ add::b#0 ]
reg byte a [ add::return#0 ]
reg byte x [ plus::return#2 ]
reg byte a [ add::return#1 ]
FINAL ASSEMBLER
Score: 128
// File Comments
/**
* @file call-banked-phi-case-3-near-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #3.
* Implementation using the __bank() directive.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-3-near-0.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// plus('0', 7)
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
ldx #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('0', 7)
// [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
txa
// main::@1
// [3] main::$0 = plus::return#0
// SCREEN[0] = plus('0', 7)
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// plus('1', 6)
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
ldx #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('1', 6)
// [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
txa
// main::@2
// [7] main::$1 = plus::return#1
// SCREEN[1] = plus('1', 6)
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// close call
sta SCREEN+1
// main::@return
// }
// [9] return
rts
}
.segment RAM_Bank1
// plus
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// add(a, b)
// [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
sta.z add.a
// [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
txa
// [13] call add
jsr add
// [14] add::return#0 = add::return#1
// plus::@1
// [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
tax
// plus::@return
// }
// [16] return
rts
}
.segment Code
// add
// __register(A) char add(__zp(2) char a, __register(A) char b)
add: {
.label a = 2
// a+b
// [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
clc
adc.z a
// add::@return
// }
// [18] return
rts
}
// File Data

View File

@ -0,0 +1,33 @@
__constant char * const SCREEN = (char *) 1024
char add(char a , char b)
char add::a
char add::a#0 // a zp[1]:2 56.0
char add::b
char add::b#0 // reg byte a 112.0
char add::return
char add::return#0 // reg byte a 22.0
char add::return#1 // reg byte a 37.33333333333333
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte a 11.0
char plus::b
char plus::b#2 // reg byte x 5.5
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte x 3.75
reg byte a [ plus::a#2 ]
reg byte x [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
zp[1]:2 [ add::a#0 ]
reg byte a [ add::b#0 ]
reg byte a [ add::return#0 ]
reg byte x [ plus::return#2 ]
reg byte a [ add::return#1 ]

View File

@ -0,0 +1,106 @@
/**
* @file call-banked-phi-case-3-near-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #3.
* Implementation using the #pragma bank and nobank directives.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
.file [name="call-banked-phi-case-3-near-1.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
.label SCREEN = $400
.segment Code
main: {
// plus('0', 7)
ldx #7
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('0', 7)
txa
// SCREEN[0] = plus('0', 7)
sta SCREEN
// plus('1', 6)
ldx #6
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('1', 6)
txa
// SCREEN[1] = plus('1', 6)
// close call
sta SCREEN+1
// }
rts
}
.segment RAM_Bank1
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// add(a, b)
sta.z add.a
txa
jsr add
tax
// }
rts
}
.segment Code
// __register(A) char add(__zp(2) char a, __register(A) char b)
add: {
.label a = 2
// a+b
clc
adc.z a
// }
rts
}

View File

@ -0,0 +1,44 @@
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] add::a#0 = plus::a#2
[12] add::b#0 = plus::b#2
[13] call add
[14] add::return#0 = add::return#1
to:plus::@1
plus::@1: scope:[plus] from plus
[15] plus::return#2 = add::return#0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
[16] return
to:@return
char add(char a , char b)
add: scope:[add] from plus
[17] add::return#1 = add::a#0 + add::b#0
to:add::@return
add::@return: scope:[add] from add
[18] return
to:@return

View File

@ -0,0 +1,660 @@
Loading link script "call-banked-phi.ld"
CONTROL FLOW GRAPH SSA
void main()
main: scope:[main] from __start
plus::a#0 = '0'
plus::b#0 = 7
call plus
plus::return#0 = plus::return#3
to:main::@1
main::@1: scope:[main] from main
plus::return#4 = phi( main/plus::return#0 )
main::$0 = plus::return#4
SCREEN[0] = main::$0
plus::a#1 = '1'
plus::b#1 = 6
call plus
plus::return#1 = plus::return#3
to:main::@2
main::@2: scope:[main] from main::@1
plus::return#5 = phi( main::@1/plus::return#1 )
main::$1 = plus::return#5
SCREEN[1] = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
add::a#0 = plus::a#2
add::b#0 = plus::b#2
call add
add::return#0 = add::return#2
to:plus::@1
plus::@1: scope:[plus] from plus
add::return#3 = phi( plus/add::return#0 )
plus::$0 = add::return#3
plus::return#2 = plus::$0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
plus::return#6 = phi( plus::@1/plus::return#2 )
plus::return#3 = plus::return#6
return
to:@return
char add(char a , char b)
add: scope:[add] from plus
add::b#1 = phi( plus/add::b#0 )
add::a#1 = phi( plus/add::a#0 )
add::$0 = add::a#1 + add::b#1
add::return#1 = add::$0
to:add::@return
add::@return: scope:[add] from add
add::return#4 = phi( add/add::return#1 )
add::return#2 = add::return#4
return
to:@return
void __start()
__start: scope:[__start] from
call main
to:__start::@1
__start::@1: scope:[__start] from __start
to:__start::@return
__start::@return: scope:[__start] from __start::@1
return
to:@return
SYMBOL TABLE SSA
__constant char * const SCREEN = (char *)$400
void __start()
char add(char a , char b)
char add::$0
char add::a
char add::a#0
char add::a#1
char add::b
char add::b#0
char add::b#1
char add::return
char add::return#0
char add::return#1
char add::return#2
char add::return#3
char add::return#4
void main()
char main::$0
char main::$1
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::$0
char plus::a
char plus::a#0
char plus::a#1
char plus::a#2
char plus::b
char plus::b#0
char plus::b#1
char plus::b#2
char plus::return
char plus::return#0
char plus::return#1
char plus::return#2
char plus::return#3
char plus::return#4
char plus::return#5
char plus::return#6
Adding number conversion cast (unumber) 7 in plus::b#0 = 7
Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
Adding number conversion cast (unumber) 6 in plus::b#1 = 6
Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast plus::b#0 = (unumber)7
Inlining cast plus::b#1 = (unumber)6
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (char *) 1024
Simplifying constant integer cast 7
Simplifying constant integer cast 0
Simplifying constant integer cast 6
Simplifying constant integer cast 1
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (char) 7
Finalized unsigned number type (char) 0
Finalized unsigned number type (char) 6
Finalized unsigned number type (char) 1
Successful SSA optimization PassNFinalizeNumberTypeConversions
Alias plus::return#0 = plus::return#4
Alias plus::return#1 = plus::return#5
Alias add::return#0 = add::return#3
Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
Alias add::return#1 = add::$0 add::return#4 add::return#2
Successful SSA optimization Pass2AliasElimination
Identical Phi Values add::a#1 add::a#0
Identical Phi Values add::b#1 add::b#0
Successful SSA optimization Pass2IdenticalPhiElimination
Constant plus::a#0 = '0'
Constant plus::b#0 = 7
Constant plus::a#1 = '1'
Constant plus::b#1 = 6
Successful SSA optimization Pass2ConstantIdentification
Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
Successful SSA optimization PassNSimplifyExpressionWithZero
Removing unused procedure __start
Removing unused procedure block __start
Removing unused procedure block __start::@1
Removing unused procedure block __start::@return
Successful SSA optimization PassNEliminateEmptyStart
Inlining constant with var siblings plus::a#0
Inlining constant with var siblings plus::b#0
Inlining constant with var siblings plus::a#1
Inlining constant with var siblings plus::b#1
Constant inlined plus::b#1 = 6
Constant inlined plus::b#0 = 7
Constant inlined plus::a#1 = '1'
Constant inlined plus::a#0 = '0'
Successful SSA optimization Pass2ConstantInlining
Consolidated array index constant in *(SCREEN+1)
Successful SSA optimization Pass2ConstantAdditionElimination
Adding NOP phi() at start of main
CALL GRAPH
Calls in [main] to plus:1 plus:5
Calls in [plus] to add:13
Created 2 initial phi equivalence classes
Coalesced down to 2 phi equivalence classes
Adding NOP phi() at start of main
FINAL CONTROL FLOW GRAPH
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] add::a#0 = plus::a#2
[12] add::b#0 = plus::b#2
[13] call add
[14] add::return#0 = add::return#1
to:plus::@1
plus::@1: scope:[plus] from plus
[15] plus::return#2 = add::return#0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
[16] return
to:@return
char add(char a , char b)
add: scope:[add] from plus
[17] add::return#1 = add::a#0 + add::b#0
to:add::@return
add::@return: scope:[add] from add
[18] return
to:@return
VARIABLE REGISTER WEIGHTS
char add(char a , char b)
char add::a
char add::a#0 // 56.0
char add::b
char add::b#0 // 112.0
char add::return
char add::return#0 // 22.0
char add::return#1 // 37.33333333333333
void main()
char main::$0 // 4.0
char main::$1 // 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // 11.0
char plus::b
char plus::b#2 // 5.5
char plus::return
char plus::return#0 // 4.0
char plus::return#1 // 4.0
char plus::return#2 // 3.75
Initial phi equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
Added variable main::$0 to live range equivalence class [ main::$0 ]
Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
Added variable main::$1 to live range equivalence class [ main::$1 ]
Added variable add::a#0 to live range equivalence class [ add::a#0 ]
Added variable add::b#0 to live range equivalence class [ add::b#0 ]
Added variable add::return#0 to live range equivalence class [ add::return#0 ]
Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
Added variable add::return#1 to live range equivalence class [ add::return#1 ]
Complete equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
[ plus::return#0 ]
[ main::$0 ]
[ plus::return#1 ]
[ main::$1 ]
[ add::a#0 ]
[ add::b#0 ]
[ add::return#0 ]
[ plus::return#2 ]
[ add::return#1 ]
Allocated zp[1]:2 [ add::b#0 ]
Allocated zp[1]:3 [ add::a#0 ]
Allocated zp[1]:4 [ add::return#1 ]
Allocated zp[1]:5 [ add::return#0 ]
Allocated zp[1]:6 [ plus::a#2 ]
Allocated zp[1]:7 [ plus::b#2 ]
Allocated zp[1]:8 [ plus::return#0 ]
Allocated zp[1]:9 [ main::$0 ]
Allocated zp[1]:10 [ plus::return#1 ]
Allocated zp[1]:11 [ main::$1 ]
Allocated zp[1]:12 [ plus::return#2 ]
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ]
Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y ,
Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y ,
REGISTER UPLIFT SCOPES
Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ]
Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ]
Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ]
Uplift Scope []
Uplifting [add] best 189 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte a [ add::return#1 ] reg byte a [ add::return#0 ]
Limited combination testing to 100 combinations of 256 possible.
Uplifting [plus] best 161 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ]
Limited combination testing to 100 combinations of 768 possible.
Uplifting [main] best 149 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
Uplifting [] best 149 combination
Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ]
Uplifting [add] best 149 combination zp[1]:3 [ add::a#0 ]
Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ]
Uplifting [plus] best 146 combination reg byte x [ plus::return#2 ]
Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ]
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
/**
* @file call-banked-phi-case-3-near-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #3.
* Implementation using the #pragma bank and nobank directives.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-3-near-1.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
plus_from_main:
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
ldx #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
txa
jmp __b1
// main::@1
__b1:
// [3] main::$0 = plus::return#0
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
plus_from___b1:
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
ldx #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
txa
jmp __b2
// main::@2
__b2:
// [7] main::$1 = plus::return#1
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// close call
sta SCREEN+1
jmp __breturn
// main::@return
__breturn:
// [9] return
rts
}
.segment RAM_Bank1
// plus
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
sta.z add.a
// [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
txa
// [13] call add
jsr add
// [14] add::return#0 = add::return#1
jmp __b1
// plus::@1
__b1:
// [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
tax
jmp __breturn
// plus::@return
__breturn:
// [16] return
rts
}
.segment Code
// add
// __register(A) char add(__zp(2) char a, __register(A) char b)
add: {
.label a = 2
// [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
clc
adc.z a
jmp __breturn
// add::@return
__breturn:
// [18] return
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __b1
Removing instruction jmp __b2
Removing instruction jmp __breturn
Removing instruction jmp __b1
Removing instruction jmp __breturn
Removing instruction jmp __breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction plus_from_main:
Removing instruction __b1:
Removing instruction plus_from___b1:
Removing instruction __b2:
Removing instruction __breturn:
Removing instruction __b1:
Removing instruction __breturn:
Removing instruction __breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
__constant char * const SCREEN = (char *) 1024
char add(char a , char b)
char add::a
char add::a#0 // a zp[1]:2 56.0
char add::b
char add::b#0 // reg byte a 112.0
char add::return
char add::return#0 // reg byte a 22.0
char add::return#1 // reg byte a 37.33333333333333
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte a 11.0
char plus::b
char plus::b#2 // reg byte x 5.5
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte x 3.75
reg byte a [ plus::a#2 ]
reg byte x [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
zp[1]:2 [ add::a#0 ]
reg byte a [ add::b#0 ]
reg byte a [ add::return#0 ]
reg byte x [ plus::return#2 ]
reg byte a [ add::return#1 ]
FINAL ASSEMBLER
Score: 128
// File Comments
/**
* @file call-banked-phi-case-3-near-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #3.
* Implementation using the #pragma bank and nobank directives.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-3-near-1.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// plus('0', 7)
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
ldx #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('0', 7)
// [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
txa
// main::@1
// [3] main::$0 = plus::return#0
// SCREEN[0] = plus('0', 7)
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// plus('1', 6)
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
ldx #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('1', 6)
// [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
txa
// main::@2
// [7] main::$1 = plus::return#1
// SCREEN[1] = plus('1', 6)
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// close call
sta SCREEN+1
// main::@return
// }
// [9] return
rts
}
.segment RAM_Bank1
// plus
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// add(a, b)
// [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
sta.z add.a
// [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
txa
// [13] call add
jsr add
// [14] add::return#0 = add::return#1
// plus::@1
// [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
tax
// plus::@return
// }
// [16] return
rts
}
.segment Code
// add
// __register(A) char add(__zp(2) char a, __register(A) char b)
add: {
.label a = 2
// a+b
// [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
clc
adc.z a
// add::@return
// }
// [18] return
rts
}
// File Data

View File

@ -0,0 +1,33 @@
__constant char * const SCREEN = (char *) 1024
char add(char a , char b)
char add::a
char add::a#0 // a zp[1]:2 56.0
char add::b
char add::b#0 // reg byte a 112.0
char add::return
char add::return#0 // reg byte a 22.0
char add::return#1 // reg byte a 37.33333333333333
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte a 11.0
char plus::b
char plus::b#2 // reg byte x 5.5
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte x 3.75
reg byte a [ plus::a#2 ]
reg byte x [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
zp[1]:2 [ add::a#0 ]
reg byte a [ add::b#0 ]
reg byte a [ add::return#0 ]
reg byte x [ plus::return#2 ]
reg byte a [ add::return#1 ]

View File

@ -0,0 +1,106 @@
/**
* @file call-banked-phi-case-4-near-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #4.
* Implementation using the __bank() directive.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
.file [name="call-banked-phi-case-4-near-0.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
.label SCREEN = $400
.segment Code
main: {
// plus('0', 7)
ldx #7
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('0', 7)
txa
// SCREEN[0] = plus('0', 7)
sta SCREEN
// plus('1', 6)
ldx #6
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('1', 6)
txa
// SCREEN[1] = plus('1', 6)
// close call
sta SCREEN+1
// }
rts
}
.segment RAM_Bank1
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// add(a, b)
sta.z add.a
txa
jsr add
tax
// }
rts
}
// __register(A) char add(__zp(2) char a, __register(A) char b)
// __bank(cx16_ram, 1)
add: {
.label a = 2
// a+b
clc
adc.z a
// }
rts
}

View File

@ -0,0 +1,44 @@
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] add::a#0 = plus::a#2
[12] add::b#0 = plus::b#2
[13] call add
[14] add::return#0 = add::return#1
to:plus::@1
plus::@1: scope:[plus] from plus
[15] plus::return#2 = add::return#0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
[16] return
to:@return
__bank(cx16_ram, 1) char add(char a , char b)
add: scope:[add] from plus
[17] add::return#1 = add::a#0 + add::b#0
to:add::@return
add::@return: scope:[add] from add
[18] return
to:@return

View File

@ -0,0 +1,660 @@
Loading link script "call-banked-phi.ld"
CONTROL FLOW GRAPH SSA
void main()
main: scope:[main] from __start
plus::a#0 = '0'
plus::b#0 = 7
call plus
plus::return#0 = plus::return#3
to:main::@1
main::@1: scope:[main] from main
plus::return#4 = phi( main/plus::return#0 )
main::$0 = plus::return#4
SCREEN[0] = main::$0
plus::a#1 = '1'
plus::b#1 = 6
call plus
plus::return#1 = plus::return#3
to:main::@2
main::@2: scope:[main] from main::@1
plus::return#5 = phi( main::@1/plus::return#1 )
main::$1 = plus::return#5
SCREEN[1] = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
add::a#0 = plus::a#2
add::b#0 = plus::b#2
call add
add::return#0 = add::return#2
to:plus::@1
plus::@1: scope:[plus] from plus
add::return#3 = phi( plus/add::return#0 )
plus::$0 = add::return#3
plus::return#2 = plus::$0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
plus::return#6 = phi( plus::@1/plus::return#2 )
plus::return#3 = plus::return#6
return
to:@return
__bank(cx16_ram, 1) char add(char a , char b)
add: scope:[add] from plus
add::b#1 = phi( plus/add::b#0 )
add::a#1 = phi( plus/add::a#0 )
add::$0 = add::a#1 + add::b#1
add::return#1 = add::$0
to:add::@return
add::@return: scope:[add] from add
add::return#4 = phi( add/add::return#1 )
add::return#2 = add::return#4
return
to:@return
void __start()
__start: scope:[__start] from
call main
to:__start::@1
__start::@1: scope:[__start] from __start
to:__start::@return
__start::@return: scope:[__start] from __start::@1
return
to:@return
SYMBOL TABLE SSA
__constant char * const SCREEN = (char *)$400
void __start()
__bank(cx16_ram, 1) char add(char a , char b)
char add::$0
char add::a
char add::a#0
char add::a#1
char add::b
char add::b#0
char add::b#1
char add::return
char add::return#0
char add::return#1
char add::return#2
char add::return#3
char add::return#4
void main()
char main::$0
char main::$1
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::$0
char plus::a
char plus::a#0
char plus::a#1
char plus::a#2
char plus::b
char plus::b#0
char plus::b#1
char plus::b#2
char plus::return
char plus::return#0
char plus::return#1
char plus::return#2
char plus::return#3
char plus::return#4
char plus::return#5
char plus::return#6
Adding number conversion cast (unumber) 7 in plus::b#0 = 7
Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
Adding number conversion cast (unumber) 6 in plus::b#1 = 6
Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast plus::b#0 = (unumber)7
Inlining cast plus::b#1 = (unumber)6
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (char *) 1024
Simplifying constant integer cast 7
Simplifying constant integer cast 0
Simplifying constant integer cast 6
Simplifying constant integer cast 1
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (char) 7
Finalized unsigned number type (char) 0
Finalized unsigned number type (char) 6
Finalized unsigned number type (char) 1
Successful SSA optimization PassNFinalizeNumberTypeConversions
Alias plus::return#0 = plus::return#4
Alias plus::return#1 = plus::return#5
Alias add::return#0 = add::return#3
Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
Alias add::return#1 = add::$0 add::return#4 add::return#2
Successful SSA optimization Pass2AliasElimination
Identical Phi Values add::a#1 add::a#0
Identical Phi Values add::b#1 add::b#0
Successful SSA optimization Pass2IdenticalPhiElimination
Constant plus::a#0 = '0'
Constant plus::b#0 = 7
Constant plus::a#1 = '1'
Constant plus::b#1 = 6
Successful SSA optimization Pass2ConstantIdentification
Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
Successful SSA optimization PassNSimplifyExpressionWithZero
Removing unused procedure __start
Removing unused procedure block __start
Removing unused procedure block __start::@1
Removing unused procedure block __start::@return
Successful SSA optimization PassNEliminateEmptyStart
Inlining constant with var siblings plus::a#0
Inlining constant with var siblings plus::b#0
Inlining constant with var siblings plus::a#1
Inlining constant with var siblings plus::b#1
Constant inlined plus::b#1 = 6
Constant inlined plus::b#0 = 7
Constant inlined plus::a#1 = '1'
Constant inlined plus::a#0 = '0'
Successful SSA optimization Pass2ConstantInlining
Consolidated array index constant in *(SCREEN+1)
Successful SSA optimization Pass2ConstantAdditionElimination
Adding NOP phi() at start of main
CALL GRAPH
Calls in [main] to plus:1 plus:5
Calls in [plus] to add:13
Created 2 initial phi equivalence classes
Coalesced down to 2 phi equivalence classes
Adding NOP phi() at start of main
FINAL CONTROL FLOW GRAPH
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] add::a#0 = plus::a#2
[12] add::b#0 = plus::b#2
[13] call add
[14] add::return#0 = add::return#1
to:plus::@1
plus::@1: scope:[plus] from plus
[15] plus::return#2 = add::return#0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
[16] return
to:@return
__bank(cx16_ram, 1) char add(char a , char b)
add: scope:[add] from plus
[17] add::return#1 = add::a#0 + add::b#0
to:add::@return
add::@return: scope:[add] from add
[18] return
to:@return
VARIABLE REGISTER WEIGHTS
__bank(cx16_ram, 1) char add(char a , char b)
char add::a
char add::a#0 // 56.0
char add::b
char add::b#0 // 112.0
char add::return
char add::return#0 // 22.0
char add::return#1 // 37.33333333333333
void main()
char main::$0 // 4.0
char main::$1 // 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // 11.0
char plus::b
char plus::b#2 // 5.5
char plus::return
char plus::return#0 // 4.0
char plus::return#1 // 4.0
char plus::return#2 // 3.75
Initial phi equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
Added variable main::$0 to live range equivalence class [ main::$0 ]
Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
Added variable main::$1 to live range equivalence class [ main::$1 ]
Added variable add::a#0 to live range equivalence class [ add::a#0 ]
Added variable add::b#0 to live range equivalence class [ add::b#0 ]
Added variable add::return#0 to live range equivalence class [ add::return#0 ]
Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
Added variable add::return#1 to live range equivalence class [ add::return#1 ]
Complete equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
[ plus::return#0 ]
[ main::$0 ]
[ plus::return#1 ]
[ main::$1 ]
[ add::a#0 ]
[ add::b#0 ]
[ add::return#0 ]
[ plus::return#2 ]
[ add::return#1 ]
Allocated zp[1]:2 [ add::b#0 ]
Allocated zp[1]:3 [ add::a#0 ]
Allocated zp[1]:4 [ add::return#1 ]
Allocated zp[1]:5 [ add::return#0 ]
Allocated zp[1]:6 [ plus::a#2 ]
Allocated zp[1]:7 [ plus::b#2 ]
Allocated zp[1]:8 [ plus::return#0 ]
Allocated zp[1]:9 [ main::$0 ]
Allocated zp[1]:10 [ plus::return#1 ]
Allocated zp[1]:11 [ main::$1 ]
Allocated zp[1]:12 [ plus::return#2 ]
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ]
Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y ,
Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y ,
REGISTER UPLIFT SCOPES
Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ]
Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ]
Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ]
Uplift Scope []
Uplifting [add] best 189 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte a [ add::return#1 ] reg byte a [ add::return#0 ]
Limited combination testing to 100 combinations of 256 possible.
Uplifting [plus] best 161 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ]
Limited combination testing to 100 combinations of 768 possible.
Uplifting [main] best 149 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
Uplifting [] best 149 combination
Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ]
Uplifting [add] best 149 combination zp[1]:3 [ add::a#0 ]
Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ]
Uplifting [plus] best 146 combination reg byte x [ plus::return#2 ]
Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ]
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
/**
* @file call-banked-phi-case-4-near-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #4.
* Implementation using the __bank() directive.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-4-near-0.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
plus_from_main:
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
ldx #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
txa
jmp __b1
// main::@1
__b1:
// [3] main::$0 = plus::return#0
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
plus_from___b1:
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
ldx #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
txa
jmp __b2
// main::@2
__b2:
// [7] main::$1 = plus::return#1
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// close call
sta SCREEN+1
jmp __breturn
// main::@return
__breturn:
// [9] return
rts
}
.segment RAM_Bank1
// plus
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
sta.z add.a
// [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
txa
// [13] call add
jsr add
// [14] add::return#0 = add::return#1
jmp __b1
// plus::@1
__b1:
// [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
tax
jmp __breturn
// plus::@return
__breturn:
// [16] return
rts
}
// add
// __register(A) char add(__zp(2) char a, __register(A) char b)
// __bank(cx16_ram, 1)
add: {
.label a = 2
// [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
clc
adc.z a
jmp __breturn
// add::@return
__breturn:
// [18] return
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __b1
Removing instruction jmp __b2
Removing instruction jmp __breturn
Removing instruction jmp __b1
Removing instruction jmp __breturn
Removing instruction jmp __breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction plus_from_main:
Removing instruction __b1:
Removing instruction plus_from___b1:
Removing instruction __b2:
Removing instruction __breturn:
Removing instruction __b1:
Removing instruction __breturn:
Removing instruction __breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
__constant char * const SCREEN = (char *) 1024
__bank(cx16_ram, 1) char add(char a , char b)
char add::a
char add::a#0 // a zp[1]:2 56.0
char add::b
char add::b#0 // reg byte a 112.0
char add::return
char add::return#0 // reg byte a 22.0
char add::return#1 // reg byte a 37.33333333333333
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte a 11.0
char plus::b
char plus::b#2 // reg byte x 5.5
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte x 3.75
reg byte a [ plus::a#2 ]
reg byte x [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
zp[1]:2 [ add::a#0 ]
reg byte a [ add::b#0 ]
reg byte a [ add::return#0 ]
reg byte x [ plus::return#2 ]
reg byte a [ add::return#1 ]
FINAL ASSEMBLER
Score: 128
// File Comments
/**
* @file call-banked-phi-case-4-near-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #4.
* Implementation using the __bank() directive.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-4-near-0.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// plus('0', 7)
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
ldx #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('0', 7)
// [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
txa
// main::@1
// [3] main::$0 = plus::return#0
// SCREEN[0] = plus('0', 7)
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// plus('1', 6)
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
ldx #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('1', 6)
// [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
txa
// main::@2
// [7] main::$1 = plus::return#1
// SCREEN[1] = plus('1', 6)
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// close call
sta SCREEN+1
// main::@return
// }
// [9] return
rts
}
.segment RAM_Bank1
// plus
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// add(a, b)
// [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
sta.z add.a
// [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
txa
// [13] call add
jsr add
// [14] add::return#0 = add::return#1
// plus::@1
// [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
tax
// plus::@return
// }
// [16] return
rts
}
// add
// __register(A) char add(__zp(2) char a, __register(A) char b)
// __bank(cx16_ram, 1)
add: {
.label a = 2
// a+b
// [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
clc
adc.z a
// add::@return
// }
// [18] return
rts
}
// File Data

View File

@ -0,0 +1,33 @@
__constant char * const SCREEN = (char *) 1024
__bank(cx16_ram, 1) char add(char a , char b)
char add::a
char add::a#0 // a zp[1]:2 56.0
char add::b
char add::b#0 // reg byte a 112.0
char add::return
char add::return#0 // reg byte a 22.0
char add::return#1 // reg byte a 37.33333333333333
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte a 11.0
char plus::b
char plus::b#2 // reg byte x 5.5
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte x 3.75
reg byte a [ plus::a#2 ]
reg byte x [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
zp[1]:2 [ add::a#0 ]
reg byte a [ add::b#0 ]
reg byte a [ add::return#0 ]
reg byte x [ plus::return#2 ]
reg byte a [ add::return#1 ]

View File

@ -0,0 +1,106 @@
/**
* @file call-banked-phi-case-4-near-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #4.
* Implementation using the #pragma bank and nobank directives.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
.file [name="call-banked-phi-case-4-near-1.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
.label SCREEN = $400
.segment Code
main: {
// plus('0', 7)
ldx #7
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('0', 7)
txa
// SCREEN[0] = plus('0', 7)
sta SCREEN
// plus('1', 6)
ldx #6
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('1', 6)
txa
// SCREEN[1] = plus('1', 6)
// close call
sta SCREEN+1
// }
rts
}
.segment RAM_Bank1
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// add(a, b)
sta.z add.a
txa
jsr add
tax
// }
rts
}
// __register(A) char add(__zp(2) char a, __register(A) char b)
// __bank(cx16_ram, 1)
add: {
.label a = 2
// a+b
clc
adc.z a
// }
rts
}

View File

@ -0,0 +1,44 @@
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] add::a#0 = plus::a#2
[12] add::b#0 = plus::b#2
[13] call add
[14] add::return#0 = add::return#1
to:plus::@1
plus::@1: scope:[plus] from plus
[15] plus::return#2 = add::return#0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
[16] return
to:@return
__bank(cx16_ram, 1) char add(char a , char b)
add: scope:[add] from plus
[17] add::return#1 = add::a#0 + add::b#0
to:add::@return
add::@return: scope:[add] from add
[18] return
to:@return

View File

@ -0,0 +1,660 @@
Loading link script "call-banked-phi.ld"
CONTROL FLOW GRAPH SSA
void main()
main: scope:[main] from __start
plus::a#0 = '0'
plus::b#0 = 7
call plus
plus::return#0 = plus::return#3
to:main::@1
main::@1: scope:[main] from main
plus::return#4 = phi( main/plus::return#0 )
main::$0 = plus::return#4
SCREEN[0] = main::$0
plus::a#1 = '1'
plus::b#1 = 6
call plus
plus::return#1 = plus::return#3
to:main::@2
main::@2: scope:[main] from main::@1
plus::return#5 = phi( main::@1/plus::return#1 )
main::$1 = plus::return#5
SCREEN[1] = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
add::a#0 = plus::a#2
add::b#0 = plus::b#2
call add
add::return#0 = add::return#2
to:plus::@1
plus::@1: scope:[plus] from plus
add::return#3 = phi( plus/add::return#0 )
plus::$0 = add::return#3
plus::return#2 = plus::$0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
plus::return#6 = phi( plus::@1/plus::return#2 )
plus::return#3 = plus::return#6
return
to:@return
__bank(cx16_ram, 1) char add(char a , char b)
add: scope:[add] from plus
add::b#1 = phi( plus/add::b#0 )
add::a#1 = phi( plus/add::a#0 )
add::$0 = add::a#1 + add::b#1
add::return#1 = add::$0
to:add::@return
add::@return: scope:[add] from add
add::return#4 = phi( add/add::return#1 )
add::return#2 = add::return#4
return
to:@return
void __start()
__start: scope:[__start] from
call main
to:__start::@1
__start::@1: scope:[__start] from __start
to:__start::@return
__start::@return: scope:[__start] from __start::@1
return
to:@return
SYMBOL TABLE SSA
__constant char * const SCREEN = (char *)$400
void __start()
__bank(cx16_ram, 1) char add(char a , char b)
char add::$0
char add::a
char add::a#0
char add::a#1
char add::b
char add::b#0
char add::b#1
char add::return
char add::return#0
char add::return#1
char add::return#2
char add::return#3
char add::return#4
void main()
char main::$0
char main::$1
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::$0
char plus::a
char plus::a#0
char plus::a#1
char plus::a#2
char plus::b
char plus::b#0
char plus::b#1
char plus::b#2
char plus::return
char plus::return#0
char plus::return#1
char plus::return#2
char plus::return#3
char plus::return#4
char plus::return#5
char plus::return#6
Adding number conversion cast (unumber) 7 in plus::b#0 = 7
Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
Adding number conversion cast (unumber) 6 in plus::b#1 = 6
Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast plus::b#0 = (unumber)7
Inlining cast plus::b#1 = (unumber)6
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (char *) 1024
Simplifying constant integer cast 7
Simplifying constant integer cast 0
Simplifying constant integer cast 6
Simplifying constant integer cast 1
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (char) 7
Finalized unsigned number type (char) 0
Finalized unsigned number type (char) 6
Finalized unsigned number type (char) 1
Successful SSA optimization PassNFinalizeNumberTypeConversions
Alias plus::return#0 = plus::return#4
Alias plus::return#1 = plus::return#5
Alias add::return#0 = add::return#3
Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
Alias add::return#1 = add::$0 add::return#4 add::return#2
Successful SSA optimization Pass2AliasElimination
Identical Phi Values add::a#1 add::a#0
Identical Phi Values add::b#1 add::b#0
Successful SSA optimization Pass2IdenticalPhiElimination
Constant plus::a#0 = '0'
Constant plus::b#0 = 7
Constant plus::a#1 = '1'
Constant plus::b#1 = 6
Successful SSA optimization Pass2ConstantIdentification
Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
Successful SSA optimization PassNSimplifyExpressionWithZero
Removing unused procedure __start
Removing unused procedure block __start
Removing unused procedure block __start::@1
Removing unused procedure block __start::@return
Successful SSA optimization PassNEliminateEmptyStart
Inlining constant with var siblings plus::a#0
Inlining constant with var siblings plus::b#0
Inlining constant with var siblings plus::a#1
Inlining constant with var siblings plus::b#1
Constant inlined plus::b#1 = 6
Constant inlined plus::b#0 = 7
Constant inlined plus::a#1 = '1'
Constant inlined plus::a#0 = '0'
Successful SSA optimization Pass2ConstantInlining
Consolidated array index constant in *(SCREEN+1)
Successful SSA optimization Pass2ConstantAdditionElimination
Adding NOP phi() at start of main
CALL GRAPH
Calls in [main] to plus:1 plus:5
Calls in [plus] to add:13
Created 2 initial phi equivalence classes
Coalesced down to 2 phi equivalence classes
Adding NOP phi() at start of main
FINAL CONTROL FLOW GRAPH
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] add::a#0 = plus::a#2
[12] add::b#0 = plus::b#2
[13] call add
[14] add::return#0 = add::return#1
to:plus::@1
plus::@1: scope:[plus] from plus
[15] plus::return#2 = add::return#0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
[16] return
to:@return
__bank(cx16_ram, 1) char add(char a , char b)
add: scope:[add] from plus
[17] add::return#1 = add::a#0 + add::b#0
to:add::@return
add::@return: scope:[add] from add
[18] return
to:@return
VARIABLE REGISTER WEIGHTS
__bank(cx16_ram, 1) char add(char a , char b)
char add::a
char add::a#0 // 56.0
char add::b
char add::b#0 // 112.0
char add::return
char add::return#0 // 22.0
char add::return#1 // 37.33333333333333
void main()
char main::$0 // 4.0
char main::$1 // 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // 11.0
char plus::b
char plus::b#2 // 5.5
char plus::return
char plus::return#0 // 4.0
char plus::return#1 // 4.0
char plus::return#2 // 3.75
Initial phi equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
Added variable main::$0 to live range equivalence class [ main::$0 ]
Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
Added variable main::$1 to live range equivalence class [ main::$1 ]
Added variable add::a#0 to live range equivalence class [ add::a#0 ]
Added variable add::b#0 to live range equivalence class [ add::b#0 ]
Added variable add::return#0 to live range equivalence class [ add::return#0 ]
Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
Added variable add::return#1 to live range equivalence class [ add::return#1 ]
Complete equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
[ plus::return#0 ]
[ main::$0 ]
[ plus::return#1 ]
[ main::$1 ]
[ add::a#0 ]
[ add::b#0 ]
[ add::return#0 ]
[ plus::return#2 ]
[ add::return#1 ]
Allocated zp[1]:2 [ add::b#0 ]
Allocated zp[1]:3 [ add::a#0 ]
Allocated zp[1]:4 [ add::return#1 ]
Allocated zp[1]:5 [ add::return#0 ]
Allocated zp[1]:6 [ plus::a#2 ]
Allocated zp[1]:7 [ plus::b#2 ]
Allocated zp[1]:8 [ plus::return#0 ]
Allocated zp[1]:9 [ main::$0 ]
Allocated zp[1]:10 [ plus::return#1 ]
Allocated zp[1]:11 [ main::$1 ]
Allocated zp[1]:12 [ plus::return#2 ]
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ]
Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y ,
Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y ,
REGISTER UPLIFT SCOPES
Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ]
Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ]
Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ]
Uplift Scope []
Uplifting [add] best 189 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte a [ add::return#1 ] reg byte a [ add::return#0 ]
Limited combination testing to 100 combinations of 256 possible.
Uplifting [plus] best 161 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ]
Limited combination testing to 100 combinations of 768 possible.
Uplifting [main] best 149 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
Uplifting [] best 149 combination
Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ]
Uplifting [add] best 149 combination zp[1]:3 [ add::a#0 ]
Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ]
Uplifting [plus] best 146 combination reg byte x [ plus::return#2 ]
Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ]
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
/**
* @file call-banked-phi-case-4-near-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #4.
* Implementation using the #pragma bank and nobank directives.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-4-near-1.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
plus_from_main:
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
ldx #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
txa
jmp __b1
// main::@1
__b1:
// [3] main::$0 = plus::return#0
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
plus_from___b1:
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
ldx #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
txa
jmp __b2
// main::@2
__b2:
// [7] main::$1 = plus::return#1
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// close call
sta SCREEN+1
jmp __breturn
// main::@return
__breturn:
// [9] return
rts
}
.segment RAM_Bank1
// plus
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
sta.z add.a
// [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
txa
// [13] call add
jsr add
// [14] add::return#0 = add::return#1
jmp __b1
// plus::@1
__b1:
// [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
tax
jmp __breturn
// plus::@return
__breturn:
// [16] return
rts
}
// add
// __register(A) char add(__zp(2) char a, __register(A) char b)
// __bank(cx16_ram, 1)
add: {
.label a = 2
// [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
clc
adc.z a
jmp __breturn
// add::@return
__breturn:
// [18] return
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __b1
Removing instruction jmp __b2
Removing instruction jmp __breturn
Removing instruction jmp __b1
Removing instruction jmp __breturn
Removing instruction jmp __breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction plus_from_main:
Removing instruction __b1:
Removing instruction plus_from___b1:
Removing instruction __b2:
Removing instruction __breturn:
Removing instruction __b1:
Removing instruction __breturn:
Removing instruction __breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
__constant char * const SCREEN = (char *) 1024
__bank(cx16_ram, 1) char add(char a , char b)
char add::a
char add::a#0 // a zp[1]:2 56.0
char add::b
char add::b#0 // reg byte a 112.0
char add::return
char add::return#0 // reg byte a 22.0
char add::return#1 // reg byte a 37.33333333333333
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte a 11.0
char plus::b
char plus::b#2 // reg byte x 5.5
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte x 3.75
reg byte a [ plus::a#2 ]
reg byte x [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
zp[1]:2 [ add::a#0 ]
reg byte a [ add::b#0 ]
reg byte a [ add::return#0 ]
reg byte x [ plus::return#2 ]
reg byte a [ add::return#1 ]
FINAL ASSEMBLER
Score: 128
// File Comments
/**
* @file call-banked-phi-case-4-near-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #4.
* Implementation using the #pragma bank and nobank directives.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-4-near-1.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// plus('0', 7)
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
ldx #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('0', 7)
// [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
txa
// main::@1
// [3] main::$0 = plus::return#0
// SCREEN[0] = plus('0', 7)
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// plus('1', 6)
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
ldx #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('1', 6)
// [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
txa
// main::@2
// [7] main::$1 = plus::return#1
// SCREEN[1] = plus('1', 6)
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// close call
sta SCREEN+1
// main::@return
// }
// [9] return
rts
}
.segment RAM_Bank1
// plus
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// add(a, b)
// [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
sta.z add.a
// [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
txa
// [13] call add
jsr add
// [14] add::return#0 = add::return#1
// plus::@1
// [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
tax
// plus::@return
// }
// [16] return
rts
}
// add
// __register(A) char add(__zp(2) char a, __register(A) char b)
// __bank(cx16_ram, 1)
add: {
.label a = 2
// a+b
// [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
clc
adc.z a
// add::@return
// }
// [18] return
rts
}
// File Data

View File

@ -0,0 +1,33 @@
__constant char * const SCREEN = (char *) 1024
__bank(cx16_ram, 1) char add(char a , char b)
char add::a
char add::a#0 // a zp[1]:2 56.0
char add::b
char add::b#0 // reg byte a 112.0
char add::return
char add::return#0 // reg byte a 22.0
char add::return#1 // reg byte a 37.33333333333333
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte a 11.0
char plus::b
char plus::b#2 // reg byte x 5.5
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte x 3.75
reg byte a [ plus::a#2 ]
reg byte x [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
zp[1]:2 [ add::a#0 ]
reg byte a [ add::b#0 ]
reg byte a [ add::return#0 ]
reg byte x [ plus::return#2 ]
reg byte a [ add::return#1 ]

View File

@ -0,0 +1,110 @@
/**
* @file call-banked-phi-case-5-far-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #5.
* Implementation using the __bank() directive.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
.file [name="call-banked-phi-case-5-far-0.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
.label SCREEN = $400
.segment Code
main: {
// plus('0', 7)
ldx #7
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('0', 7)
txa
// SCREEN[0] = plus('0', 7)
sta SCREEN
// plus('1', 6)
ldx #6
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('1', 6)
txa
// SCREEN[1] = plus('1', 6)
// close call
sta SCREEN+1
// }
rts
}
.segment RAM_Bank1
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// add(a, b)
sta.z add.a
txa
jsr $ff6e
.byte <add
.byte >add
.byte 2
tax
// }
rts
}
.segment RAM_Bank2
// __register(A) char add(__zp(2) char a, __register(A) char b)
// __bank(cx16_ram, 2)
add: {
.label a = 2
// a+b
clc
adc.z a
// }
rts
}

View File

@ -0,0 +1,44 @@
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] add::a#0 = plus::a#2
[12] add::b#0 = plus::b#2
[13] call add
[14] add::return#0 = add::return#1
to:plus::@1
plus::@1: scope:[plus] from plus
[15] plus::return#2 = add::return#0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
[16] return
to:@return
__bank(cx16_ram, 2) char add(char a , char b)
add: scope:[add] from plus
[17] add::return#1 = add::a#0 + add::b#0
to:add::@return
add::@return: scope:[add] from add
[18] return
to:@return

View File

@ -0,0 +1,668 @@
Loading link script "call-banked-phi.ld"
CONTROL FLOW GRAPH SSA
void main()
main: scope:[main] from __start
plus::a#0 = '0'
plus::b#0 = 7
call plus
plus::return#0 = plus::return#3
to:main::@1
main::@1: scope:[main] from main
plus::return#4 = phi( main/plus::return#0 )
main::$0 = plus::return#4
SCREEN[0] = main::$0
plus::a#1 = '1'
plus::b#1 = 6
call plus
plus::return#1 = plus::return#3
to:main::@2
main::@2: scope:[main] from main::@1
plus::return#5 = phi( main::@1/plus::return#1 )
main::$1 = plus::return#5
SCREEN[1] = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
add::a#0 = plus::a#2
add::b#0 = plus::b#2
call add
add::return#0 = add::return#2
to:plus::@1
plus::@1: scope:[plus] from plus
add::return#3 = phi( plus/add::return#0 )
plus::$0 = add::return#3
plus::return#2 = plus::$0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
plus::return#6 = phi( plus::@1/plus::return#2 )
plus::return#3 = plus::return#6
return
to:@return
__bank(cx16_ram, 2) char add(char a , char b)
add: scope:[add] from plus
add::b#1 = phi( plus/add::b#0 )
add::a#1 = phi( plus/add::a#0 )
add::$0 = add::a#1 + add::b#1
add::return#1 = add::$0
to:add::@return
add::@return: scope:[add] from add
add::return#4 = phi( add/add::return#1 )
add::return#2 = add::return#4
return
to:@return
void __start()
__start: scope:[__start] from
call main
to:__start::@1
__start::@1: scope:[__start] from __start
to:__start::@return
__start::@return: scope:[__start] from __start::@1
return
to:@return
SYMBOL TABLE SSA
__constant char * const SCREEN = (char *)$400
void __start()
__bank(cx16_ram, 2) char add(char a , char b)
char add::$0
char add::a
char add::a#0
char add::a#1
char add::b
char add::b#0
char add::b#1
char add::return
char add::return#0
char add::return#1
char add::return#2
char add::return#3
char add::return#4
void main()
char main::$0
char main::$1
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::$0
char plus::a
char plus::a#0
char plus::a#1
char plus::a#2
char plus::b
char plus::b#0
char plus::b#1
char plus::b#2
char plus::return
char plus::return#0
char plus::return#1
char plus::return#2
char plus::return#3
char plus::return#4
char plus::return#5
char plus::return#6
Adding number conversion cast (unumber) 7 in plus::b#0 = 7
Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
Adding number conversion cast (unumber) 6 in plus::b#1 = 6
Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast plus::b#0 = (unumber)7
Inlining cast plus::b#1 = (unumber)6
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (char *) 1024
Simplifying constant integer cast 7
Simplifying constant integer cast 0
Simplifying constant integer cast 6
Simplifying constant integer cast 1
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (char) 7
Finalized unsigned number type (char) 0
Finalized unsigned number type (char) 6
Finalized unsigned number type (char) 1
Successful SSA optimization PassNFinalizeNumberTypeConversions
Alias plus::return#0 = plus::return#4
Alias plus::return#1 = plus::return#5
Alias add::return#0 = add::return#3
Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
Alias add::return#1 = add::$0 add::return#4 add::return#2
Successful SSA optimization Pass2AliasElimination
Identical Phi Values add::a#1 add::a#0
Identical Phi Values add::b#1 add::b#0
Successful SSA optimization Pass2IdenticalPhiElimination
Constant plus::a#0 = '0'
Constant plus::b#0 = 7
Constant plus::a#1 = '1'
Constant plus::b#1 = 6
Successful SSA optimization Pass2ConstantIdentification
Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
Successful SSA optimization PassNSimplifyExpressionWithZero
Removing unused procedure __start
Removing unused procedure block __start
Removing unused procedure block __start::@1
Removing unused procedure block __start::@return
Successful SSA optimization PassNEliminateEmptyStart
Inlining constant with var siblings plus::a#0
Inlining constant with var siblings plus::b#0
Inlining constant with var siblings plus::a#1
Inlining constant with var siblings plus::b#1
Constant inlined plus::b#1 = 6
Constant inlined plus::b#0 = 7
Constant inlined plus::a#1 = '1'
Constant inlined plus::a#0 = '0'
Successful SSA optimization Pass2ConstantInlining
Consolidated array index constant in *(SCREEN+1)
Successful SSA optimization Pass2ConstantAdditionElimination
Adding NOP phi() at start of main
CALL GRAPH
Calls in [main] to plus:1 plus:5
Calls in [plus] to add:13
Created 2 initial phi equivalence classes
Coalesced down to 2 phi equivalence classes
Adding NOP phi() at start of main
FINAL CONTROL FLOW GRAPH
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] add::a#0 = plus::a#2
[12] add::b#0 = plus::b#2
[13] call add
[14] add::return#0 = add::return#1
to:plus::@1
plus::@1: scope:[plus] from plus
[15] plus::return#2 = add::return#0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
[16] return
to:@return
__bank(cx16_ram, 2) char add(char a , char b)
add: scope:[add] from plus
[17] add::return#1 = add::a#0 + add::b#0
to:add::@return
add::@return: scope:[add] from add
[18] return
to:@return
VARIABLE REGISTER WEIGHTS
__bank(cx16_ram, 2) char add(char a , char b)
char add::a
char add::a#0 // 56.0
char add::b
char add::b#0 // 112.0
char add::return
char add::return#0 // 22.0
char add::return#1 // 37.33333333333333
void main()
char main::$0 // 4.0
char main::$1 // 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // 11.0
char plus::b
char plus::b#2 // 5.5
char plus::return
char plus::return#0 // 4.0
char plus::return#1 // 4.0
char plus::return#2 // 3.75
Initial phi equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
Added variable main::$0 to live range equivalence class [ main::$0 ]
Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
Added variable main::$1 to live range equivalence class [ main::$1 ]
Added variable add::a#0 to live range equivalence class [ add::a#0 ]
Added variable add::b#0 to live range equivalence class [ add::b#0 ]
Added variable add::return#0 to live range equivalence class [ add::return#0 ]
Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
Added variable add::return#1 to live range equivalence class [ add::return#1 ]
Complete equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
[ plus::return#0 ]
[ main::$0 ]
[ plus::return#1 ]
[ main::$1 ]
[ add::a#0 ]
[ add::b#0 ]
[ add::return#0 ]
[ plus::return#2 ]
[ add::return#1 ]
Allocated zp[1]:2 [ add::b#0 ]
Allocated zp[1]:3 [ add::a#0 ]
Allocated zp[1]:4 [ add::return#1 ]
Allocated zp[1]:5 [ add::return#0 ]
Allocated zp[1]:6 [ plus::a#2 ]
Allocated zp[1]:7 [ plus::b#2 ]
Allocated zp[1]:8 [ plus::return#0 ]
Allocated zp[1]:9 [ main::$0 ]
Allocated zp[1]:10 [ plus::return#1 ]
Allocated zp[1]:11 [ main::$1 ]
Allocated zp[1]:12 [ plus::return#2 ]
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ]
Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y ,
Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y ,
REGISTER UPLIFT SCOPES
Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ]
Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ]
Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ]
Uplift Scope []
Uplifting [add] best 189 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte a [ add::return#1 ] reg byte a [ add::return#0 ]
Limited combination testing to 100 combinations of 256 possible.
Uplifting [plus] best 161 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ]
Limited combination testing to 100 combinations of 768 possible.
Uplifting [main] best 149 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
Uplifting [] best 149 combination
Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ]
Uplifting [add] best 149 combination zp[1]:3 [ add::a#0 ]
Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ]
Uplifting [plus] best 146 combination reg byte x [ plus::return#2 ]
Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ]
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
/**
* @file call-banked-phi-case-5-far-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #5.
* Implementation using the __bank() directive.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-5-far-0.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
plus_from_main:
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
ldx #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
txa
jmp __b1
// main::@1
__b1:
// [3] main::$0 = plus::return#0
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
plus_from___b1:
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
ldx #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
txa
jmp __b2
// main::@2
__b2:
// [7] main::$1 = plus::return#1
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// close call
sta SCREEN+1
jmp __breturn
// main::@return
__breturn:
// [9] return
rts
}
.segment RAM_Bank1
// plus
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
sta.z add.a
// [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
txa
// [13] call add -- call_phi_far_cx16_ram
jsr $ff6e
.byte <add
.byte >add
.byte 2
// [14] add::return#0 = add::return#1
jmp __b1
// plus::@1
__b1:
// [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
tax
jmp __breturn
// plus::@return
__breturn:
// [16] return
rts
}
.segment RAM_Bank2
// add
// __register(A) char add(__zp(2) char a, __register(A) char b)
// __bank(cx16_ram, 2)
add: {
.label a = 2
// [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
clc
adc.z a
jmp __breturn
// add::@return
__breturn:
// [18] return
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __b1
Removing instruction jmp __b2
Removing instruction jmp __breturn
Removing instruction jmp __b1
Removing instruction jmp __breturn
Removing instruction jmp __breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction plus_from_main:
Removing instruction __b1:
Removing instruction plus_from___b1:
Removing instruction __b2:
Removing instruction __breturn:
Removing instruction __b1:
Removing instruction __breturn:
Removing instruction __breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
__constant char * const SCREEN = (char *) 1024
__bank(cx16_ram, 2) char add(char a , char b)
char add::a
char add::a#0 // a zp[1]:2 56.0
char add::b
char add::b#0 // reg byte a 112.0
char add::return
char add::return#0 // reg byte a 22.0
char add::return#1 // reg byte a 37.33333333333333
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte a 11.0
char plus::b
char plus::b#2 // reg byte x 5.5
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte x 3.75
reg byte a [ plus::a#2 ]
reg byte x [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
zp[1]:2 [ add::a#0 ]
reg byte a [ add::b#0 ]
reg byte a [ add::return#0 ]
reg byte x [ plus::return#2 ]
reg byte a [ add::return#1 ]
FINAL ASSEMBLER
Score: 128
// File Comments
/**
* @file call-banked-phi-case-5-far-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #5.
* Implementation using the __bank() directive.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-5-far-0.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// plus('0', 7)
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
ldx #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('0', 7)
// [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
txa
// main::@1
// [3] main::$0 = plus::return#0
// SCREEN[0] = plus('0', 7)
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// plus('1', 6)
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
ldx #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('1', 6)
// [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
txa
// main::@2
// [7] main::$1 = plus::return#1
// SCREEN[1] = plus('1', 6)
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// close call
sta SCREEN+1
// main::@return
// }
// [9] return
rts
}
.segment RAM_Bank1
// plus
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// add(a, b)
// [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
sta.z add.a
// [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
txa
// [13] call add -- call_phi_far_cx16_ram
jsr $ff6e
.byte <add
.byte >add
.byte 2
// [14] add::return#0 = add::return#1
// plus::@1
// [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
tax
// plus::@return
// }
// [16] return
rts
}
.segment RAM_Bank2
// add
// __register(A) char add(__zp(2) char a, __register(A) char b)
// __bank(cx16_ram, 2)
add: {
.label a = 2
// a+b
// [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
clc
adc.z a
// add::@return
// }
// [18] return
rts
}
// File Data

View File

@ -0,0 +1,33 @@
__constant char * const SCREEN = (char *) 1024
__bank(cx16_ram, 2) char add(char a , char b)
char add::a
char add::a#0 // a zp[1]:2 56.0
char add::b
char add::b#0 // reg byte a 112.0
char add::return
char add::return#0 // reg byte a 22.0
char add::return#1 // reg byte a 37.33333333333333
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte a 11.0
char plus::b
char plus::b#2 // reg byte x 5.5
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte x 3.75
reg byte a [ plus::a#2 ]
reg byte x [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
zp[1]:2 [ add::a#0 ]
reg byte a [ add::b#0 ]
reg byte a [ add::return#0 ]
reg byte x [ plus::return#2 ]
reg byte a [ add::return#1 ]

View File

@ -0,0 +1,110 @@
/**
* @file call-banked-phi-case-5-far-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #5.
* Implementation using the #pragma bank and nobank directives.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
.file [name="call-banked-phi-case-5-far-1.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
.label SCREEN = $400
.segment Code
main: {
// plus('0', 7)
ldx #7
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('0', 7)
txa
// SCREEN[0] = plus('0', 7)
sta SCREEN
// plus('1', 6)
ldx #6
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('1', 6)
txa
// SCREEN[1] = plus('1', 6)
// close call
sta SCREEN+1
// }
rts
}
.segment RAM_Bank1
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// add(a, b)
sta.z add.a
txa
jsr $ff6e
.byte <add
.byte >add
.byte 2
tax
// }
rts
}
.segment RAM_Bank2
// __register(A) char add(__zp(2) char a, __register(A) char b)
// __bank(cx16_ram, 2)
add: {
.label a = 2
// a+b
clc
adc.z a
// }
rts
}

View File

@ -0,0 +1,44 @@
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] add::a#0 = plus::a#2
[12] add::b#0 = plus::b#2
[13] call add
[14] add::return#0 = add::return#1
to:plus::@1
plus::@1: scope:[plus] from plus
[15] plus::return#2 = add::return#0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
[16] return
to:@return
__bank(cx16_ram, 2) char add(char a , char b)
add: scope:[add] from plus
[17] add::return#1 = add::a#0 + add::b#0
to:add::@return
add::@return: scope:[add] from add
[18] return
to:@return

View File

@ -0,0 +1,668 @@
Loading link script "call-banked-phi.ld"
CONTROL FLOW GRAPH SSA
void main()
main: scope:[main] from __start
plus::a#0 = '0'
plus::b#0 = 7
call plus
plus::return#0 = plus::return#3
to:main::@1
main::@1: scope:[main] from main
plus::return#4 = phi( main/plus::return#0 )
main::$0 = plus::return#4
SCREEN[0] = main::$0
plus::a#1 = '1'
plus::b#1 = 6
call plus
plus::return#1 = plus::return#3
to:main::@2
main::@2: scope:[main] from main::@1
plus::return#5 = phi( main::@1/plus::return#1 )
main::$1 = plus::return#5
SCREEN[1] = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
add::a#0 = plus::a#2
add::b#0 = plus::b#2
call add
add::return#0 = add::return#2
to:plus::@1
plus::@1: scope:[plus] from plus
add::return#3 = phi( plus/add::return#0 )
plus::$0 = add::return#3
plus::return#2 = plus::$0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
plus::return#6 = phi( plus::@1/plus::return#2 )
plus::return#3 = plus::return#6
return
to:@return
__bank(cx16_ram, 2) char add(char a , char b)
add: scope:[add] from plus
add::b#1 = phi( plus/add::b#0 )
add::a#1 = phi( plus/add::a#0 )
add::$0 = add::a#1 + add::b#1
add::return#1 = add::$0
to:add::@return
add::@return: scope:[add] from add
add::return#4 = phi( add/add::return#1 )
add::return#2 = add::return#4
return
to:@return
void __start()
__start: scope:[__start] from
call main
to:__start::@1
__start::@1: scope:[__start] from __start
to:__start::@return
__start::@return: scope:[__start] from __start::@1
return
to:@return
SYMBOL TABLE SSA
__constant char * const SCREEN = (char *)$400
void __start()
__bank(cx16_ram, 2) char add(char a , char b)
char add::$0
char add::a
char add::a#0
char add::a#1
char add::b
char add::b#0
char add::b#1
char add::return
char add::return#0
char add::return#1
char add::return#2
char add::return#3
char add::return#4
void main()
char main::$0
char main::$1
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::$0
char plus::a
char plus::a#0
char plus::a#1
char plus::a#2
char plus::b
char plus::b#0
char plus::b#1
char plus::b#2
char plus::return
char plus::return#0
char plus::return#1
char plus::return#2
char plus::return#3
char plus::return#4
char plus::return#5
char plus::return#6
Adding number conversion cast (unumber) 7 in plus::b#0 = 7
Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
Adding number conversion cast (unumber) 6 in plus::b#1 = 6
Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast plus::b#0 = (unumber)7
Inlining cast plus::b#1 = (unumber)6
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (char *) 1024
Simplifying constant integer cast 7
Simplifying constant integer cast 0
Simplifying constant integer cast 6
Simplifying constant integer cast 1
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (char) 7
Finalized unsigned number type (char) 0
Finalized unsigned number type (char) 6
Finalized unsigned number type (char) 1
Successful SSA optimization PassNFinalizeNumberTypeConversions
Alias plus::return#0 = plus::return#4
Alias plus::return#1 = plus::return#5
Alias add::return#0 = add::return#3
Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
Alias add::return#1 = add::$0 add::return#4 add::return#2
Successful SSA optimization Pass2AliasElimination
Identical Phi Values add::a#1 add::a#0
Identical Phi Values add::b#1 add::b#0
Successful SSA optimization Pass2IdenticalPhiElimination
Constant plus::a#0 = '0'
Constant plus::b#0 = 7
Constant plus::a#1 = '1'
Constant plus::b#1 = 6
Successful SSA optimization Pass2ConstantIdentification
Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
Successful SSA optimization PassNSimplifyExpressionWithZero
Removing unused procedure __start
Removing unused procedure block __start
Removing unused procedure block __start::@1
Removing unused procedure block __start::@return
Successful SSA optimization PassNEliminateEmptyStart
Inlining constant with var siblings plus::a#0
Inlining constant with var siblings plus::b#0
Inlining constant with var siblings plus::a#1
Inlining constant with var siblings plus::b#1
Constant inlined plus::b#1 = 6
Constant inlined plus::b#0 = 7
Constant inlined plus::a#1 = '1'
Constant inlined plus::a#0 = '0'
Successful SSA optimization Pass2ConstantInlining
Consolidated array index constant in *(SCREEN+1)
Successful SSA optimization Pass2ConstantAdditionElimination
Adding NOP phi() at start of main
CALL GRAPH
Calls in [main] to plus:1 plus:5
Calls in [plus] to add:13
Created 2 initial phi equivalence classes
Coalesced down to 2 phi equivalence classes
Adding NOP phi() at start of main
FINAL CONTROL FLOW GRAPH
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] add::a#0 = plus::a#2
[12] add::b#0 = plus::b#2
[13] call add
[14] add::return#0 = add::return#1
to:plus::@1
plus::@1: scope:[plus] from plus
[15] plus::return#2 = add::return#0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
[16] return
to:@return
__bank(cx16_ram, 2) char add(char a , char b)
add: scope:[add] from plus
[17] add::return#1 = add::a#0 + add::b#0
to:add::@return
add::@return: scope:[add] from add
[18] return
to:@return
VARIABLE REGISTER WEIGHTS
__bank(cx16_ram, 2) char add(char a , char b)
char add::a
char add::a#0 // 56.0
char add::b
char add::b#0 // 112.0
char add::return
char add::return#0 // 22.0
char add::return#1 // 37.33333333333333
void main()
char main::$0 // 4.0
char main::$1 // 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // 11.0
char plus::b
char plus::b#2 // 5.5
char plus::return
char plus::return#0 // 4.0
char plus::return#1 // 4.0
char plus::return#2 // 3.75
Initial phi equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
Added variable main::$0 to live range equivalence class [ main::$0 ]
Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
Added variable main::$1 to live range equivalence class [ main::$1 ]
Added variable add::a#0 to live range equivalence class [ add::a#0 ]
Added variable add::b#0 to live range equivalence class [ add::b#0 ]
Added variable add::return#0 to live range equivalence class [ add::return#0 ]
Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
Added variable add::return#1 to live range equivalence class [ add::return#1 ]
Complete equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
[ plus::return#0 ]
[ main::$0 ]
[ plus::return#1 ]
[ main::$1 ]
[ add::a#0 ]
[ add::b#0 ]
[ add::return#0 ]
[ plus::return#2 ]
[ add::return#1 ]
Allocated zp[1]:2 [ add::b#0 ]
Allocated zp[1]:3 [ add::a#0 ]
Allocated zp[1]:4 [ add::return#1 ]
Allocated zp[1]:5 [ add::return#0 ]
Allocated zp[1]:6 [ plus::a#2 ]
Allocated zp[1]:7 [ plus::b#2 ]
Allocated zp[1]:8 [ plus::return#0 ]
Allocated zp[1]:9 [ main::$0 ]
Allocated zp[1]:10 [ plus::return#1 ]
Allocated zp[1]:11 [ main::$1 ]
Allocated zp[1]:12 [ plus::return#2 ]
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ]
Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y ,
Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y ,
REGISTER UPLIFT SCOPES
Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ]
Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ]
Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ]
Uplift Scope []
Uplifting [add] best 189 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte a [ add::return#1 ] reg byte a [ add::return#0 ]
Limited combination testing to 100 combinations of 256 possible.
Uplifting [plus] best 161 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ]
Limited combination testing to 100 combinations of 768 possible.
Uplifting [main] best 149 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
Uplifting [] best 149 combination
Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ]
Uplifting [add] best 149 combination zp[1]:3 [ add::a#0 ]
Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ]
Uplifting [plus] best 146 combination reg byte x [ plus::return#2 ]
Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ]
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
/**
* @file call-banked-phi-case-5-far-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #5.
* Implementation using the #pragma bank and nobank directives.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-5-far-1.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
plus_from_main:
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
ldx #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
txa
jmp __b1
// main::@1
__b1:
// [3] main::$0 = plus::return#0
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
plus_from___b1:
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
ldx #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
txa
jmp __b2
// main::@2
__b2:
// [7] main::$1 = plus::return#1
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// close call
sta SCREEN+1
jmp __breturn
// main::@return
__breturn:
// [9] return
rts
}
.segment RAM_Bank1
// plus
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
sta.z add.a
// [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
txa
// [13] call add -- call_phi_far_cx16_ram
jsr $ff6e
.byte <add
.byte >add
.byte 2
// [14] add::return#0 = add::return#1
jmp __b1
// plus::@1
__b1:
// [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
tax
jmp __breturn
// plus::@return
__breturn:
// [16] return
rts
}
.segment RAM_Bank2
// add
// __register(A) char add(__zp(2) char a, __register(A) char b)
// __bank(cx16_ram, 2)
add: {
.label a = 2
// [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
clc
adc.z a
jmp __breturn
// add::@return
__breturn:
// [18] return
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __b1
Removing instruction jmp __b2
Removing instruction jmp __breturn
Removing instruction jmp __b1
Removing instruction jmp __breturn
Removing instruction jmp __breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction plus_from_main:
Removing instruction __b1:
Removing instruction plus_from___b1:
Removing instruction __b2:
Removing instruction __breturn:
Removing instruction __b1:
Removing instruction __breturn:
Removing instruction __breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
__constant char * const SCREEN = (char *) 1024
__bank(cx16_ram, 2) char add(char a , char b)
char add::a
char add::a#0 // a zp[1]:2 56.0
char add::b
char add::b#0 // reg byte a 112.0
char add::return
char add::return#0 // reg byte a 22.0
char add::return#1 // reg byte a 37.33333333333333
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte a 11.0
char plus::b
char plus::b#2 // reg byte x 5.5
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte x 3.75
reg byte a [ plus::a#2 ]
reg byte x [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
zp[1]:2 [ add::a#0 ]
reg byte a [ add::b#0 ]
reg byte a [ add::return#0 ]
reg byte x [ plus::return#2 ]
reg byte a [ add::return#1 ]
FINAL ASSEMBLER
Score: 128
// File Comments
/**
* @file call-banked-phi-case-5-far-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #5.
* Implementation using the #pragma bank and nobank directives.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-5-far-1.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// plus('0', 7)
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
ldx #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('0', 7)
// [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
txa
// main::@1
// [3] main::$0 = plus::return#0
// SCREEN[0] = plus('0', 7)
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// plus('1', 6)
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
ldx #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('1', 6)
// [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
txa
// main::@2
// [7] main::$1 = plus::return#1
// SCREEN[1] = plus('1', 6)
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// close call
sta SCREEN+1
// main::@return
// }
// [9] return
rts
}
.segment RAM_Bank1
// plus
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// add(a, b)
// [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
sta.z add.a
// [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
txa
// [13] call add -- call_phi_far_cx16_ram
jsr $ff6e
.byte <add
.byte >add
.byte 2
// [14] add::return#0 = add::return#1
// plus::@1
// [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
tax
// plus::@return
// }
// [16] return
rts
}
.segment RAM_Bank2
// add
// __register(A) char add(__zp(2) char a, __register(A) char b)
// __bank(cx16_ram, 2)
add: {
.label a = 2
// a+b
// [17] add::return#1 = add::a#0 + add::b#0 -- vbuaa=vbuz1_plus_vbuaa
clc
adc.z a
// add::@return
// }
// [18] return
rts
}
// File Data

View File

@ -0,0 +1,33 @@
__constant char * const SCREEN = (char *) 1024
__bank(cx16_ram, 2) char add(char a , char b)
char add::a
char add::a#0 // a zp[1]:2 56.0
char add::b
char add::b#0 // reg byte a 112.0
char add::return
char add::return#0 // reg byte a 22.0
char add::return#1 // reg byte a 37.33333333333333
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte a 11.0
char plus::b
char plus::b#2 // reg byte x 5.5
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte x 3.75
reg byte a [ plus::a#2 ]
reg byte x [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
zp[1]:2 [ add::a#0 ]
reg byte a [ add::b#0 ]
reg byte a [ add::return#0 ]
reg byte x [ plus::return#2 ]
reg byte a [ add::return#1 ]

View File

@ -0,0 +1,119 @@
/**
* @file call-banked-phi-case-6-close-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #6.
* Implementation using the __bank() directive.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
.file [name="call-banked-phi-case-6-close-0.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
.label SCREEN = $400
.segment Code
main: {
// plus('0', 7)
ldx #7
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('0', 7)
txa
// SCREEN[0] = plus('0', 7)
sta SCREEN
// plus('1', 6)
ldx #6
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('1', 6)
txa
// SCREEN[1] = plus('1', 6)
// close call
sta SCREEN+1
// }
rts
}
.segment RAM_Bank1
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// add(a, b)
sta.z add.a
txa
sta.z $ff
lda.z 1
pha
lda #1
sta.z 1
lda.z $ff
jsr add
sta.z $ff
pla
sta.z 1
lda.z $ff
txa
tax
// }
rts
}
.segment ROM_Bank1
// __register(X) char add(__zp(2) char a, __register(A) char b)
// __bank(cx16_rom, 1)
add: {
.label a = 2
// a+b
clc
adc.z a
tax
// }
rts
}

View File

@ -0,0 +1,44 @@
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] add::a#0 = plus::a#2
[12] add::b#0 = plus::b#2
[13] call add
[14] add::return#0 = add::return#1
to:plus::@1
plus::@1: scope:[plus] from plus
[15] plus::return#2 = add::return#0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
[16] return
to:@return
__bank(cx16_rom, 1) char add(char a , char b)
add: scope:[add] from plus
[17] add::return#1 = add::a#0 + add::b#0
to:add::@return
add::@return: scope:[add] from add
[18] return
to:@return

View File

@ -0,0 +1,689 @@
Loading link script "call-banked-phi.ld"
CONTROL FLOW GRAPH SSA
void main()
main: scope:[main] from __start
plus::a#0 = '0'
plus::b#0 = 7
call plus
plus::return#0 = plus::return#3
to:main::@1
main::@1: scope:[main] from main
plus::return#4 = phi( main/plus::return#0 )
main::$0 = plus::return#4
SCREEN[0] = main::$0
plus::a#1 = '1'
plus::b#1 = 6
call plus
plus::return#1 = plus::return#3
to:main::@2
main::@2: scope:[main] from main::@1
plus::return#5 = phi( main::@1/plus::return#1 )
main::$1 = plus::return#5
SCREEN[1] = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
add::a#0 = plus::a#2
add::b#0 = plus::b#2
call add
add::return#0 = add::return#2
to:plus::@1
plus::@1: scope:[plus] from plus
add::return#3 = phi( plus/add::return#0 )
plus::$0 = add::return#3
plus::return#2 = plus::$0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
plus::return#6 = phi( plus::@1/plus::return#2 )
plus::return#3 = plus::return#6
return
to:@return
__bank(cx16_rom, 1) char add(char a , char b)
add: scope:[add] from plus
add::b#1 = phi( plus/add::b#0 )
add::a#1 = phi( plus/add::a#0 )
add::$0 = add::a#1 + add::b#1
add::return#1 = add::$0
to:add::@return
add::@return: scope:[add] from add
add::return#4 = phi( add/add::return#1 )
add::return#2 = add::return#4
return
to:@return
void __start()
__start: scope:[__start] from
call main
to:__start::@1
__start::@1: scope:[__start] from __start
to:__start::@return
__start::@return: scope:[__start] from __start::@1
return
to:@return
SYMBOL TABLE SSA
__constant char * const SCREEN = (char *)$400
void __start()
__bank(cx16_rom, 1) char add(char a , char b)
char add::$0
char add::a
char add::a#0
char add::a#1
char add::b
char add::b#0
char add::b#1
char add::return
char add::return#0
char add::return#1
char add::return#2
char add::return#3
char add::return#4
void main()
char main::$0
char main::$1
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::$0
char plus::a
char plus::a#0
char plus::a#1
char plus::a#2
char plus::b
char plus::b#0
char plus::b#1
char plus::b#2
char plus::return
char plus::return#0
char plus::return#1
char plus::return#2
char plus::return#3
char plus::return#4
char plus::return#5
char plus::return#6
Adding number conversion cast (unumber) 7 in plus::b#0 = 7
Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
Adding number conversion cast (unumber) 6 in plus::b#1 = 6
Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast plus::b#0 = (unumber)7
Inlining cast plus::b#1 = (unumber)6
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (char *) 1024
Simplifying constant integer cast 7
Simplifying constant integer cast 0
Simplifying constant integer cast 6
Simplifying constant integer cast 1
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (char) 7
Finalized unsigned number type (char) 0
Finalized unsigned number type (char) 6
Finalized unsigned number type (char) 1
Successful SSA optimization PassNFinalizeNumberTypeConversions
Alias plus::return#0 = plus::return#4
Alias plus::return#1 = plus::return#5
Alias add::return#0 = add::return#3
Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
Alias add::return#1 = add::$0 add::return#4 add::return#2
Successful SSA optimization Pass2AliasElimination
Identical Phi Values add::a#1 add::a#0
Identical Phi Values add::b#1 add::b#0
Successful SSA optimization Pass2IdenticalPhiElimination
Constant plus::a#0 = '0'
Constant plus::b#0 = 7
Constant plus::a#1 = '1'
Constant plus::b#1 = 6
Successful SSA optimization Pass2ConstantIdentification
Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
Successful SSA optimization PassNSimplifyExpressionWithZero
Removing unused procedure __start
Removing unused procedure block __start
Removing unused procedure block __start::@1
Removing unused procedure block __start::@return
Successful SSA optimization PassNEliminateEmptyStart
Inlining constant with var siblings plus::a#0
Inlining constant with var siblings plus::b#0
Inlining constant with var siblings plus::a#1
Inlining constant with var siblings plus::b#1
Constant inlined plus::b#1 = 6
Constant inlined plus::b#0 = 7
Constant inlined plus::a#1 = '1'
Constant inlined plus::a#0 = '0'
Successful SSA optimization Pass2ConstantInlining
Consolidated array index constant in *(SCREEN+1)
Successful SSA optimization Pass2ConstantAdditionElimination
Adding NOP phi() at start of main
CALL GRAPH
Calls in [main] to plus:1 plus:5
Calls in [plus] to add:13
Created 2 initial phi equivalence classes
Coalesced down to 2 phi equivalence classes
Adding NOP phi() at start of main
FINAL CONTROL FLOW GRAPH
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] add::a#0 = plus::a#2
[12] add::b#0 = plus::b#2
[13] call add
[14] add::return#0 = add::return#1
to:plus::@1
plus::@1: scope:[plus] from plus
[15] plus::return#2 = add::return#0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
[16] return
to:@return
__bank(cx16_rom, 1) char add(char a , char b)
add: scope:[add] from plus
[17] add::return#1 = add::a#0 + add::b#0
to:add::@return
add::@return: scope:[add] from add
[18] return
to:@return
VARIABLE REGISTER WEIGHTS
__bank(cx16_rom, 1) char add(char a , char b)
char add::a
char add::a#0 // 56.0
char add::b
char add::b#0 // 112.0
char add::return
char add::return#0 // 22.0
char add::return#1 // 37.33333333333333
void main()
char main::$0 // 4.0
char main::$1 // 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // 11.0
char plus::b
char plus::b#2 // 5.5
char plus::return
char plus::return#0 // 4.0
char plus::return#1 // 4.0
char plus::return#2 // 3.75
Initial phi equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
Added variable main::$0 to live range equivalence class [ main::$0 ]
Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
Added variable main::$1 to live range equivalence class [ main::$1 ]
Added variable add::a#0 to live range equivalence class [ add::a#0 ]
Added variable add::b#0 to live range equivalence class [ add::b#0 ]
Added variable add::return#0 to live range equivalence class [ add::return#0 ]
Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
Added variable add::return#1 to live range equivalence class [ add::return#1 ]
Complete equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
[ plus::return#0 ]
[ main::$0 ]
[ plus::return#1 ]
[ main::$1 ]
[ add::a#0 ]
[ add::b#0 ]
[ add::return#0 ]
[ plus::return#2 ]
[ add::return#1 ]
Allocated zp[1]:2 [ add::b#0 ]
Allocated zp[1]:3 [ add::a#0 ]
Allocated zp[1]:4 [ add::return#1 ]
Allocated zp[1]:5 [ add::return#0 ]
Allocated zp[1]:6 [ plus::a#2 ]
Allocated zp[1]:7 [ plus::b#2 ]
Allocated zp[1]:8 [ plus::return#0 ]
Allocated zp[1]:9 [ main::$0 ]
Allocated zp[1]:10 [ plus::return#1 ]
Allocated zp[1]:11 [ main::$1 ]
Allocated zp[1]:12 [ plus::return#2 ]
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ]
Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [13] call add [ add::return#1 ] ( plus:1 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
Removing always clobbered register reg byte a as potential for zp[1]:4 [ add::return#1 ]
Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [13] call add [ add::return#1 ] ( plus:1 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y ,
Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte x , reg byte y ,
REGISTER UPLIFT SCOPES
Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ]
Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ]
Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ]
Uplift Scope []
Uplifting [add] best 223 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte x [ add::return#1 ] reg byte a [ add::return#0 ]
Limited combination testing to 100 combinations of 192 possible.
Uplifting [plus] best 195 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ]
Limited combination testing to 100 combinations of 768 possible.
Uplifting [main] best 183 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
Uplifting [] best 183 combination
Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ]
Uplifting [add] best 183 combination zp[1]:3 [ add::a#0 ]
Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ]
Uplifting [plus] best 180 combination reg byte x [ plus::return#2 ]
Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ]
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
/**
* @file call-banked-phi-case-6-close-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #6.
* Implementation using the __bank() directive.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-6-close-0.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
plus_from_main:
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
ldx #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
txa
jmp __b1
// main::@1
__b1:
// [3] main::$0 = plus::return#0
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
plus_from___b1:
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
ldx #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
txa
jmp __b2
// main::@2
__b2:
// [7] main::$1 = plus::return#1
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// close call
sta SCREEN+1
jmp __breturn
// main::@return
__breturn:
// [9] return
rts
}
.segment RAM_Bank1
// plus
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
sta.z add.a
// [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
txa
// [13] call add -- call_phi_close_cx16_rom
sta.z $ff
lda.z 1
pha
lda #1
sta.z 1
lda.z $ff
jsr add
sta.z $ff
pla
sta.z 1
lda.z $ff
// [14] add::return#0 = add::return#1 -- vbuaa=vbuxx
txa
jmp __b1
// plus::@1
__b1:
// [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
tax
jmp __breturn
// plus::@return
__breturn:
// [16] return
rts
}
.segment ROM_Bank1
// add
// __register(X) char add(__zp(2) char a, __register(A) char b)
// __bank(cx16_rom, 1)
add: {
.label a = 2
// [17] add::return#1 = add::a#0 + add::b#0 -- vbuxx=vbuz1_plus_vbuaa
clc
adc.z a
tax
jmp __breturn
// add::@return
__breturn:
// [18] return
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __b1
Removing instruction jmp __b2
Removing instruction jmp __breturn
Removing instruction jmp __b1
Removing instruction jmp __breturn
Removing instruction jmp __breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction plus_from_main:
Removing instruction __b1:
Removing instruction plus_from___b1:
Removing instruction __b2:
Removing instruction __breturn:
Removing instruction __b1:
Removing instruction __breturn:
Removing instruction __breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
__constant char * const SCREEN = (char *) 1024
__bank(cx16_rom, 1) char add(char a , char b)
char add::a
char add::a#0 // a zp[1]:2 56.0
char add::b
char add::b#0 // reg byte a 112.0
char add::return
char add::return#0 // reg byte a 22.0
char add::return#1 // reg byte x 37.33333333333333
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte a 11.0
char plus::b
char plus::b#2 // reg byte x 5.5
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte x 3.75
reg byte a [ plus::a#2 ]
reg byte x [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
zp[1]:2 [ add::a#0 ]
reg byte a [ add::b#0 ]
reg byte a [ add::return#0 ]
reg byte x [ plus::return#2 ]
reg byte x [ add::return#1 ]
FINAL ASSEMBLER
Score: 162
// File Comments
/**
* @file call-banked-phi-case-6-close-0.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #6.
* Implementation using the __bank() directive.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-6-close-0.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// plus('0', 7)
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
ldx #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('0', 7)
// [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
txa
// main::@1
// [3] main::$0 = plus::return#0
// SCREEN[0] = plus('0', 7)
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// plus('1', 6)
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
ldx #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('1', 6)
// [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
txa
// main::@2
// [7] main::$1 = plus::return#1
// SCREEN[1] = plus('1', 6)
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// close call
sta SCREEN+1
// main::@return
// }
// [9] return
rts
}
.segment RAM_Bank1
// plus
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// add(a, b)
// [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
sta.z add.a
// [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
txa
// [13] call add -- call_phi_close_cx16_rom
sta.z $ff
lda.z 1
pha
lda #1
sta.z 1
lda.z $ff
jsr add
sta.z $ff
pla
sta.z 1
lda.z $ff
// [14] add::return#0 = add::return#1 -- vbuaa=vbuxx
txa
// plus::@1
// [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
tax
// plus::@return
// }
// [16] return
rts
}
.segment ROM_Bank1
// add
// __register(X) char add(__zp(2) char a, __register(A) char b)
// __bank(cx16_rom, 1)
add: {
.label a = 2
// a+b
// [17] add::return#1 = add::a#0 + add::b#0 -- vbuxx=vbuz1_plus_vbuaa
clc
adc.z a
tax
// add::@return
// }
// [18] return
rts
}
// File Data

View File

@ -0,0 +1,33 @@
__constant char * const SCREEN = (char *) 1024
__bank(cx16_rom, 1) char add(char a , char b)
char add::a
char add::a#0 // a zp[1]:2 56.0
char add::b
char add::b#0 // reg byte a 112.0
char add::return
char add::return#0 // reg byte a 22.0
char add::return#1 // reg byte x 37.33333333333333
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte a 11.0
char plus::b
char plus::b#2 // reg byte x 5.5
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte x 3.75
reg byte a [ plus::a#2 ]
reg byte x [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
zp[1]:2 [ add::a#0 ]
reg byte a [ add::b#0 ]
reg byte a [ add::return#0 ]
reg byte x [ plus::return#2 ]
reg byte x [ add::return#1 ]

View File

@ -0,0 +1,119 @@
/**
* @file call-banked-phi-case-6-close-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #6.
* Implementation using the #pragma bank and nobank directives.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
.file [name="call-banked-phi-case-6-close-1.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
.label SCREEN = $400
.segment Code
main: {
// plus('0', 7)
ldx #7
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('0', 7)
txa
// SCREEN[0] = plus('0', 7)
sta SCREEN
// plus('1', 6)
ldx #6
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('1', 6)
txa
// SCREEN[1] = plus('1', 6)
// close call
sta SCREEN+1
// }
rts
}
.segment RAM_Bank1
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// add(a, b)
sta.z add.a
txa
sta.z $ff
lda.z 1
pha
lda #1
sta.z 1
lda.z $ff
jsr add
sta.z $ff
pla
sta.z 1
lda.z $ff
txa
tax
// }
rts
}
.segment ROM_Bank1
// __register(X) char add(__zp(2) char a, __register(A) char b)
// __bank(cx16_rom, 1)
add: {
.label a = 2
// a+b
clc
adc.z a
tax
// }
rts
}

View File

@ -0,0 +1,44 @@
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] add::a#0 = plus::a#2
[12] add::b#0 = plus::b#2
[13] call add
[14] add::return#0 = add::return#1
to:plus::@1
plus::@1: scope:[plus] from plus
[15] plus::return#2 = add::return#0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
[16] return
to:@return
__bank(cx16_rom, 1) char add(char a , char b)
add: scope:[add] from plus
[17] add::return#1 = add::a#0 + add::b#0
to:add::@return
add::@return: scope:[add] from add
[18] return
to:@return

View File

@ -0,0 +1,689 @@
Loading link script "call-banked-phi.ld"
CONTROL FLOW GRAPH SSA
void main()
main: scope:[main] from __start
plus::a#0 = '0'
plus::b#0 = 7
call plus
plus::return#0 = plus::return#3
to:main::@1
main::@1: scope:[main] from main
plus::return#4 = phi( main/plus::return#0 )
main::$0 = plus::return#4
SCREEN[0] = main::$0
plus::a#1 = '1'
plus::b#1 = 6
call plus
plus::return#1 = plus::return#3
to:main::@2
main::@2: scope:[main] from main::@1
plus::return#5 = phi( main::@1/plus::return#1 )
main::$1 = plus::return#5
SCREEN[1] = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
plus::b#2 = phi( main/plus::b#0, main::@1/plus::b#1 )
plus::a#2 = phi( main/plus::a#0, main::@1/plus::a#1 )
add::a#0 = plus::a#2
add::b#0 = plus::b#2
call add
add::return#0 = add::return#2
to:plus::@1
plus::@1: scope:[plus] from plus
add::return#3 = phi( plus/add::return#0 )
plus::$0 = add::return#3
plus::return#2 = plus::$0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
plus::return#6 = phi( plus::@1/plus::return#2 )
plus::return#3 = plus::return#6
return
to:@return
__bank(cx16_rom, 1) char add(char a , char b)
add: scope:[add] from plus
add::b#1 = phi( plus/add::b#0 )
add::a#1 = phi( plus/add::a#0 )
add::$0 = add::a#1 + add::b#1
add::return#1 = add::$0
to:add::@return
add::@return: scope:[add] from add
add::return#4 = phi( add/add::return#1 )
add::return#2 = add::return#4
return
to:@return
void __start()
__start: scope:[__start] from
call main
to:__start::@1
__start::@1: scope:[__start] from __start
to:__start::@return
__start::@return: scope:[__start] from __start::@1
return
to:@return
SYMBOL TABLE SSA
__constant char * const SCREEN = (char *)$400
void __start()
__bank(cx16_rom, 1) char add(char a , char b)
char add::$0
char add::a
char add::a#0
char add::a#1
char add::b
char add::b#0
char add::b#1
char add::return
char add::return#0
char add::return#1
char add::return#2
char add::return#3
char add::return#4
void main()
char main::$0
char main::$1
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::$0
char plus::a
char plus::a#0
char plus::a#1
char plus::a#2
char plus::b
char plus::b#0
char plus::b#1
char plus::b#2
char plus::return
char plus::return#0
char plus::return#1
char plus::return#2
char plus::return#3
char plus::return#4
char plus::return#5
char plus::return#6
Adding number conversion cast (unumber) 7 in plus::b#0 = 7
Adding number conversion cast (unumber) 0 in SCREEN[0] = main::$0
Adding number conversion cast (unumber) 6 in plus::b#1 = 6
Adding number conversion cast (unumber) 1 in SCREEN[1] = main::$1
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast plus::b#0 = (unumber)7
Inlining cast plus::b#1 = (unumber)6
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (char *) 1024
Simplifying constant integer cast 7
Simplifying constant integer cast 0
Simplifying constant integer cast 6
Simplifying constant integer cast 1
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (char) 7
Finalized unsigned number type (char) 0
Finalized unsigned number type (char) 6
Finalized unsigned number type (char) 1
Successful SSA optimization PassNFinalizeNumberTypeConversions
Alias plus::return#0 = plus::return#4
Alias plus::return#1 = plus::return#5
Alias add::return#0 = add::return#3
Alias plus::return#2 = plus::$0 plus::return#6 plus::return#3
Alias add::return#1 = add::$0 add::return#4 add::return#2
Successful SSA optimization Pass2AliasElimination
Identical Phi Values add::a#1 add::a#0
Identical Phi Values add::b#1 add::b#0
Successful SSA optimization Pass2IdenticalPhiElimination
Constant plus::a#0 = '0'
Constant plus::b#0 = 7
Constant plus::a#1 = '1'
Constant plus::b#1 = 6
Successful SSA optimization Pass2ConstantIdentification
Simplifying expression containing zero SCREEN in [5] SCREEN[0] = main::$0
Successful SSA optimization PassNSimplifyExpressionWithZero
Removing unused procedure __start
Removing unused procedure block __start
Removing unused procedure block __start::@1
Removing unused procedure block __start::@return
Successful SSA optimization PassNEliminateEmptyStart
Inlining constant with var siblings plus::a#0
Inlining constant with var siblings plus::b#0
Inlining constant with var siblings plus::a#1
Inlining constant with var siblings plus::b#1
Constant inlined plus::b#1 = 6
Constant inlined plus::b#0 = 7
Constant inlined plus::a#1 = '1'
Constant inlined plus::a#0 = '0'
Successful SSA optimization Pass2ConstantInlining
Consolidated array index constant in *(SCREEN+1)
Successful SSA optimization Pass2ConstantAdditionElimination
Adding NOP phi() at start of main
CALL GRAPH
Calls in [main] to plus:1 plus:5
Calls in [plus] to add:13
Created 2 initial phi equivalence classes
Coalesced down to 2 phi equivalence classes
Adding NOP phi() at start of main
FINAL CONTROL FLOW GRAPH
void main()
main: scope:[main] from
[0] phi()
[1] call plus
[2] plus::return#0 = plus::return#2
to:main::@1
main::@1: scope:[main] from main
[3] main::$0 = plus::return#0
[4] *SCREEN = main::$0
[5] call plus
[6] plus::return#1 = plus::return#2
to:main::@2
main::@2: scope:[main] from main::@1
[7] main::$1 = plus::return#1
[8] *(SCREEN+1) = main::$1
to:main::@return
main::@return: scope:[main] from main::@2
[9] return
to:@return
__bank(cx16_ram, 1) char plus(char a , char b)
plus: scope:[plus] from main main::@1
[10] plus::b#2 = phi( main/7, main::@1/6 )
[10] plus::a#2 = phi( main/'0', main::@1/'1' )
[11] add::a#0 = plus::a#2
[12] add::b#0 = plus::b#2
[13] call add
[14] add::return#0 = add::return#1
to:plus::@1
plus::@1: scope:[plus] from plus
[15] plus::return#2 = add::return#0
to:plus::@return
plus::@return: scope:[plus] from plus::@1
[16] return
to:@return
__bank(cx16_rom, 1) char add(char a , char b)
add: scope:[add] from plus
[17] add::return#1 = add::a#0 + add::b#0
to:add::@return
add::@return: scope:[add] from add
[18] return
to:@return
VARIABLE REGISTER WEIGHTS
__bank(cx16_rom, 1) char add(char a , char b)
char add::a
char add::a#0 // 56.0
char add::b
char add::b#0 // 112.0
char add::return
char add::return#0 // 22.0
char add::return#1 // 37.33333333333333
void main()
char main::$0 // 4.0
char main::$1 // 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // 11.0
char plus::b
char plus::b#2 // 5.5
char plus::return
char plus::return#0 // 4.0
char plus::return#1 // 4.0
char plus::return#2 // 3.75
Initial phi equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
Added variable main::$0 to live range equivalence class [ main::$0 ]
Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
Added variable main::$1 to live range equivalence class [ main::$1 ]
Added variable add::a#0 to live range equivalence class [ add::a#0 ]
Added variable add::b#0 to live range equivalence class [ add::b#0 ]
Added variable add::return#0 to live range equivalence class [ add::return#0 ]
Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
Added variable add::return#1 to live range equivalence class [ add::return#1 ]
Complete equivalence classes
[ plus::a#2 ]
[ plus::b#2 ]
[ plus::return#0 ]
[ main::$0 ]
[ plus::return#1 ]
[ main::$1 ]
[ add::a#0 ]
[ add::b#0 ]
[ add::return#0 ]
[ plus::return#2 ]
[ add::return#1 ]
Allocated zp[1]:2 [ add::b#0 ]
Allocated zp[1]:3 [ add::a#0 ]
Allocated zp[1]:4 [ add::return#1 ]
Allocated zp[1]:5 [ add::return#0 ]
Allocated zp[1]:6 [ plus::a#2 ]
Allocated zp[1]:7 [ plus::b#2 ]
Allocated zp[1]:8 [ plus::return#0 ]
Allocated zp[1]:9 [ main::$0 ]
Allocated zp[1]:10 [ plus::return#1 ]
Allocated zp[1]:11 [ main::$1 ]
Allocated zp[1]:12 [ plus::return#2 ]
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Removing always clobbered register reg byte a as potential for zp[1]:12 [ plus::return#2 ]
Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [13] call add [ add::return#1 ] ( plus:1 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
Removing always clobbered register reg byte a as potential for zp[1]:4 [ add::return#1 ]
Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
Statement [1] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Statement [5] call plus [ plus::return#2 ] ( [ plus::return#2 ] { { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [13] call add [ add::return#1 ] ( plus:1 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
Statement [17] add::return#1 = add::a#0 + add::b#0 [ add::return#1 ] ( plus:1::add:13 [ add::return#1 ] { { plus::return#0 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } plus:5::add:13 [ add::return#1 ] { { plus::return#1 = plus::return#2 } { add::a#0 = plus::a#2 } { add::b#0 = plus::b#2 } { add::return#0 = add::return#1 } } ) always clobbers reg byte a
Potential registers zp[1]:6 [ plus::a#2 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:7 [ plus::b#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:8 [ plus::return#0 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:10 [ plus::return#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:11 [ main::$1 ] : zp[1]:11 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:3 [ add::a#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:2 [ add::b#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:5 [ add::return#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y ,
Potential registers zp[1]:12 [ plus::return#2 ] : zp[1]:12 , reg byte x , reg byte y ,
Potential registers zp[1]:4 [ add::return#1 ] : zp[1]:4 , reg byte x , reg byte y ,
REGISTER UPLIFT SCOPES
Uplift Scope [add] 112: zp[1]:2 [ add::b#0 ] 56: zp[1]:3 [ add::a#0 ] 37.33: zp[1]:4 [ add::return#1 ] 22: zp[1]:5 [ add::return#0 ]
Uplift Scope [plus] 11: zp[1]:6 [ plus::a#2 ] 5.5: zp[1]:7 [ plus::b#2 ] 4: zp[1]:8 [ plus::return#0 ] 4: zp[1]:10 [ plus::return#1 ] 3.75: zp[1]:12 [ plus::return#2 ]
Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:11 [ main::$1 ]
Uplift Scope []
Uplifting [add] best 223 combination reg byte a [ add::b#0 ] zp[1]:3 [ add::a#0 ] reg byte x [ add::return#1 ] reg byte a [ add::return#0 ]
Limited combination testing to 100 combinations of 192 possible.
Uplifting [plus] best 195 combination reg byte a [ plus::a#2 ] reg byte x [ plus::b#2 ] reg byte a [ plus::return#0 ] reg byte a [ plus::return#1 ] zp[1]:12 [ plus::return#2 ]
Limited combination testing to 100 combinations of 768 possible.
Uplifting [main] best 183 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ]
Uplifting [] best 183 combination
Attempting to uplift remaining variables inzp[1]:3 [ add::a#0 ]
Uplifting [add] best 183 combination zp[1]:3 [ add::a#0 ]
Attempting to uplift remaining variables inzp[1]:12 [ plus::return#2 ]
Uplifting [plus] best 180 combination reg byte x [ plus::return#2 ]
Allocated (was zp[1]:3) zp[1]:2 [ add::a#0 ]
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
/**
* @file call-banked-phi-case-6-close-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #6.
* Implementation using the #pragma bank and nobank directives.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-6-close-1.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
plus_from_main:
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
ldx #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
txa
jmp __b1
// main::@1
__b1:
// [3] main::$0 = plus::return#0
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
plus_from___b1:
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
ldx #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
txa
jmp __b2
// main::@2
__b2:
// [7] main::$1 = plus::return#1
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// close call
sta SCREEN+1
jmp __breturn
// main::@return
__breturn:
// [9] return
rts
}
.segment RAM_Bank1
// plus
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
sta.z add.a
// [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
txa
// [13] call add -- call_phi_close_cx16_rom
sta.z $ff
lda.z 1
pha
lda #1
sta.z 1
lda.z $ff
jsr add
sta.z $ff
pla
sta.z 1
lda.z $ff
// [14] add::return#0 = add::return#1 -- vbuaa=vbuxx
txa
jmp __b1
// plus::@1
__b1:
// [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
tax
jmp __breturn
// plus::@return
__breturn:
// [16] return
rts
}
.segment ROM_Bank1
// add
// __register(X) char add(__zp(2) char a, __register(A) char b)
// __bank(cx16_rom, 1)
add: {
.label a = 2
// [17] add::return#1 = add::a#0 + add::b#0 -- vbuxx=vbuz1_plus_vbuaa
clc
adc.z a
tax
jmp __breturn
// add::@return
__breturn:
// [18] return
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __b1
Removing instruction jmp __b2
Removing instruction jmp __breturn
Removing instruction jmp __b1
Removing instruction jmp __breturn
Removing instruction jmp __breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction plus_from_main:
Removing instruction __b1:
Removing instruction plus_from___b1:
Removing instruction __b2:
Removing instruction __breturn:
Removing instruction __b1:
Removing instruction __breturn:
Removing instruction __breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
__constant char * const SCREEN = (char *) 1024
__bank(cx16_rom, 1) char add(char a , char b)
char add::a
char add::a#0 // a zp[1]:2 56.0
char add::b
char add::b#0 // reg byte a 112.0
char add::return
char add::return#0 // reg byte a 22.0
char add::return#1 // reg byte x 37.33333333333333
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte a 11.0
char plus::b
char plus::b#2 // reg byte x 5.5
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte x 3.75
reg byte a [ plus::a#2 ]
reg byte x [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
zp[1]:2 [ add::a#0 ]
reg byte a [ add::b#0 ]
reg byte a [ add::return#0 ]
reg byte x [ plus::return#2 ]
reg byte x [ add::return#1 ]
FINAL ASSEMBLER
Score: 162
// File Comments
/**
* @file call-banked-phi-case-6-close-1.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be),
* @author Jesper Gravgaard
* @brief Test a procedure with calling convention PHI - case #6.
* Implementation using the #pragma bank and nobank directives.
* @version 0.1
* @date 2023-04-11
*
* @copyright Copyright (c) 2023
*
* The following cases exist in banked calling implementations:
*
* - case #1 - unbanked to unbanked and no banking areas
* - case #2 - unbanked to banked to any bank area
* - case #3 - banked to unbanked from any bank area
* - case #4 - banked to same bank in same bank area
* - case #5 - banked to different bank in same bank area
* - case #6 - banked to any bank between different bank areas
*
* This brings us to the call types:
*
* - near = case #1, #3, #4
* - close = case #2, #6
* - far = case #5
*
*/
// Upstart
.file [name="call-banked-phi-case-6-close-1.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// plus('0', 7)
// [1] call plus
// [10] phi from main to plus [phi:main->plus]
// [10] phi plus::b#2 = 7 [phi:main->plus#0] -- vbuxx=vbuc1
ldx #7
// [10] phi plus::a#2 = '0' [phi:main->plus#1] -- call_phi_close_cx16_ram
lda #'0'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('0', 7)
// [2] plus::return#0 = plus::return#2 -- vbuaa=vbuxx
txa
// main::@1
// [3] main::$0 = plus::return#0
// SCREEN[0] = plus('0', 7)
// [4] *SCREEN = main::$0 -- _deref_pbuc1=vbuaa
sta SCREEN
// plus('1', 6)
// [5] call plus
// [10] phi from main::@1 to plus [phi:main::@1->plus]
// [10] phi plus::b#2 = 6 [phi:main::@1->plus#0] -- vbuxx=vbuc1
ldx #6
// [10] phi plus::a#2 = '1' [phi:main::@1->plus#1] -- call_phi_close_cx16_ram
lda #'1'
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus('1', 6)
// [6] plus::return#1 = plus::return#2 -- vbuaa=vbuxx
txa
// main::@2
// [7] main::$1 = plus::return#1
// SCREEN[1] = plus('1', 6)
// [8] *(SCREEN+1) = main::$1 -- _deref_pbuc1=vbuaa
// close call
sta SCREEN+1
// main::@return
// }
// [9] return
rts
}
.segment RAM_Bank1
// plus
// __register(X) char plus(__register(A) char a, __register(X) char b)
// __bank(cx16_ram, 1)
plus: {
// add(a, b)
// [11] add::a#0 = plus::a#2 -- vbuz1=vbuaa
sta.z add.a
// [12] add::b#0 = plus::b#2 -- vbuaa=vbuxx
txa
// [13] call add -- call_phi_close_cx16_rom
sta.z $ff
lda.z 1
pha
lda #1
sta.z 1
lda.z $ff
jsr add
sta.z $ff
pla
sta.z 1
lda.z $ff
// [14] add::return#0 = add::return#1 -- vbuaa=vbuxx
txa
// plus::@1
// [15] plus::return#2 = add::return#0 -- vbuxx=vbuaa
tax
// plus::@return
// }
// [16] return
rts
}
.segment ROM_Bank1
// add
// __register(X) char add(__zp(2) char a, __register(A) char b)
// __bank(cx16_rom, 1)
add: {
.label a = 2
// a+b
// [17] add::return#1 = add::a#0 + add::b#0 -- vbuxx=vbuz1_plus_vbuaa
clc
adc.z a
tax
// add::@return
// }
// [18] return
rts
}
// File Data

View File

@ -0,0 +1,33 @@
__constant char * const SCREEN = (char *) 1024
__bank(cx16_rom, 1) char add(char a , char b)
char add::a
char add::a#0 // a zp[1]:2 56.0
char add::b
char add::b#0 // reg byte a 112.0
char add::return
char add::return#0 // reg byte a 22.0
char add::return#1 // reg byte x 37.33333333333333
void main()
char main::$0 // reg byte a 4.0
char main::$1 // reg byte a 4.0
__bank(cx16_ram, 1) char plus(char a , char b)
char plus::a
char plus::a#2 // reg byte a 11.0
char plus::b
char plus::b#2 // reg byte x 5.5
char plus::return
char plus::return#0 // reg byte a 4.0
char plus::return#1 // reg byte a 4.0
char plus::return#2 // reg byte x 3.75
reg byte a [ plus::a#2 ]
reg byte x [ plus::b#2 ]
reg byte a [ plus::return#0 ]
reg byte a [ main::$0 ]
reg byte a [ plus::return#1 ]
reg byte a [ main::$1 ]
zp[1]:2 [ add::a#0 ]
reg byte a [ add::b#0 ]
reg byte a [ add::return#0 ]
reg byte x [ plus::return#2 ]
reg byte x [ add::return#1 ]

View File

@ -0,0 +1,144 @@
/**
* Test banked calls with memory variables.
* The parameters & return should end up in the shared/common bank.
*/
.file [name="call-banked-phi-memvars.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
.label SCREEN = $400
.segment Code
main: {
ldy #0
__b1:
// for(char i=0;i<5; i++)
cpy #5
bcc __b2
// }
rts
__b2:
// plus(100, (int)i)
tya
sta plus.b
lda #0
sta plus.b+1
lda #<$64
sta plus.a
lda #>$64
sta plus.a+1
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus(100, (int)i)
// SCREEN[i] = plus(100, (int)i)
tya
asl
tax
lda __1
sta SCREEN,x
lda __1+1
sta SCREEN+1,x
// 10+i
tya
tax
axs #-[$a]
// plus(200, (int)i)
tya
sta plus.b
lda #0
sta plus.b+1
lda #<$c8
sta plus.a
lda #>$c8
sta plus.a+1
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus(200, (int)i)
// SCREEN[10+i] = plus(200, (int)i)
txa
asl
tax
lda __3
sta SCREEN,x
lda __3+1
sta SCREEN+1,x
// for(char i=0;i<5; i++)
iny
jmp __b1
.segment Data
.label __1 = plus.b
.label __3 = plus.b
}
.segment RAM_Bank1
// __mem() int plus(__mem() int a, __mem() int b)
// __bank(cx16_ram, 1)
plus: {
// r += a
clc
lda a
adc #<2
sta r
lda a+1
adc #>2
sta r+1
// r += b
clc
lda r
adc b
sta r
lda r+1
adc b+1
sta r+1
// r += a
clc
lda r
adc a
sta r
lda r+1
adc a+1
sta r+1
// r += b
clc
lda return
adc r
sta return
lda return+1
adc r+1
sta return+1
// }
rts
.segment Data
b: .word 0
.label return = b
.segment RAM_Bank1
r: .word 0
.segment Data
a: .word 0
}

View File

@ -0,0 +1,45 @@
void main()
main: scope:[main] from
[0] phi()
to:main::@1
main::@1: scope:[main] from main main::@4
[1] main::i#2 = phi( main/0, main::@4/main::i#1 )
[2] if(main::i#2<5) goto main::@2
to:main::@return
main::@return: scope:[main] from main::@1
[3] return
to:@return
main::@2: scope:[main] from main::@1
[4] plus::b#0 = (int)main::i#2
[5] call plus
[6] plus::return#0 = plus::return#2
to:main::@3
main::@3: scope:[main] from main::@2
[7] main::$1 = plus::return#0
[8] main::$4 = main::i#2 << 1
[9] SCREEN[main::$4] = main::$1
[10] main::$2 = $a + main::i#2
[11] plus::b#1 = (int)main::i#2
[12] call plus
[13] plus::return#1 = plus::return#2
to:main::@4
main::@4: scope:[main] from main::@3
[14] main::$3 = plus::return#1
[15] main::$5 = main::$2 << 1
[16] SCREEN[main::$5] = main::$3
[17] main::i#1 = ++ main::i#2
to:main::@1
__bank(cx16_ram, 1) int plus(int a , int b)
plus: scope:[plus] from main::@2 main::@3
[18] plus::b#2 = phi( main::@2/plus::b#0, main::@3/plus::b#1 )
[18] plus::a#2 = phi( main::@2/$64, main::@3/$c8 )
[19] plus::r#1 = 2 + plus::a#2
[20] plus::r#2 = plus::r#1 + plus::b#2
[21] plus::r#3 = plus::r#2 + plus::a#2
[22] plus::return#2 = plus::r#3 + plus::b#2
to:plus::@return
plus::@return: scope:[plus] from plus
[23] return
to:@return

View File

@ -0,0 +1,803 @@
Loading link script "call-banked-phi.ld"
Updating intermediate variable memory area to MAIN_MEMORY main::$0
Updating intermediate variable memory area to MAIN_MEMORY main::$1
Updating intermediate variable memory area to MAIN_MEMORY main::$2
Updating intermediate variable memory area to MAIN_MEMORY main::$3
Fixing banked procedure parameter/return value to default segment plus::a
Fixing banked procedure parameter/return value to default segment plus::b
Fixing banked procedure parameter/return value to default segment plus::return
CONTROL FLOW GRAPH SSA
void main()
main: scope:[main] from __start
main::i#0 = 0
to:main::@1
main::@1: scope:[main] from main main::@4
main::i#2 = phi( main/main::i#0, main::@4/main::i#1 )
main::$0 = main::i#2 < 5
if(main::$0) goto main::@2
to:main::@return
main::@2: scope:[main] from main::@1
main::i#3 = phi( main::@1/main::i#2 )
plus::a#0 = $64
plus::b#0 = (int)main::i#3
call plus
plus::return#0 = plus::return#3
to:main::@3
main::@3: scope:[main] from main::@2
main::i#4 = phi( main::@2/main::i#3 )
plus::return#4 = phi( main::@2/plus::return#0 )
main::$1 = plus::return#4
main::$4 = main::i#4 * SIZEOF_INT
SCREEN[main::$4] = main::$1
main::$2 = $a + main::i#4
plus::a#1 = $c8
plus::b#1 = (int)main::i#4
call plus
plus::return#1 = plus::return#3
to:main::@4
main::@4: scope:[main] from main::@3
main::i#5 = phi( main::@3/main::i#4 )
plus::return#5 = phi( main::@3/plus::return#1 )
main::$3 = plus::return#5
main::$5 = main::$2 * SIZEOF_INT
SCREEN[main::$5] = main::$3
main::i#1 = ++ main::i#5
to:main::@1
main::@return: scope:[main] from main::@1
return
to:@return
__bank(cx16_ram, 1) int plus(int a , int b)
plus: scope:[plus] from main::@2 main::@3
plus::b#2 = phi( main::@2/plus::b#0, main::@3/plus::b#1 )
plus::a#2 = phi( main::@2/plus::a#0, main::@3/plus::a#1 )
plus::r#0 = 2
plus::r#1 = plus::r#0 + plus::a#2
plus::r#2 = plus::r#1 + plus::b#2
plus::r#3 = plus::r#2 + plus::a#2
plus::r#4 = plus::r#3 + plus::b#2
plus::return#2 = plus::r#4
to:plus::@return
plus::@return: scope:[plus] from plus
plus::return#6 = phi( plus/plus::return#2 )
plus::return#3 = plus::return#6
return
to:@return
void __start()
__start: scope:[__start] from
call main
to:__start::@1
__start::@1: scope:[__start] from __start
to:__start::@return
__start::@return: scope:[__start] from __start::@1
return
to:@return
SYMBOL TABLE SSA
__constant int * const SCREEN = (int *)$400
__constant char SIZEOF_INT = 2
void __start()
void main()
bool main::$0
int main::$1
number main::$2
int main::$3
char main::$4
number main::$5
char main::i
char main::i#0
char main::i#1
char main::i#2
char main::i#3
char main::i#4
char main::i#5
__bank(cx16_ram, 1) int plus(int a , int b)
int plus::a
int plus::a#0
int plus::a#1
int plus::a#2
int plus::b
int plus::b#0
int plus::b#1
int plus::b#2
int plus::r
int plus::r#0
int plus::r#1
int plus::r#2
int plus::r#3
int plus::r#4
int plus::return
int plus::return#0
int plus::return#1
int plus::return#2
int plus::return#3
int plus::return#4
int plus::return#5
int plus::return#6
Adding number conversion cast (unumber) 5 in main::$0 = main::i#2 < 5
Adding number conversion cast (snumber) $64 in plus::a#0 = $64
Adding number conversion cast (unumber) $a in main::$2 = $a + main::i#4
Adding number conversion cast (unumber) main::$2 in main::$2 = (unumber)$a + main::i#4
Adding number conversion cast (snumber) $c8 in plus::a#1 = $c8
Adding number conversion cast (unumber) main::$5 in main::$5 = main::$2 * SIZEOF_INT
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast plus::a#0 = (snumber)$64
Inlining cast plus::a#1 = (snumber)$c8
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (int *) 1024
Simplifying constant integer cast 5
Simplifying constant integer cast $64
Simplifying constant integer cast $a
Simplifying constant integer cast $c8
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (char) 5
Finalized signed number type (signed char) $64
Finalized unsigned number type (char) $a
Finalized signed number type (int) $c8
Successful SSA optimization PassNFinalizeNumberTypeConversions
Inferred type updated to char in main::$2 = $a + main::i#4
Inferred type updated to char in main::$5 = main::$2 * SIZEOF_INT
Alias main::i#2 = main::i#3 main::i#4 main::i#5
Alias plus::return#0 = plus::return#4
Alias plus::return#1 = plus::return#5
Alias plus::return#2 = plus::r#4 plus::return#6 plus::return#3
Successful SSA optimization Pass2AliasElimination
Simple Condition main::$0 [3] if(main::i#2<5) goto main::@2
Successful SSA optimization Pass2ConditionalJumpSimplification
Constant main::i#0 = 0
Constant plus::a#0 = $64
Constant plus::a#1 = $c8
Constant plus::r#0 = 2
Successful SSA optimization Pass2ConstantIdentification
Removing unused procedure __start
Removing unused procedure block __start
Removing unused procedure block __start::@1
Removing unused procedure block __start::@return
Successful SSA optimization PassNEliminateEmptyStart
Rewriting multiplication to use shift [6] main::$4 = main::i#2 * SIZEOF_INT
Rewriting multiplication to use shift [13] main::$5 = main::$2 * SIZEOF_INT
Successful SSA optimization Pass2MultiplyToShiftRewriting
Inlining constant with var siblings main::i#0
Inlining constant with var siblings plus::a#0
Inlining constant with var siblings plus::a#1
Inlining constant with var siblings plus::r#0
Constant inlined main::i#0 = 0
Constant inlined plus::a#1 = $c8
Constant inlined plus::r#0 = 2
Constant inlined plus::a#0 = $64
Successful SSA optimization Pass2ConstantInlining
Eliminating unused constant SIZEOF_INT
Successful SSA optimization PassNEliminateUnusedVars
Adding NOP phi() at start of main
CALL GRAPH
Calls in [main] to plus:6 plus:14
Created 3 initial phi equivalence classes
Coalesced [5] plus::b#3 = plus::b#0
Coalesced [13] plus::b#4 = plus::b#1
Coalesced [20] main::i#6 = main::i#1
Coalesced down to 3 phi equivalence classes
Adding NOP phi() at start of main
FINAL CONTROL FLOW GRAPH
void main()
main: scope:[main] from
[0] phi()
to:main::@1
main::@1: scope:[main] from main main::@4
[1] main::i#2 = phi( main/0, main::@4/main::i#1 )
[2] if(main::i#2<5) goto main::@2
to:main::@return
main::@return: scope:[main] from main::@1
[3] return
to:@return
main::@2: scope:[main] from main::@1
[4] plus::b#0 = (int)main::i#2
[5] call plus
[6] plus::return#0 = plus::return#2
to:main::@3
main::@3: scope:[main] from main::@2
[7] main::$1 = plus::return#0
[8] main::$4 = main::i#2 << 1
[9] SCREEN[main::$4] = main::$1
[10] main::$2 = $a + main::i#2
[11] plus::b#1 = (int)main::i#2
[12] call plus
[13] plus::return#1 = plus::return#2
to:main::@4
main::@4: scope:[main] from main::@3
[14] main::$3 = plus::return#1
[15] main::$5 = main::$2 << 1
[16] SCREEN[main::$5] = main::$3
[17] main::i#1 = ++ main::i#2
to:main::@1
__bank(cx16_ram, 1) int plus(int a , int b)
plus: scope:[plus] from main::@2 main::@3
[18] plus::b#2 = phi( main::@2/plus::b#0, main::@3/plus::b#1 )
[18] plus::a#2 = phi( main::@2/$64, main::@3/$c8 )
[19] plus::r#1 = 2 + plus::a#2
[20] plus::r#2 = plus::r#1 + plus::b#2
[21] plus::r#3 = plus::r#2 + plus::a#2
[22] plus::return#2 = plus::r#3 + plus::b#2
to:plus::@return
plus::@return: scope:[plus] from plus
[23] return
to:@return
VARIABLE REGISTER WEIGHTS
void main()
int main::$1 // 11.0
char main::$2 // 4.4
int main::$3 // 11.0
char main::$4 // 22.0
char main::$5 // 22.0
char main::i
char main::i#1 // 22.0
char main::i#2 // 3.6666666666666665
__bank(cx16_ram, 1) int plus(int a , int b)
int plus::a
int plus::a#2 // 67.33333333333333
int plus::b
int plus::b#0 // 22.0
int plus::b#1 // 22.0
int plus::b#2 // 56.0
int plus::r
int plus::r#1 // 202.0
int plus::r#2 // 202.0
int plus::r#3 // 202.0
int plus::return
int plus::return#0 // 22.0
int plus::return#1 // 22.0
int plus::return#2 // 30.75
Initial phi equivalence classes
[ main::i#2 main::i#1 ]
[ plus::a#2 ]
[ plus::b#2 plus::b#0 plus::b#1 ]
Added variable plus::return#0 to live range equivalence class [ plus::return#0 ]
Added variable main::$1 to live range equivalence class [ main::$1 ]
Added variable main::$4 to live range equivalence class [ main::$4 ]
Added variable main::$2 to live range equivalence class [ main::$2 ]
Added variable plus::return#1 to live range equivalence class [ plus::return#1 ]
Added variable main::$3 to live range equivalence class [ main::$3 ]
Added variable main::$5 to live range equivalence class [ main::$5 ]
Added variable plus::r#1 to live range equivalence class [ plus::r#1 ]
Added variable plus::r#2 to live range equivalence class [ plus::r#2 ]
Added variable plus::r#3 to live range equivalence class [ plus::r#3 ]
Added variable plus::return#2 to live range equivalence class [ plus::return#2 ]
Complete equivalence classes
[ main::i#2 main::i#1 ]
[ plus::a#2 ]
[ plus::b#2 plus::b#0 plus::b#1 ]
[ plus::return#0 ]
[ main::$1 ]
[ main::$4 ]
[ main::$2 ]
[ plus::return#1 ]
[ main::$3 ]
[ main::$5 ]
[ plus::r#1 ]
[ plus::r#2 ]
[ plus::r#3 ]
[ plus::return#2 ]
Allocated mem[2] [ plus::r#1 ]
Allocated mem[2] [ plus::r#2 ]
Allocated mem[2] [ plus::r#3 ]
Allocated mem[2] [ plus::b#2 plus::b#0 plus::b#1 ]
Allocated mem[2] [ plus::a#2 ]
Allocated mem[2] [ plus::return#2 ]
Allocated mem[1] [ main::i#2 main::i#1 ]
Allocated mem[2] [ plus::return#0 ]
Allocated mem[1] [ main::$4 ]
Allocated mem[2] [ plus::return#1 ]
Allocated mem[1] [ main::$5 ]
Allocated mem[2] [ main::$1 ]
Allocated mem[2] [ main::$3 ]
Allocated mem[1] [ main::$2 ]
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [4] plus::b#0 = (int)main::i#2 [ main::i#2 plus::b#0 ] ( [ main::i#2 plus::b#0 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Removing always clobbered register reg byte a as potential for mem[1] [ main::i#2 main::i#1 ]
Statement [5] call plus [ main::i#2 plus::return#2 ] ( [ main::i#2 plus::return#2 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Statement [6] plus::return#0 = plus::return#2 [ main::i#2 plus::return#0 ] ( [ main::i#2 plus::return#0 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Statement [7] main::$1 = plus::return#0 [ main::i#2 main::$1 ] ( [ main::i#2 main::$1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [8] main::$4 = main::i#2 << 1 [ main::i#2 main::$1 main::$4 ] ( [ main::i#2 main::$1 main::$4 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [9] SCREEN[main::$4] = main::$1 [ main::i#2 ] ( [ main::i#2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [10] main::$2 = $a + main::i#2 [ main::i#2 main::$2 ] ( [ main::i#2 main::$2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [11] plus::b#1 = (int)main::i#2 [ main::i#2 main::$2 plus::b#1 ] ( [ main::i#2 main::$2 plus::b#1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Removing always clobbered register reg byte a as potential for mem[1] [ main::$2 ]
Statement [12] call plus [ main::i#2 plus::return#2 main::$2 ] ( [ main::i#2 plus::return#2 main::$2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [13] plus::return#1 = plus::return#2 [ main::i#2 main::$2 plus::return#1 ] ( [ main::i#2 main::$2 plus::return#1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [14] main::$3 = plus::return#1 [ main::i#2 main::$2 main::$3 ] ( [ main::i#2 main::$2 main::$3 ] { } ) always clobbers reg byte a
Statement [15] main::$5 = main::$2 << 1 [ main::i#2 main::$3 main::$5 ] ( [ main::i#2 main::$3 main::$5 ] { } ) always clobbers reg byte a
Statement [16] SCREEN[main::$5] = main::$3 [ main::i#2 ] ( [ main::i#2 ] { } ) always clobbers reg byte a
Statement [19] plus::r#1 = 2 + plus::a#2 [ plus::a#2 plus::b#2 plus::r#1 ] ( plus:5 [ main::i#2 plus::a#2 plus::b#2 plus::r#1 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::a#2 plus::b#2 plus::r#1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [20] plus::r#2 = plus::r#1 + plus::b#2 [ plus::a#2 plus::b#2 plus::r#2 ] ( plus:5 [ main::i#2 plus::a#2 plus::b#2 plus::r#2 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::a#2 plus::b#2 plus::r#2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [21] plus::r#3 = plus::r#2 + plus::a#2 [ plus::b#2 plus::r#3 ] ( plus:5 [ main::i#2 plus::b#2 plus::r#3 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::b#2 plus::r#3 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [22] plus::return#2 = plus::r#3 + plus::b#2 [ plus::return#2 ] ( plus:5 [ main::i#2 plus::return#2 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::return#2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [4] plus::b#0 = (int)main::i#2 [ main::i#2 plus::b#0 ] ( [ main::i#2 plus::b#0 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Statement [5] call plus [ main::i#2 plus::return#2 ] ( [ main::i#2 plus::return#2 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Statement [6] plus::return#0 = plus::return#2 [ main::i#2 plus::return#0 ] ( [ main::i#2 plus::return#0 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } ) always clobbers reg byte a
Statement [7] main::$1 = plus::return#0 [ main::i#2 main::$1 ] ( [ main::i#2 main::$1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [8] main::$4 = main::i#2 << 1 [ main::i#2 main::$1 main::$4 ] ( [ main::i#2 main::$1 main::$4 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [9] SCREEN[main::$4] = main::$1 [ main::i#2 ] ( [ main::i#2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [10] main::$2 = $a + main::i#2 [ main::i#2 main::$2 ] ( [ main::i#2 main::$2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [11] plus::b#1 = (int)main::i#2 [ main::i#2 main::$2 plus::b#1 ] ( [ main::i#2 main::$2 plus::b#1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [12] call plus [ main::i#2 plus::return#2 main::$2 ] ( [ main::i#2 plus::return#2 main::$2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [13] plus::return#1 = plus::return#2 [ main::i#2 main::$2 plus::return#1 ] ( [ main::i#2 main::$2 plus::return#1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [14] main::$3 = plus::return#1 [ main::i#2 main::$2 main::$3 ] ( [ main::i#2 main::$2 main::$3 ] { } ) always clobbers reg byte a
Statement [15] main::$5 = main::$2 << 1 [ main::i#2 main::$3 main::$5 ] ( [ main::i#2 main::$3 main::$5 ] { } ) always clobbers reg byte a
Statement [16] SCREEN[main::$5] = main::$3 [ main::i#2 ] ( [ main::i#2 ] { } ) always clobbers reg byte a
Statement [19] plus::r#1 = 2 + plus::a#2 [ plus::a#2 plus::b#2 plus::r#1 ] ( plus:5 [ main::i#2 plus::a#2 plus::b#2 plus::r#1 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::a#2 plus::b#2 plus::r#1 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [20] plus::r#2 = plus::r#1 + plus::b#2 [ plus::a#2 plus::b#2 plus::r#2 ] ( plus:5 [ main::i#2 plus::a#2 plus::b#2 plus::r#2 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::a#2 plus::b#2 plus::r#2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [21] plus::r#3 = plus::r#2 + plus::a#2 [ plus::b#2 plus::r#3 ] ( plus:5 [ main::i#2 plus::b#2 plus::r#3 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::b#2 plus::r#3 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Statement [22] plus::return#2 = plus::r#3 + plus::b#2 [ plus::return#2 ] ( plus:5 [ main::i#2 plus::return#2 ] { { plus::b#0 = plus::b#2 } { plus::return#0 = plus::return#2 } } plus:12 [ main::i#2 main::$2 plus::return#2 ] { { plus::b#1 = plus::b#2 } { plus::return#1 = plus::return#2 } } ) always clobbers reg byte a
Potential registers mem[1] [ main::i#2 main::i#1 ] : mem[1] , reg byte x , reg byte y ,
Potential registers mem[2] [ plus::a#2 ] : mem[2] ,
Potential registers mem[2] [ plus::b#2 plus::b#0 plus::b#1 ] : mem[2] ,
Potential registers mem[2] [ plus::return#0 ] : mem[2] ,
Potential registers mem[2] [ main::$1 ] : mem[2] ,
Potential registers mem[1] [ main::$4 ] : mem[1] , reg byte a , reg byte x , reg byte y ,
Potential registers mem[1] [ main::$2 ] : mem[1] , reg byte x , reg byte y ,
Potential registers mem[2] [ plus::return#1 ] : mem[2] ,
Potential registers mem[2] [ main::$3 ] : mem[2] ,
Potential registers mem[1] [ main::$5 ] : mem[1] , reg byte a , reg byte x , reg byte y ,
Potential registers mem[2] [ plus::r#1 ] : mem[2] ,
Potential registers mem[2] [ plus::r#2 ] : mem[2] ,
Potential registers mem[2] [ plus::r#3 ] : mem[2] ,
Potential registers mem[2] [ plus::return#2 ] : mem[2] ,
REGISTER UPLIFT SCOPES
Uplift Scope [plus] 202: mem[2] [ plus::r#1 ] 202: mem[2] [ plus::r#2 ] 202: mem[2] [ plus::r#3 ] 100: mem[2] [ plus::b#2 plus::b#0 plus::b#1 ] 67.33: mem[2] [ plus::a#2 ] 30.75: mem[2] [ plus::return#2 ] 22: mem[2] [ plus::return#0 ] 22: mem[2] [ plus::return#1 ]
Uplift Scope [main] 25.67: mem[1] [ main::i#2 main::i#1 ] 22: mem[1] [ main::$4 ] 22: mem[1] [ main::$5 ] 11: mem[2] [ main::$1 ] 11: mem[2] [ main::$3 ] 4.4: mem[1] [ main::$2 ]
Uplift Scope []
Uplifting [plus] best 2226 combination mem[2] [ plus::r#1 ] mem[2] [ plus::r#2 ] mem[2] [ plus::r#3 ] mem[2] [ plus::b#2 plus::b#0 plus::b#1 ] mem[2] [ plus::a#2 ] mem[2] [ plus::return#2 ] mem[2] [ plus::return#0 ] mem[2] [ plus::return#1 ]
Uplifting [main] best 1866 combination reg byte y [ main::i#2 main::i#1 ] reg byte x [ main::$4 ] reg byte x [ main::$5 ] mem[2] [ main::$1 ] mem[2] [ main::$3 ] reg byte x [ main::$2 ]
Limited combination testing to 100 combinations of 144 possible.
Uplifting [] best 1866 combination
Coalescing zero page register [ mem[2] [ plus::b#2 plus::b#0 plus::b#1 ] ] with [ mem[2] [ plus::return#2 ] ] - score: 1
Coalescing zero page register [ mem[2] [ plus::return#0 ] ] with [ mem[2] [ main::$1 ] ] - score: 1
Coalescing zero page register [ mem[2] [ plus::return#1 ] ] with [ mem[2] [ main::$3 ] ] - score: 1
Coalescing zero page register [ mem[2] [ plus::r#1 ] ] with [ mem[2] [ plus::r#2 ] ] - score: 1
Coalescing zero page register [ mem[2] [ plus::b#2 plus::b#0 plus::b#1 plus::return#2 ] ] with [ mem[2] [ plus::return#0 main::$1 ] ] - score: 1
Coalescing zero page register [ mem[2] [ plus::b#2 plus::b#0 plus::b#1 plus::return#2 plus::return#0 main::$1 ] ] with [ mem[2] [ plus::return#1 main::$3 ] ] - score: 1
Coalescing zero page register [ mem[2] [ plus::r#1 plus::r#2 ] ] with [ mem[2] [ plus::r#3 ] ] - score: 1
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
/**
* Test banked calls with memory variables.
* The parameters & return should end up in the shared/common bank.
*/
// Upstart
.file [name="call-banked-phi-memvars.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// [1] phi from main to main::@1 [phi:main->main::@1]
__b1_from_main:
// [1] phi main::i#2 = 0 [phi:main->main::@1#0] -- vbuyy=vbuc1
ldy #0
jmp __b1
// main::@1
__b1:
// [2] if(main::i#2<5) goto main::@2 -- vbuyy_lt_vbuc1_then_la1
cpy #5
bcc __b2
jmp __breturn
// main::@return
__breturn:
// [3] return
rts
// main::@2
__b2:
// [4] plus::b#0 = (int)main::i#2 -- vwsm1=_sword_vbuyy
tya
sta plus.b
lda #0
sta plus.b+1
// [5] call plus
// [18] phi from main::@2 to plus [phi:main::@2->plus]
plus_from___b2:
// [18] phi plus::b#2 = plus::b#0 [phi:main::@2->plus#0] -- register_copy
// [18] phi plus::a#2 = $64 [phi:main::@2->plus#1] -- call_phi_close_cx16_ram
lda #<$64
sta plus.a
lda #>$64
sta plus.a+1
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// [6] plus::return#0 = plus::return#2
jmp __b3
// main::@3
__b3:
// [7] main::$1 = plus::return#0
// [8] main::$4 = main::i#2 << 1 -- vbuxx=vbuyy_rol_1
tya
asl
tax
// [9] SCREEN[main::$4] = main::$1 -- pwsc1_derefidx_vbuxx=vwsm1
lda __1
sta SCREEN,x
lda __1+1
sta SCREEN+1,x
// [10] main::$2 = $a + main::i#2 -- vbuxx=vbuc1_plus_vbuyy
tya
tax
axs #-[$a]
// [11] plus::b#1 = (int)main::i#2 -- vwsm1=_sword_vbuyy
tya
sta plus.b
lda #0
sta plus.b+1
// [12] call plus
// [18] phi from main::@3 to plus [phi:main::@3->plus]
plus_from___b3:
// [18] phi plus::b#2 = plus::b#1 [phi:main::@3->plus#0] -- register_copy
// [18] phi plus::a#2 = $c8 [phi:main::@3->plus#1] -- call_phi_close_cx16_ram
lda #<$c8
sta plus.a
lda #>$c8
sta plus.a+1
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// [13] plus::return#1 = plus::return#2
jmp __b4
// main::@4
__b4:
// [14] main::$3 = plus::return#1
// [15] main::$5 = main::$2 << 1 -- vbuxx=vbuxx_rol_1
txa
asl
tax
// [16] SCREEN[main::$5] = main::$3 -- pwsc1_derefidx_vbuxx=vwsm1
lda __3
sta SCREEN,x
lda __3+1
sta SCREEN+1,x
// [17] main::i#1 = ++ main::i#2 -- vbuyy=_inc_vbuyy
iny
// [1] phi from main::@4 to main::@1 [phi:main::@4->main::@1]
__b1_from___b4:
// [1] phi main::i#2 = main::i#1 [phi:main::@4->main::@1#0] -- register_copy
jmp __b1
.segment Data
.label __1 = plus.b
.label __3 = plus.b
}
.segment RAM_Bank1
// plus
// __mem() int plus(__mem() int a, __mem() int b)
// __bank(cx16_ram, 1)
plus: {
// [19] plus::r#1 = 2 + plus::a#2 -- vwsm1=vwsc1_plus_vwsm2
clc
lda a
adc #<2
sta r
lda a+1
adc #>2
sta r+1
// [20] plus::r#2 = plus::r#1 + plus::b#2 -- vwsm1=vwsm1_plus_vwsm2
clc
lda r
adc b
sta r
lda r+1
adc b+1
sta r+1
// [21] plus::r#3 = plus::r#2 + plus::a#2 -- vwsm1=vwsm1_plus_vwsm2
clc
lda r
adc a
sta r
lda r+1
adc a+1
sta r+1
// [22] plus::return#2 = plus::r#3 + plus::b#2 -- vwsm1=vwsm2_plus_vwsm1
clc
lda return
adc r
sta return
lda return+1
adc r+1
sta return+1
jmp __breturn
// plus::@return
__breturn:
// [23] return
rts
.segment Data
b: .word 0
.label return = b
.segment RAM_Bank1
r: .word 0
.segment Data
a: .word 0
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __b1
Removing instruction jmp __breturn
Removing instruction jmp __b3
Removing instruction jmp __b4
Removing instruction jmp __breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction __b1_from_main:
Removing instruction __breturn:
Removing instruction plus_from___b2:
Removing instruction __b3:
Removing instruction plus_from___b3:
Removing instruction __b4:
Removing instruction __b1_from___b4:
Removing instruction __breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
__constant int * const SCREEN = (int *) 1024
void main()
int main::$1 // mem[2] 11.0
char main::$2 // reg byte x 4.4
int main::$3 // mem[2] 11.0
char main::$4 // reg byte x 22.0
char main::$5 // reg byte x 22.0
char main::i
char main::i#1 // reg byte y 22.0
char main::i#2 // reg byte y 3.6666666666666665
__bank(cx16_ram, 1) int plus(int a , int b)
int plus::a
int plus::a#2 // a mem[2] 67.33333333333333
int plus::b
int plus::b#0 // b mem[2] 22.0
int plus::b#1 // b mem[2] 22.0
int plus::b#2 // b mem[2] 56.0
int plus::r
int plus::r#1 // r mem[2] 202.0
int plus::r#2 // r mem[2] 202.0
int plus::r#3 // r mem[2] 202.0
int plus::return
int plus::return#0 // return mem[2] 22.0
int plus::return#1 // return mem[2] 22.0
int plus::return#2 // return mem[2] 30.75
reg byte y [ main::i#2 main::i#1 ]
mem[2] [ plus::a#2 ]
mem[2] [ plus::b#2 plus::b#0 plus::b#1 plus::return#2 plus::return#0 main::$1 plus::return#1 main::$3 ]
reg byte x [ main::$4 ]
reg byte x [ main::$2 ]
reg byte x [ main::$5 ]
mem[2] [ plus::r#1 plus::r#2 plus::r#3 ]
FINAL ASSEMBLER
Score: 1103
// File Comments
/**
* Test banked calls with memory variables.
* The parameters & return should end up in the shared/common bank.
*/
// Upstart
.file [name="call-banked-phi-memvars.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segmentdef RAM_Bank1 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef RAM_Bank2 [start=$A000, min=$A000, max=$BFFF, align=$100]
.segmentdef ROM_Bank1 [start=$C000, min=$C000, max=$FFFF, align=$100]
.segment Basic
:BasicUpstart(main)
.segment Code
.segment Data
// Global Constants & labels
.label SCREEN = $400
.segment Code
// main
main: {
// [1] phi from main to main::@1 [phi:main->main::@1]
// [1] phi main::i#2 = 0 [phi:main->main::@1#0] -- vbuyy=vbuc1
ldy #0
// main::@1
__b1:
// for(char i=0;i<5; i++)
// [2] if(main::i#2<5) goto main::@2 -- vbuyy_lt_vbuc1_then_la1
cpy #5
bcc __b2
// main::@return
// }
// [3] return
rts
// main::@2
__b2:
// plus(100, (int)i)
// [4] plus::b#0 = (int)main::i#2 -- vwsm1=_sword_vbuyy
tya
sta plus.b
lda #0
sta plus.b+1
// [5] call plus
// [18] phi from main::@2 to plus [phi:main::@2->plus]
// [18] phi plus::b#2 = plus::b#0 [phi:main::@2->plus#0] -- register_copy
// [18] phi plus::a#2 = $64 [phi:main::@2->plus#1] -- call_phi_close_cx16_ram
lda #<$64
sta plus.a
lda #>$64
sta plus.a+1
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus(100, (int)i)
// [6] plus::return#0 = plus::return#2
// main::@3
// [7] main::$1 = plus::return#0
// SCREEN[i] = plus(100, (int)i)
// [8] main::$4 = main::i#2 << 1 -- vbuxx=vbuyy_rol_1
tya
asl
tax
// [9] SCREEN[main::$4] = main::$1 -- pwsc1_derefidx_vbuxx=vwsm1
lda __1
sta SCREEN,x
lda __1+1
sta SCREEN+1,x
// 10+i
// [10] main::$2 = $a + main::i#2 -- vbuxx=vbuc1_plus_vbuyy
tya
tax
axs #-[$a]
// plus(200, (int)i)
// [11] plus::b#1 = (int)main::i#2 -- vwsm1=_sword_vbuyy
tya
sta plus.b
lda #0
sta plus.b+1
// [12] call plus
// [18] phi from main::@3 to plus [phi:main::@3->plus]
// [18] phi plus::b#2 = plus::b#1 [phi:main::@3->plus#0] -- register_copy
// [18] phi plus::a#2 = $c8 [phi:main::@3->plus#1] -- call_phi_close_cx16_ram
lda #<$c8
sta plus.a
lda #>$c8
sta plus.a+1
sta.z $ff
lda.z 0
pha
lda #1
sta.z 0
lda.z $ff
jsr plus
sta.z $ff
pla
sta.z 0
lda.z $ff
// plus(200, (int)i)
// [13] plus::return#1 = plus::return#2
// main::@4
// [14] main::$3 = plus::return#1
// SCREEN[10+i] = plus(200, (int)i)
// [15] main::$5 = main::$2 << 1 -- vbuxx=vbuxx_rol_1
txa
asl
tax
// [16] SCREEN[main::$5] = main::$3 -- pwsc1_derefidx_vbuxx=vwsm1
lda __3
sta SCREEN,x
lda __3+1
sta SCREEN+1,x
// for(char i=0;i<5; i++)
// [17] main::i#1 = ++ main::i#2 -- vbuyy=_inc_vbuyy
iny
// [1] phi from main::@4 to main::@1 [phi:main::@4->main::@1]
// [1] phi main::i#2 = main::i#1 [phi:main::@4->main::@1#0] -- register_copy
jmp __b1
.segment Data
.label __1 = plus.b
.label __3 = plus.b
}
.segment RAM_Bank1
// plus
// __mem() int plus(__mem() int a, __mem() int b)
// __bank(cx16_ram, 1)
plus: {
// r += a
// [19] plus::r#1 = 2 + plus::a#2 -- vwsm1=vwsc1_plus_vwsm2
clc
lda a
adc #<2
sta r
lda a+1
adc #>2
sta r+1
// r += b
// [20] plus::r#2 = plus::r#1 + plus::b#2 -- vwsm1=vwsm1_plus_vwsm2
clc
lda r
adc b
sta r
lda r+1
adc b+1
sta r+1
// r += a
// [21] plus::r#3 = plus::r#2 + plus::a#2 -- vwsm1=vwsm1_plus_vwsm2
clc
lda r
adc a
sta r
lda r+1
adc a+1
sta r+1
// r += b
// [22] plus::return#2 = plus::r#3 + plus::b#2 -- vwsm1=vwsm2_plus_vwsm1
clc
lda return
adc r
sta return
lda return+1
adc r+1
sta return+1
// plus::@return
// }
// [23] return
rts
.segment Data
b: .word 0
.label return = b
.segment RAM_Bank1
r: .word 0
.segment Data
a: .word 0
}
// File Data

View File

@ -0,0 +1,33 @@
__constant int * const SCREEN = (int *) 1024
void main()
int main::$1 // mem[2] 11.0
char main::$2 // reg byte x 4.4
int main::$3 // mem[2] 11.0
char main::$4 // reg byte x 22.0
char main::$5 // reg byte x 22.0
char main::i
char main::i#1 // reg byte y 22.0
char main::i#2 // reg byte y 3.6666666666666665
__bank(cx16_ram, 1) int plus(int a , int b)
int plus::a
int plus::a#2 // a mem[2] 67.33333333333333
int plus::b
int plus::b#0 // b mem[2] 22.0
int plus::b#1 // b mem[2] 22.0
int plus::b#2 // b mem[2] 56.0
int plus::r
int plus::r#1 // r mem[2] 202.0
int plus::r#2 // r mem[2] 202.0
int plus::r#3 // r mem[2] 202.0
int plus::return
int plus::return#0 // return mem[2] 22.0
int plus::return#1 // return mem[2] 22.0
int plus::return#2 // return mem[2] 30.75
reg byte y [ main::i#2 main::i#1 ]
mem[2] [ plus::a#2 ]
mem[2] [ plus::b#2 plus::b#0 plus::b#1 plus::return#2 plus::return#0 main::$1 plus::return#1 main::$3 ]
reg byte x [ main::$4 ]
reg byte x [ main::$2 ]
reg byte x [ main::$5 ]
mem[2] [ plus::r#1 plus::r#2 plus::r#3 ]

View File

@ -4,7 +4,7 @@ CONTROL FLOW GRAPH SSA
void main()
main: scope:[main] from __start
asm { lda#'c' stasub.ll+1 }
call sub
call sub
to:main::@1
main::@1: scope:[main] from main
to:main::@return
@ -22,7 +22,7 @@ sub::@return: scope:[sub] from sub
void __start()
__start: scope:[__start] from
call main
call main
to:__start::@1
__start::@1: scope:[__start] from __start
to:__start::@return
@ -53,7 +53,7 @@ FINAL CONTROL FLOW GRAPH
void main()
main: scope:[main] from
asm { lda#'c' stasub.ll+1 }
[1] call sub
[1] call sub
to:main::@return
main::@return: scope:[main] from main
[2] return
@ -106,7 +106,7 @@ main: {
// asm { lda#'c' stasub.ll+1 }
lda #'c'
sta sub.ll+1
// [1] call sub
// [1] call sub
jsr sub
jmp __breturn
// main::@return
@ -165,7 +165,7 @@ main: {
lda #'c'
sta sub.ll+1
// sub()
// [1] call sub
// [1] call sub
jsr sub
// main::@return
// }

View File

@ -1,4 +1,3 @@
Warning! Unknown #pragma nobank
CONTROL FLOW GRAPH SSA