1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-06-03 07:29:37 +00:00

far calls

This commit is contained in:
Flight_Control 2022-11-15 17:32:04 +01:00
parent 152832fd4e
commit 828e38a8b3
20 changed files with 2554 additions and 4 deletions

487
gen/KickCLexer.interp Normal file

File diff suppressed because one or more lines are too long

1596
gen/KickCLexer.java Normal file

File diff suppressed because it is too large Load Diff

225
gen/KickCLexer.tokens Normal file
View File

@ -0,0 +1,225 @@
TYPEDEFNAME=1
CURLY_BEGIN=2
CURLY_END=3
BRACKET_BEGIN=4
BRACKET_END=5
PAR_BEGIN=6
PAR_END=7
SEMICOLON=8
COLON=9
COMMA=10
RANGE=11
PARAM_LIST=12
CONDITION=13
DOT=14
ARROW=15
PLUS=16
MINUS=17
ASTERISK=18
DIVIDE=19
MODULO=20
INC=21
DEC=22
AND=23
BIT_NOT=24
BIT_XOR=25
BIT_OR=26
SHIFT_LEFT=27
SHIFT_RIGHT=28
EQUAL=29
NOT_EQUAL=30
LESS_THAN=31
LESS_THAN_EQUAL=32
GREATER_THAN_EQUAL=33
GREATER_THAN=34
LOGIC_AND=35
LOGIC_OR=36
ASSIGN=37
ASSIGN_COMPOUND=38
TYPEDEF=39
CONST=40
EXTERN=41
EXPORT=42
ALIGN=43
INLINE=44
VOLATILE=45
STATIC=46
INTERRUPT=47
REGISTER=48
LOCAL_RESERVE=49
ADDRESS=50
ADDRESS_ZEROPAGE=51
ADDRESS_MAINMEM=52
FAR=53
FORM_SSA=54
FORM_MA=55
INTRINSIC=56
CALLINGCONVENTION=57
IF=58
ELSE=59
WHILE=60
DO=61
FOR=62
SWITCH=63
RETURN=64
BREAK=65
CONTINUE=66
GOTO=67
ASM=68
DEFAULT=69
CASE=70
STRUCT=71
UNION=72
ENUM=73
SIZEOF=74
TYPEID=75
DEFINED=76
KICKASM=77
LOGIC_NOT=78
SIMPLETYPE=79
BOOLEAN=80
KICKASM_BODY=81
IMPORT=82
INCLUDE=83
PRAGMA=84
DEFINE=85
DEFINE_CONTINUE=86
UNDEF=87
IFDEF=88
IFNDEF=89
IFIF=90
ELIF=91
IFELSE=92
ENDIF=93
ERROR=94
TOKEN_STRINGIZE=95
TOKEN_MERGE=96
NUMBER=97
NUMFLOAT=98
BINFLOAT=99
DECFLOAT=100
HEXFLOAT=101
NUMINT=102
BININTEGER=103
DECINTEGER=104
HEXINTEGER=105
NAME=106
STRING=107
CHAR=108
WS=109
COMMENT_LINE=110
COMMENT_BLOCK=111
ASM_BYTE=112
ASM_MNEMONIC=113
ASM_IMM=114
ASM_COLON=115
ASM_COMMA=116
ASM_PAR_BEGIN=117
ASM_PAR_END=118
ASM_BRACKET_BEGIN=119
ASM_BRACKET_END=120
ASM_DOT=121
ASM_SHIFT_LEFT=122
ASM_SHIFT_RIGHT=123
ASM_PLUS=124
ASM_MINUS=125
ASM_LESS_THAN=126
ASM_GREATER_THAN=127
ASM_MULTIPLY=128
ASM_DIVIDE=129
ASM_CURLY_BEGIN=130
ASM_CURLY_END=131
ASM_NUMBER=132
ASM_NUMFLOAT=133
ASM_BINFLOAT=134
ASM_DECFLOAT=135
ASM_HEXFLOAT=136
ASM_NUMINT=137
ASM_BININTEGER=138
ASM_DECINTEGER=139
ASM_HEXINTEGER=140
ASM_CHAR=141
ASM_MULTI_REL=142
ASM_MULTI_NAME=143
ASM_NAME=144
ASM_TAG=145
ASM_WS=146
ASM_COMMENT_LINE=147
ASM_COMMENT_BLOCK=148
IMPORT_SYSTEMFILE=149
IMPORT_LOCALFILE=150
IMPORT_WS=151
IMPORT_COMMENT_LINE=152
IMPORT_COMMENT_BLOCK=153
';'=8
'..'=11
'...'=12
'?'=13
'->'=15
'%'=20
'++'=21
'--'=22
'&'=23
'~'=24
'^'=25
'|'=26
'=='=29
'!='=30
'<='=32
'>='=33
'&&'=35
'||'=36
'='=37
'typedef'=39
'const'=40
'extern'=41
'__export'=42
'__align'=43
'inline'=44
'volatile'=45
'static'=46
'__interrupt'=47
'register'=48
'__zp_reserve'=49
'__address'=50
'__zp'=51
'__mem'=52
'__far'=53
'__ssa'=54
'__ma'=55
'__intrinsic'=56
'if'=58
'else'=59
'while'=60
'do'=61
'for'=62
'switch'=63
'return'=64
'break'=65
'continue'=66
'goto'=67
'asm'=68
'default'=69
'case'=70
'struct'=71
'union'=72
'enum'=73
'sizeof'=74
'typeid'=75
'defined'=76
'kickasm'=77
'!'=78
'#import'=82
'#include'=83
'#pragma'=84
'#define'=85
'#undef'=87
'#ifdef'=88
'#ifndef'=89
'#if'=90
'#elif'=91
'#else'=92
'#endif'=93
'#error'=94
'.byte'=112
'#'=114

