mirror of
https://gitlab.com/camelot/kickc.git
synced 2025-02-20 00:29:10 +00:00
Merge branch 'far-call-isolated-updates' into 'master'
Merging far calls... See merge request camelot/kickc!47
This commit is contained in:
commit
3d03993175
7
.gitignore
vendored
7
.gitignore
vendored
@ -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
|
||||
|
@ -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' ;
|
||||
|
@ -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
|
||||
|
11
src/main/fragment/mos6502-common/call_phi_close_cx16_ram.asm
Normal file
11
src/main/fragment/mos6502-common/call_phi_close_cx16_ram.asm
Normal 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
|
11
src/main/fragment/mos6502-common/call_phi_close_cx16_rom.asm
Normal file
11
src/main/fragment/mos6502-common/call_phi_close_cx16_rom.asm
Normal 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
|
@ -0,0 +1,4 @@
|
||||
jsr $FF6E
|
||||
.byte <{la1}
|
||||
.byte >{la1}
|
||||
.byte {c1}
|
@ -0,0 +1,4 @@
|
||||
jsr $FF6E
|
||||
.byte <{la1}
|
||||
.byte >{la1}
|
||||
.byte {c1}
|
1
src/main/fragment/mos6502-common/call_phi_near.asm
Normal file
1
src/main/fragment/mos6502-common/call_phi_near.asm
Normal file
@ -0,0 +1 @@
|
||||
jsr {la1}
|
@ -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();
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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);
|
||||
|
@ -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>.
|
||||
*/
|
||||
|
@ -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"); }
|
||||
|
173
src/main/java/dk/camelot64/kickc/model/symbols/Bank.java
Normal file
173
src/main/java/dk/camelot64/kickc/model/symbols/Bank.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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");
|
||||
|
41
src/test/kc/call-banked-phi-case-1-near-0.c
Normal file
41
src/test/kc/call-banked-phi-case-1-near-0.c
Normal 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;
|
||||
}
|
46
src/test/kc/call-banked-phi-case-1-near-1.c
Normal file
46
src/test/kc/call-banked-phi-case-1-near-1.c
Normal 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;
|
||||
}
|
42
src/test/kc/call-banked-phi-case-2-close-0.c
Normal file
42
src/test/kc/call-banked-phi-case-2-close-0.c
Normal 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;
|
||||
}
|
43
src/test/kc/call-banked-phi-case-2-close-1.c
Normal file
43
src/test/kc/call-banked-phi-case-2-close-1.c
Normal 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;
|
||||
}
|
50
src/test/kc/call-banked-phi-case-3-near-0.c
Normal file
50
src/test/kc/call-banked-phi-case-3-near-0.c
Normal 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;
|
||||
}
|
||||
|
||||
|
51
src/test/kc/call-banked-phi-case-3-near-1.c
Normal file
51
src/test/kc/call-banked-phi-case-3-near-1.c
Normal 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;
|
||||
}
|
||||
|
||||
|
47
src/test/kc/call-banked-phi-case-4-near-0.c
Normal file
47
src/test/kc/call-banked-phi-case-4-near-0.c
Normal 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;
|
||||
}
|
49
src/test/kc/call-banked-phi-case-4-near-1.c
Normal file
49
src/test/kc/call-banked-phi-case-4-near-1.c
Normal 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;
|
||||
}
|
47
src/test/kc/call-banked-phi-case-5-far-0.c
Normal file
47
src/test/kc/call-banked-phi-case-5-far-0.c
Normal 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;
|
||||
}
|
49
src/test/kc/call-banked-phi-case-5-far-1.c
Normal file
49
src/test/kc/call-banked-phi-case-5-far-1.c
Normal 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;
|
||||
}
|
47
src/test/kc/call-banked-phi-case-6-close-0.c
Normal file
47
src/test/kc/call-banked-phi-case-6-close-0.c
Normal 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;
|
||||
}
|
49
src/test/kc/call-banked-phi-case-6-close-1.c
Normal file
49
src/test/kc/call-banked-phi-case-6-close-1.c
Normal 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;
|
||||
}
|
30
src/test/kc/call-banked-phi-memvars.c
Normal file
30
src/test/kc/call-banked-phi-memvars.c
Normal 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;
|
||||
}
|
13
src/test/kc/call-banked-phi.ld
Normal file
13
src/test/kc/call-banked-phi.ld
Normal 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
|
||||
|
42
src/test/kc/call-banked-stack-case-2-close-0.c
Normal file
42
src/test/kc/call-banked-stack-case-2-close-0.c
Normal 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;
|
||||
}
|
47
src/test/kc/call-banked-stack-case-5-far-0.c
Normal file
47
src/test/kc/call-banked-stack-case-5-far-0.c
Normal 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;
|
||||
}
|
13
src/test/kc/call-banked-stack.ld
Normal file
13
src/test/kc/call-banked-stack.ld
Normal 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
|
||||
|
164
src/test/kc/examples/cx16/banking/cx16-banking.c
Normal file
164
src/test/kc/examples/cx16/banking/cx16-banking.c
Normal 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.
|
||||
}
|
14
src/test/kc/examples/cx16/banking/cx16-banking.ld
Normal file
14
src/test/kc/examples/cx16/banking/cx16-banking.ld
Normal 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
|
||||
|
70
src/test/ref/call-banked-phi-case-1-near-0.asm
Normal file
70
src/test/ref/call-banked-phi-case-1-near-0.asm
Normal 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
|
||||
}
|
30
src/test/ref/call-banked-phi-case-1-near-0.cfg
Normal file
30
src/test/ref/call-banked-phi-case-1-near-0.cfg
Normal 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
|
462
src/test/ref/call-banked-phi-case-1-near-0.log
Normal file
462
src/test/ref/call-banked-phi-case-1-near-0.log
Normal 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
|
||||
|
21
src/test/ref/call-banked-phi-case-1-near-0.sym
Normal file
21
src/test/ref/call-banked-phi-case-1-near-0.sym
Normal 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 ]
|
78
src/test/ref/call-banked-phi-case-1-near-1.asm
Normal file
78
src/test/ref/call-banked-phi-case-1-near-1.asm
Normal 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
|
||||
}
|
44
src/test/ref/call-banked-phi-case-1-near-1.cfg
Normal file
44
src/test/ref/call-banked-phi-case-1-near-1.cfg
Normal 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
|
599
src/test/ref/call-banked-phi-case-1-near-1.log
Normal file
599
src/test/ref/call-banked-phi-case-1-near-1.log
Normal 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
|
||||
|
33
src/test/ref/call-banked-phi-case-1-near-1.sym
Normal file
33
src/test/ref/call-banked-phi-case-1-near-1.sym
Normal 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 ]
|
96
src/test/ref/call-banked-phi-case-2-close-0.asm
Normal file
96
src/test/ref/call-banked-phi-case-2-close-0.asm
Normal 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
|
||||
}
|
30
src/test/ref/call-banked-phi-case-2-close-0.cfg
Normal file
30
src/test/ref/call-banked-phi-case-2-close-0.cfg
Normal 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
|
520
src/test/ref/call-banked-phi-case-2-close-0.log
Normal file
520
src/test/ref/call-banked-phi-case-2-close-0.log
Normal 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
|
||||
|
21
src/test/ref/call-banked-phi-case-2-close-0.sym
Normal file
21
src/test/ref/call-banked-phi-case-2-close-0.sym
Normal 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 ]
|
96
src/test/ref/call-banked-phi-case-2-close-1.asm
Normal file
96
src/test/ref/call-banked-phi-case-2-close-1.asm
Normal 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
|
||||
}
|
30
src/test/ref/call-banked-phi-case-2-close-1.cfg
Normal file
30
src/test/ref/call-banked-phi-case-2-close-1.cfg
Normal 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
|
520
src/test/ref/call-banked-phi-case-2-close-1.log
Normal file
520
src/test/ref/call-banked-phi-case-2-close-1.log
Normal 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
|
||||
|
21
src/test/ref/call-banked-phi-case-2-close-1.sym
Normal file
21
src/test/ref/call-banked-phi-case-2-close-1.sym
Normal 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 ]
|
106
src/test/ref/call-banked-phi-case-3-near-0.asm
Normal file
106
src/test/ref/call-banked-phi-case-3-near-0.asm
Normal 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
|
||||
}
|
44
src/test/ref/call-banked-phi-case-3-near-0.cfg
Normal file
44
src/test/ref/call-banked-phi-case-3-near-0.cfg
Normal 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
|
660
src/test/ref/call-banked-phi-case-3-near-0.log
Normal file
660
src/test/ref/call-banked-phi-case-3-near-0.log
Normal 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
|
||||
|
33
src/test/ref/call-banked-phi-case-3-near-0.sym
Normal file
33
src/test/ref/call-banked-phi-case-3-near-0.sym
Normal 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 ]
|
106
src/test/ref/call-banked-phi-case-3-near-1.asm
Normal file
106
src/test/ref/call-banked-phi-case-3-near-1.asm
Normal 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
|
||||
}
|
44
src/test/ref/call-banked-phi-case-3-near-1.cfg
Normal file
44
src/test/ref/call-banked-phi-case-3-near-1.cfg
Normal 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
|
660
src/test/ref/call-banked-phi-case-3-near-1.log
Normal file
660
src/test/ref/call-banked-phi-case-3-near-1.log
Normal 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
|
||||
|
33
src/test/ref/call-banked-phi-case-3-near-1.sym
Normal file
33
src/test/ref/call-banked-phi-case-3-near-1.sym
Normal 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 ]
|
106
src/test/ref/call-banked-phi-case-4-near-0.asm
Normal file
106
src/test/ref/call-banked-phi-case-4-near-0.asm
Normal 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
|
||||
}
|
44
src/test/ref/call-banked-phi-case-4-near-0.cfg
Normal file
44
src/test/ref/call-banked-phi-case-4-near-0.cfg
Normal 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
|
660
src/test/ref/call-banked-phi-case-4-near-0.log
Normal file
660
src/test/ref/call-banked-phi-case-4-near-0.log
Normal 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
|
||||
|
33
src/test/ref/call-banked-phi-case-4-near-0.sym
Normal file
33
src/test/ref/call-banked-phi-case-4-near-0.sym
Normal 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 ]
|
106
src/test/ref/call-banked-phi-case-4-near-1.asm
Normal file
106
src/test/ref/call-banked-phi-case-4-near-1.asm
Normal 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
|
||||
}
|
44
src/test/ref/call-banked-phi-case-4-near-1.cfg
Normal file
44
src/test/ref/call-banked-phi-case-4-near-1.cfg
Normal 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
|
660
src/test/ref/call-banked-phi-case-4-near-1.log
Normal file
660
src/test/ref/call-banked-phi-case-4-near-1.log
Normal 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
|
||||
|
33
src/test/ref/call-banked-phi-case-4-near-1.sym
Normal file
33
src/test/ref/call-banked-phi-case-4-near-1.sym
Normal 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 ]
|
110
src/test/ref/call-banked-phi-case-5-far-0.asm
Normal file
110
src/test/ref/call-banked-phi-case-5-far-0.asm
Normal 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
|
||||
}
|
44
src/test/ref/call-banked-phi-case-5-far-0.cfg
Normal file
44
src/test/ref/call-banked-phi-case-5-far-0.cfg
Normal 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
|
668
src/test/ref/call-banked-phi-case-5-far-0.log
Normal file
668
src/test/ref/call-banked-phi-case-5-far-0.log
Normal 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
|
||||
|
33
src/test/ref/call-banked-phi-case-5-far-0.sym
Normal file
33
src/test/ref/call-banked-phi-case-5-far-0.sym
Normal 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 ]
|
110
src/test/ref/call-banked-phi-case-5-far-1.asm
Normal file
110
src/test/ref/call-banked-phi-case-5-far-1.asm
Normal 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
|
||||
}
|
44
src/test/ref/call-banked-phi-case-5-far-1.cfg
Normal file
44
src/test/ref/call-banked-phi-case-5-far-1.cfg
Normal 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
|
668
src/test/ref/call-banked-phi-case-5-far-1.log
Normal file
668
src/test/ref/call-banked-phi-case-5-far-1.log
Normal 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
|
||||
|
33
src/test/ref/call-banked-phi-case-5-far-1.sym
Normal file
33
src/test/ref/call-banked-phi-case-5-far-1.sym
Normal 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 ]
|
119
src/test/ref/call-banked-phi-case-6-close-0.asm
Normal file
119
src/test/ref/call-banked-phi-case-6-close-0.asm
Normal 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
|
||||
}
|
44
src/test/ref/call-banked-phi-case-6-close-0.cfg
Normal file
44
src/test/ref/call-banked-phi-case-6-close-0.cfg
Normal 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
|
689
src/test/ref/call-banked-phi-case-6-close-0.log
Normal file
689
src/test/ref/call-banked-phi-case-6-close-0.log
Normal 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
|
||||
|
33
src/test/ref/call-banked-phi-case-6-close-0.sym
Normal file
33
src/test/ref/call-banked-phi-case-6-close-0.sym
Normal 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 ]
|
119
src/test/ref/call-banked-phi-case-6-close-1.asm
Normal file
119
src/test/ref/call-banked-phi-case-6-close-1.asm
Normal 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
|
||||
}
|
44
src/test/ref/call-banked-phi-case-6-close-1.cfg
Normal file
44
src/test/ref/call-banked-phi-case-6-close-1.cfg
Normal 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
|
689
src/test/ref/call-banked-phi-case-6-close-1.log
Normal file
689
src/test/ref/call-banked-phi-case-6-close-1.log
Normal 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
|
||||
|
33
src/test/ref/call-banked-phi-case-6-close-1.sym
Normal file
33
src/test/ref/call-banked-phi-case-6-close-1.sym
Normal 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 ]
|
144
src/test/ref/call-banked-phi-memvars.asm
Normal file
144
src/test/ref/call-banked-phi-memvars.asm
Normal 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
|
||||
}
|
45
src/test/ref/call-banked-phi-memvars.cfg
Normal file
45
src/test/ref/call-banked-phi-memvars.cfg
Normal 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
|
803
src/test/ref/call-banked-phi-memvars.log
Normal file
803
src/test/ref/call-banked-phi-memvars.log
Normal 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
|
||||
|
33
src/test/ref/call-banked-phi-memvars.sym
Normal file
33
src/test/ref/call-banked-phi-memvars.sym
Normal 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 ]
|
@ -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
|
||||
// }
|
||||
|
@ -1,4 +1,3 @@
|
||||
Warning! Unknown #pragma nobank
|
||||
|
||||
CONTROL FLOW GRAPH SSA
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user