View File

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

View File

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

View File

@ -0,0 +1,4 @@
jsr $FF6E // https://github.com/commanderx16/x16-docs/blob/master/X16%20Reference%20-%2004%20-%20KERNAL.md#function-name-jsrfar
.byte <{la1}
.byte >{la1}
.byte {c1}

View File

@ -278,6 +278,7 @@ public class Compiler {
getLog().append(program.getGraph().toString(program));
}
new Pass1ProcedureInline(program).execute();
new Pass1ProcedureFar(program).execute(); // Implements far calls to procedures defined in a bank.
new PassNStatementIndices(program).step();
program.clearCallGraph();
new Pass1AssertNoRecursion(program).execute();

View File

@ -12,6 +12,7 @@ import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.values.*;
import dk.camelot64.kickc.parser.KickCParser;
import dk.camelot64.kickc.parser.KickCParserBaseVisitor;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
import java.util.ArrayList;
@ -250,9 +251,13 @@ public class AsmFragmentInstance {
@Override
public Object visitAsmBytes(KickCParser.AsmBytesContext ctx) {
List<KickCParser.AsmExprContext> asmExpr = ctx.asmExpr();
ArrayList<String> values = new ArrayList<>();
for(int i = 1; i < ctx.getChildCount(); i = i + 2) {
values.add(ctx.getChild(i).getText());
for(int i = 0; i < asmExpr.size(); i++) {
if(asmExpr.get(i) != null) {
AsmParameter par = (AsmParameter)this.visit(asmExpr.get(i));
values.add(par.getParam());
}
}
AsmDataNumeric data = new AsmDataNumeric(null, AsmDataNumeric.Type.BYTE, values);
handleTags(data, ctx.ASM_TAG());

View File

@ -18,6 +18,7 @@ import dk.camelot64.kickc.model.symbols.Symbol;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.types.SymbolTypeInference;
import dk.camelot64.kickc.model.values.*;
import kickass.pass.values.StringValue;
/**
* Creates an ASM Fragment specification for a statement in the control flow graph.
@ -41,6 +42,40 @@ final public class AsmFragmentInstanceSpecBuilder {
return new AsmFragmentInstanceSpec(program, signature, bindings, codeScope);
}
/**
* Create a fragment instance spec factory for a far call entry
*
* @param call The statement call
* @param program The program
* @return the fragment instance spec factory
*/
public static AsmFragmentInstanceSpec farCallEntry(StatementCall call, Program program) {
AsmFragmentBindings bindings = new AsmFragmentBindings(program);
AsmFragmentSignature signature = new AsmFragmentSignature.CallFar(call.getBankFar(), program.getTargetPlatform().getName(), AsmFragmentSignature.CallFar.EntryExit.Entry);
ScopeRef codeScope = program.getScope().getRef();
// ScopeRef codeScope = program.getStatementInfos().getBlock(call).getScope();
bindings.bind("c1", new ConstantInteger(call.getBankFar()));
bindings.bind("la1", new LabelRef(call.getProcedure().getFullName()));
return new AsmFragmentInstanceSpec(program, signature, bindings, codeScope);
}
/**
* Create a fragment instance spec factory for a far call exit
*
* @param call The statement call
* @param program The program
* @return the fragment instance spec factory
*/
public static AsmFragmentInstanceSpec farCallExit(StatementCall call, Program program) {
AsmFragmentBindings bindings = new AsmFragmentBindings(program);
AsmFragmentSignature signature = new AsmFragmentSignature.CallFar(call.getBankFar(), program.getTargetPlatform().getName(), AsmFragmentSignature.CallFar.EntryExit.Exit);
ScopeRef codeScope = program.getStatementInfos().getBlock(call).getScope();
bindings.bind("la1", new LabelRef(codeScope.getFullName()));
bindings.bind("la2", new ConstantInteger(call.getBankFar()));
return new AsmFragmentInstanceSpec(program, signature, bindings, codeScope);
}
/**
* Create a fragment instance spec factory for an interrupt routine entry
*

View File

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

View File

@ -70,6 +70,34 @@ public interface AsmFragmentSignature {
}
}
/**
* ASM fragment signature for a far jsr <code>if(A) goto B</code>.
*/
class CallFar implements AsmFragmentSignature {
final private Long bankFar;
final private String targetPlatform;
public enum EntryExit {
Exit,
Entry
}
final private CallFar.EntryExit entryExit;
public CallFar(Long bankFar, String targetPlatform, CallFar.EntryExit entryExit) {
this.bankFar = bankFar;
this.targetPlatform = targetPlatform;
this.entryExit = entryExit;
}
@Override
public String getName() {
return "call_far" + "_" + targetPlatform + "_" + entryExit.name().toLowerCase();
}
}
/**
* ASM fragment signature for a conditional jump <code>if(A) goto B</code>.
*/

View File

@ -43,6 +43,18 @@ public class Directive {
public Inline() { super("inline"); }
}
/** Function declared far. */
static public class Far extends Directive {
public Long bankFar;
public Far(Long bankFar) {
super("__far");
this.bankFar = bankFar;
}
}
/** Function declared intrinsic. */
public static class Intrinsic extends Directive {
public Intrinsic() { super("intrinsic"); }

View File

@ -28,6 +28,8 @@ public class StatementCall extends StatementBase implements StatementLValue, Sta
private List<RValue> parameters;
/** This is the initial assignment of the lValue. */
private boolean initialAssignment;
/** This contains the far call parameters */
private Long bankFar;
public StatementCall(LValue lValue, String procedureName, List<RValue> parameters, StatementSource source, List<Comment> comments) {
super(source, comments);
@ -64,6 +66,12 @@ public class StatementCall extends StatementBase implements StatementLValue, Sta
this.parameters = parameters;
}
public void setBankFar(Long bankFar) {
this.bankFar = bankFar;
}
public Long getBankFar() { return this.bankFar; }
public int getNumParameters() {
return parameters.size();
}

View File

@ -25,6 +25,10 @@ public class Procedure extends Scope {
private boolean variableLengthParameterList;
/** true if the procedure is declared inline. */
private boolean declaredInline;
/** true if the procedure is declared far. */
private boolean declaredFar;
/** contains the far bank. */
private Long bankFar;
/** True if the procedure is declared intrinsic. */
private boolean declaredIntrinsic;
/** The type of interrupt that the procedure serves. Null for all procedures not serving an interrupt. */
@ -52,6 +56,8 @@ public class Procedure extends Scope {
/** The method for passing parameters and return value to the procedure. */
public enum CallingConvention {
/** Far call in a cx16 bank using the https://github.com/commanderx16/x16-docs/blob/master/X16%20Reference%20-%2004%20-%20KERNAL.md#function-name-jsrfar routine*/
FAR_CALL("__far"),
/** Parameters and return value handled through PHI-transitions. */
PHI_CALL("__phicall"),
/** Parameters and return value over the stack. */
@ -89,6 +95,8 @@ public class Procedure extends Scope {
super(name, parentScope, dataSegment);
this.procedureType = procedureType;
this.declaredInline = false;
this.declaredFar = false;
this.bankFar = 0L;
this.interruptType = null;
this.comments = new ArrayList<>();
this.codeSegment = codeSegment;
@ -198,6 +206,20 @@ public class Procedure extends Scope {
this.declaredInline = declaredInline;
}
public boolean isDeclaredFar() {
return declaredFar;
}
public void setDeclaredFar(boolean declaredFar) {
this.declaredFar = declaredFar;
}
public Long getBankFar() { return this.bankFar; }
public void setBankFar(Long bankFar) {
this.bankFar = bankFar;
}
public String getInterruptType() {
return interruptType;
}
@ -258,6 +280,9 @@ public class Procedure extends Scope {
if(declaredIntrinsic) {
res.append("__intrinsic ");
}
if(declaredFar) {
res.append("__far(").append("bank").append(") ");
}
if(!callingConvention.equals(CallingConvention.PHI_CALL)) {
res.append(getCallingConvention().getName()).append(" ");
}
@ -296,6 +321,7 @@ public class Procedure extends Scope {
Procedure procedure = (Procedure) o;
return variableLengthParameterList == procedure.variableLengthParameterList &&
declaredInline == procedure.declaredInline &&
declaredFar == procedure.declaredFar &&
declaredIntrinsic == procedure.declaredIntrinsic &&
isConstructor == procedure.isConstructor &&
Objects.equals(procedureType, procedure.procedureType) &&

View File

@ -1170,6 +1170,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.Far) {
procedure.setDeclaredFar(true);
procedure.setBankFar(((Directive.Far) directive).bankFar);
} else if(directive instanceof Directive.CallingConvention) {
procedure.setCallingConvention(((Directive.CallingConvention) directive).callingConvention);
} else if(directive instanceof Directive.Interrupt) {
@ -1213,6 +1216,17 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
return new Directive.Inline();
}
@Override
public Object visitDirectiveFar(KickCParser.DirectiveFarContext ctx) {
Long bankFar;
if(ctx.getChildCount() > 1) {
bankFar = Long.valueOf(ctx.getChild(2).getText());
} else {
bankFar = 0L;
}
return new Directive.Far(bankFar);
}
@Override
public Object visitDirectiveIntrinsic(KickCParser.DirectiveIntrinsicContext ctx) {
return new Directive.Intrinsic();

View File

@ -0,0 +1,71 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.InternalError;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.iterator.ProgramValue;
import dk.camelot64.kickc.model.iterator.ProgramValueHandler;
import dk.camelot64.kickc.model.iterator.ProgramValueIterator;
import dk.camelot64.kickc.model.statements.*;
import dk.camelot64.kickc.model.symbols.*;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.values.*;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
/** Pass that modifies a control flow graph to far call any procedures declared as far */
public class Pass1ProcedureFar extends Pass1Base {
public Pass1ProcedureFar(Program program) {
super(program);
}
@Override
public boolean step() {
List<ControlFlowBlock> allBlocks = getGraph().getAllBlocks();
ListIterator<ControlFlowBlock> blocksIt = allBlocks.listIterator();
while(blocksIt.hasNext()) {
ControlFlowBlock block = blocksIt.next();
List<Statement> blockStatements = block.getStatements();
ListIterator<Statement> statementsIt = blockStatements.listIterator();
while(statementsIt.hasNext()) {
Statement statement = statementsIt.next();
if(statement instanceof StatementCall) {
StatementCall call = (StatementCall) statement;
ProcedureRef procedureRef = call.getProcedure();
Procedure procedure = getScope().getProcedure(procedureRef);
if(procedure.isDeclaredFar()) {
if(procedure.getInterruptType()!=null) {
throw new CompileError("Error! Interrupts cannot be far called. "+procedure.getRef().toString());
}
farProcedureCall(call, procedure, statementsIt, block, blocksIt);
// Exit and restart
return false;
}
}
}
}
return false;
}
/**
* Inline a specific call to a procedure.
*
* @param call The call to the far procedure
* @param procedure The procedure being called
* @param statementsIt The statement iterator pointing to the call statement
* @param block The block containing the call
* @param blocksIt The block iterator pointing to the block containing the call
*/
private void farProcedureCall(StatementCall call, Procedure procedure, ListIterator<Statement> statementsIt, ControlFlowBlock block, ListIterator<ControlFlowBlock> blocksIt) {
Scope callScope = getScope().getScope(block.getScope());
// Here we add to the call the properties to build a far call depending on the platform.
// The all properties have been entered in the __far() directive in the source code.
// These properties are then used in pass4 of the compiler, to build the platform dependent fragment to execute the far call.
call.setBankFar(procedure.getBankFar());
getLog().append("Far call " + call.toString(getProgram(), false));
}
}

View File

@ -865,9 +865,22 @@ public class Pass4CodeGeneration {
genBlockPhiTransition(asm, block, callSuccessor, block.getScope());
}
}
asm.addInstruction("jsr", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false);
if(procedure.isDeclaredFar()) {
// Generate ASM for a call (in a bank or other)
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.farCallEntry(call, program), program);
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.farCallExit(call, program), program);
// asm.addInstruction("jsr far", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false);
} else {
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);
if(procedure.isDeclaredFar()) {
// Generate ASM for a far call (in a bank or other)
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.farCallEntry(call, program), program);
AsmFragmentCodeGenerator.generateAsm(asm, AsmFragmentInstanceSpecBuilder.farCallExit(call, program), program);
} else {
asm.addInstruction("jsr", CpuAddressingMode.ABS, call.getProcedure().getFullName(), false);
}
}
} else if (statement instanceof StatementCallExecute) {
StatementCallExecute call = (StatementCallExecute) statement;

View File

@ -0,0 +1,11 @@
// Test a procedure with calling convention stack
char* const SCREEN = (char*)0x0400;
void main(void) {
SCREEN[0] = plus('0', 7);
}
char __far(1) plus(char a, char b) {
return a+b;
}

View File

@ -0,0 +1,11 @@
// Test a procedure with calling convention stack
char* const SCREEN = (char*)0x0400;
void main(void) {
SCREEN[0] = plus('0', 7);
}
char __far(1) __stackcall plus(char a, char b) {
return a+b;
}