mirror of
https://gitlab.com/camelot/kickc.git
synced 2025-01-11 04:29:53 +00:00
The first small test of calling convention VAR is working. TODO: return values, live ranges, allow registers for parameter/return transfer, entry points.
This commit is contained in:
parent
6cc6242a54
commit
2479fc08db
@ -310,7 +310,11 @@ public class Compiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
new Pass1CallVoidReturns(program).execute();
|
new Pass1CallVoidReturns(program).execute();
|
||||||
|
new Pass1CallStackVar(program).execute();
|
||||||
|
//getLog().append("PROCEDURE CALLS");
|
||||||
|
//getLog().append(program.getGraph().toString(program));
|
||||||
new Pass1CallStack(program).execute();
|
new Pass1CallStack(program).execute();
|
||||||
|
new Pass1CallVar(program).execute();
|
||||||
new Pass1CallPhiParameters(program).execute();
|
new Pass1CallPhiParameters(program).execute();
|
||||||
//getLog().append("PROCEDURE PARAMETERS");
|
//getLog().append("PROCEDURE PARAMETERS");
|
||||||
//getLog().append(program.getGraph().toString(program));
|
//getLog().append(program.getGraph().toString(program));
|
||||||
|
@ -151,7 +151,7 @@ public class ControlFlowGraph implements Serializable {
|
|||||||
public List<ControlFlowBlock> getEntryPointBlocks(Program program) {
|
public List<ControlFlowBlock> getEntryPointBlocks(Program program) {
|
||||||
List<ControlFlowBlock> entryPointBlocks = new ArrayList<>();
|
List<ControlFlowBlock> entryPointBlocks = new ArrayList<>();
|
||||||
for(Procedure procedure : program.getScope().getAllProcedures(true)) {
|
for(Procedure procedure : program.getScope().getAllProcedures(true)) {
|
||||||
if(ProcedureUtils.isEntrypoint(procedure.getRef(), program) || Procedure.CallingConvention.STACK_CALL.equals(procedure.getCallingConvention())) {
|
if(ProcedureUtils.isEntrypoint(procedure.getRef(), program) || Procedure.CallingConvention.STACK_CALL.equals(procedure.getCallingConvention()) || Procedure.CallingConvention.VAR_CALL.equals(procedure.getCallingConvention())) {
|
||||||
// Address-of is used on the procedure
|
// Address-of is used on the procedure
|
||||||
Label procedureLabel = procedure.getLabel();
|
Label procedureLabel = procedure.getLabel();
|
||||||
ControlFlowBlock procedureBlock = getBlock(procedureLabel.getRef());
|
ControlFlowBlock procedureBlock = getBlock(procedureLabel.getRef());
|
||||||
|
@ -40,10 +40,12 @@ public class Procedure extends Scope {
|
|||||||
|
|
||||||
/** The method for passing parameters and return value to the procedure. */
|
/** The method for passing parameters and return value to the procedure. */
|
||||||
public enum CallingConvention {
|
public enum CallingConvention {
|
||||||
/** Parameters and return value handled through call PHI-transitions. */
|
/** Parameters and return value handled through PHI-transitions. */
|
||||||
PHI_CALL("__phicall"),
|
PHI_CALL("__phicall"),
|
||||||
/** Parameters and return value over the stack. */
|
/** Parameters and return value over the stack. */
|
||||||
STACK_CALL("__stackcall");
|
STACK_CALL("__stackcall"),
|
||||||
|
/** Parameters and return value handled through variables. */
|
||||||
|
VAR_CALL("__varcall");
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
|
@ -29,4 +29,8 @@ public class ParamValue implements RValue {
|
|||||||
return "param("+parameter.toString(program)+")";
|
return "param("+parameter.toString(program)+")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return toString(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ ADDRESS_MAINMEM: '__mem' ;
|
|||||||
FORM_SSA: '__ssa' ;
|
FORM_SSA: '__ssa' ;
|
||||||
FORM_MA: '__ma' ;
|
FORM_MA: '__ma' ;
|
||||||
INTRINSIC: '__intrinsic' ;
|
INTRINSIC: '__intrinsic' ;
|
||||||
CALLINGCONVENTION: '__stackcall' | '__phicall' ;
|
CALLINGCONVENTION: '__stackcall' | '__phicall' | '__varcall' ;
|
||||||
IF: 'if' ;
|
IF: 'if' ;
|
||||||
ELSE: 'else' ;
|
ELSE: 'else' ;
|
||||||
WHILE: 'while' ;
|
WHILE: 'while' ;
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@ -19,7 +19,7 @@ import dk.camelot64.kickc.passes.utils.SizeOfConstants;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/** Handle calling convention {@link Procedure.CallingConvention#STACK_CALL} by converting the making control flow graph and symbols calling convention specific. */
|
/** Handle calling convention {@link Procedure.CallingConvention#STACK_CALL} by converting parameter passing / return values to stack operations. */
|
||||||
public class Pass1CallStack extends Pass2SsaOptimization {
|
public class Pass1CallStack extends Pass2SsaOptimization {
|
||||||
|
|
||||||
public Pass1CallStack(Program program) {
|
public Pass1CallStack(Program program) {
|
||||||
@ -53,41 +53,6 @@ public class Pass1CallStack extends Pass2SsaOptimization {
|
|||||||
if(createStackBase)
|
if(createStackBase)
|
||||||
CallingConventionStack.getStackBaseConstant(getScope());
|
CallingConventionStack.getStackBaseConstant(getScope());
|
||||||
|
|
||||||
// Set variables modified in STACK_CALL procedures to load/store
|
|
||||||
for(Procedure procedure : getScope().getAllProcedures(true)) {
|
|
||||||
if(Procedure.CallingConvention.STACK_CALL.equals(procedure.getCallingConvention())) {
|
|
||||||
Set<VariableRef> modifiedVars = getProgram().getProcedureModifiedVars().getModifiedVars(procedure.getRef());
|
|
||||||
for(VariableRef modifiedVar : modifiedVars) {
|
|
||||||
final Variable variable = getScope().getVariable(modifiedVar);
|
|
||||||
if(variable.isKindPhiMaster()) {
|
|
||||||
getLog().append("Converting PHI-variable modified inside __stackcall procedure "+procedure.getFullName()+"() to load/store "+variable.toString(getProgram()));
|
|
||||||
variable.setKind(Variable.Kind.LOAD_STORE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transform STACK_CALL calls to call-prepare, call-execute, call-finalize
|
|
||||||
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
|
|
||||||
ListIterator<Statement> stmtIt = block.getStatements().listIterator();
|
|
||||||
while(stmtIt.hasNext()) {
|
|
||||||
Statement statement = stmtIt.next();
|
|
||||||
if(statement instanceof StatementCall) {
|
|
||||||
StatementCall call = (StatementCall) statement;
|
|
||||||
ProcedureRef procedureRef = call.getProcedure();
|
|
||||||
Procedure procedure = getScope().getProcedure(procedureRef);
|
|
||||||
if(Procedure.CallingConvention.STACK_CALL.equals(procedure.getCallingConvention())) {
|
|
||||||
boolean hasPrepare = (call.getParameters().size() > 0) || !SymbolType.VOID.equals(procedure.getReturnType());
|
|
||||||
stmtIt.remove();
|
|
||||||
stmtIt.add(new StatementCallPrepare(procedureRef, call.getParameters(), call.getSource(), hasPrepare?call.getComments():Comment.NO_COMMENTS));
|
|
||||||
stmtIt.add(new StatementCallExecute(procedureRef, call.getSource(), hasPrepare?Comment.NO_COMMENTS:call.getComments()));
|
|
||||||
stmtIt.add(new StatementCallFinalize(call.getlValue(), procedureRef, call.getSource(), Comment.NO_COMMENTS));
|
|
||||||
getLog().append("Calling convention " + Procedure.CallingConvention.STACK_CALL + " adding prepare/execute/finalize for " + call.toString(getProgram(), false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert param(xxx) to stackidx(PARAM_X) = xxx
|
// Convert param(xxx) to stackidx(PARAM_X) = xxx
|
||||||
if(offsetConstants.size() > 0) {
|
if(offsetConstants.size() > 0) {
|
||||||
ProgramValueIterator.execute(getGraph(), (programValue, currentStmt, stmtIt, currentBlock) -> {
|
ProgramValueIterator.execute(getGraph(), (programValue, currentStmt, stmtIt, currentBlock) -> {
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
package dk.camelot64.kickc.passes;
|
||||||
|
|
||||||
|
import dk.camelot64.kickc.model.Comment;
|
||||||
|
import dk.camelot64.kickc.model.ControlFlowBlock;
|
||||||
|
import dk.camelot64.kickc.model.Program;
|
||||||
|
import dk.camelot64.kickc.model.statements.*;
|
||||||
|
import dk.camelot64.kickc.model.symbols.Procedure;
|
||||||
|
import dk.camelot64.kickc.model.symbols.Variable;
|
||||||
|
import dk.camelot64.kickc.model.types.SymbolType;
|
||||||
|
import dk.camelot64.kickc.model.values.ProcedureRef;
|
||||||
|
import dk.camelot64.kickc.model.values.VariableRef;
|
||||||
|
|
||||||
|
import java.util.ListIterator;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/** Handle calling conventions {@link Procedure.CallingConvention#STACK_CALL} {@link Procedure.CallingConvention#VAR_CALL} by converting to call-prepare, call-execute, call-finalize */
|
||||||
|
public class Pass1CallStackVar extends Pass2SsaOptimization {
|
||||||
|
|
||||||
|
public Pass1CallStackVar(Program program) {
|
||||||
|
super(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean step() {
|
||||||
|
|
||||||
|
// Set variables modified in STACK_CALL/VAR_CALL procedures to load/store
|
||||||
|
for(Procedure procedure : getScope().getAllProcedures(true)) {
|
||||||
|
if(Procedure.CallingConvention.STACK_CALL.equals(procedure.getCallingConvention()) || Procedure.CallingConvention.VAR_CALL.equals(procedure.getCallingConvention())) {
|
||||||
|
Set<VariableRef> modifiedVars = getProgram().getProcedureModifiedVars().getModifiedVars(procedure.getRef());
|
||||||
|
for(VariableRef modifiedVar : modifiedVars) {
|
||||||
|
final Variable variable = getScope().getVariable(modifiedVar);
|
||||||
|
if(variable.isKindPhiMaster()) {
|
||||||
|
getLog().append("Converting PHI-variable modified inside "+procedure.getCallingConvention().getName()+" procedure "+procedure.getFullName()+"() to load/store "+variable.toString(getProgram()));
|
||||||
|
variable.setKind(Variable.Kind.LOAD_STORE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform STACK_CALL/VAR_CALL calls to call-prepare, call-execute, call-finalize
|
||||||
|
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
|
||||||
|
ListIterator<Statement> stmtIt = block.getStatements().listIterator();
|
||||||
|
while(stmtIt.hasNext()) {
|
||||||
|
Statement statement = stmtIt.next();
|
||||||
|
if(statement instanceof StatementCall) {
|
||||||
|
StatementCall call = (StatementCall) statement;
|
||||||
|
ProcedureRef procedureRef = call.getProcedure();
|
||||||
|
Procedure procedure = getScope().getProcedure(procedureRef);
|
||||||
|
if(Procedure.CallingConvention.STACK_CALL.equals(procedure.getCallingConvention()) || Procedure.CallingConvention.VAR_CALL.equals(procedure.getCallingConvention())) {
|
||||||
|
boolean hasPrepare = (call.getParameters().size() > 0) || !SymbolType.VOID.equals(procedure.getReturnType());
|
||||||
|
stmtIt.remove();
|
||||||
|
stmtIt.add(new StatementCallPrepare(procedureRef, call.getParameters(), call.getSource(), hasPrepare?call.getComments():Comment.NO_COMMENTS));
|
||||||
|
stmtIt.add(new StatementCallExecute(procedureRef, call.getSource(), hasPrepare?Comment.NO_COMMENTS:call.getComments()));
|
||||||
|
stmtIt.add(new StatementCallFinalize(call.getlValue(), procedureRef, call.getSource(), Comment.NO_COMMENTS));
|
||||||
|
getLog().append("Calling convention " + procedure.getCallingConvention().getName() + " adding prepare/execute/finalize for " + call.toString(getProgram(), false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
145
src/main/java/dk/camelot64/kickc/passes/Pass1CallVar.java
Normal file
145
src/main/java/dk/camelot64/kickc/passes/Pass1CallVar.java
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
package dk.camelot64.kickc.passes;
|
||||||
|
|
||||||
|
import dk.camelot64.kickc.model.Comment;
|
||||||
|
import dk.camelot64.kickc.model.ControlFlowBlock;
|
||||||
|
import dk.camelot64.kickc.model.InternalError;
|
||||||
|
import dk.camelot64.kickc.model.Program;
|
||||||
|
import dk.camelot64.kickc.model.iterator.ProgramValueIterator;
|
||||||
|
import dk.camelot64.kickc.model.statements.*;
|
||||||
|
import dk.camelot64.kickc.model.symbols.Procedure;
|
||||||
|
import dk.camelot64.kickc.model.symbols.Scope;
|
||||||
|
import dk.camelot64.kickc.model.symbols.Variable;
|
||||||
|
import dk.camelot64.kickc.model.types.SymbolType;
|
||||||
|
import dk.camelot64.kickc.model.types.SymbolTypeInference;
|
||||||
|
import dk.camelot64.kickc.model.values.LValue;
|
||||||
|
import dk.camelot64.kickc.model.values.ParamValue;
|
||||||
|
import dk.camelot64.kickc.model.values.RValue;
|
||||||
|
import dk.camelot64.kickc.model.values.VariableRef;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ListIterator;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/** Handle calling convention {@link Procedure.CallingConvention#VAR_CALL } by converting the making control flow graph and symbols calling convention specific. */
|
||||||
|
public class Pass1CallVar extends Pass2SsaOptimization {
|
||||||
|
|
||||||
|
public Pass1CallVar(Program program) {
|
||||||
|
super(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean step() {
|
||||||
|
|
||||||
|
// Set all parameter/return variables in VAR_CALL procedures to LOAD/STORE
|
||||||
|
for(Procedure procedure : getScope().getAllProcedures(true)) {
|
||||||
|
if(Procedure.CallingConvention.VAR_CALL.equals(procedure.getCallingConvention())) {
|
||||||
|
for(Variable parameter : procedure.getParameters()) {
|
||||||
|
parameter.setKind(Variable.Kind.LOAD_STORE);
|
||||||
|
getLog().append("Converting parameter in "+procedure.getCallingConvention().getName()+" procedure to load/store "+parameter.toString(getProgram()));
|
||||||
|
}
|
||||||
|
if(!SymbolType.VOID.equals(procedure.getReturnType())) {
|
||||||
|
Variable returnVar = procedure.getLocalVariable("return");
|
||||||
|
returnVar.setKind(Variable.Kind.LOAD_STORE);
|
||||||
|
getLog().append("Converting return in "+procedure.getCallingConvention().getName()+" procedure to load/store "+returnVar.toString(getProgram()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert param(xxx) to ??? = xxx
|
||||||
|
ProgramValueIterator.execute(getGraph(), (programValue, currentStmt, stmtIt, currentBlock) -> {
|
||||||
|
if(programValue.get() instanceof ParamValue) {
|
||||||
|
// Convert ParamValues to calling-convention specific param-value
|
||||||
|
ParamValue paramValue = (ParamValue) programValue.get();
|
||||||
|
VariableRef parameterRef = paramValue.getParameter();
|
||||||
|
SymbolType parameterType = SymbolTypeInference.inferType(getScope(), paramValue.getParameter());
|
||||||
|
final Variable paramVar = getScope().getVariable(parameterRef);
|
||||||
|
final Scope blockScope = paramVar.getScope();
|
||||||
|
if(blockScope instanceof Procedure) {
|
||||||
|
Procedure procedure = (Procedure) blockScope;
|
||||||
|
if(Procedure.CallingConvention.VAR_CALL.equals(procedure.getCallingConvention())) {
|
||||||
|
throw new InternalError(paramValue.toString());
|
||||||
|
//programValue.set(stackIdxValue);
|
||||||
|
//getLog().append("Calling convention " + Procedure.CallingConvention.STACK_CALL + " replacing " + paramValue.toString(getProgram()) + " with " + stackIdxValue.toString(getProgram()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Convert procedure return xxx to proc.return = xxx;
|
||||||
|
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
|
||||||
|
ListIterator<Statement> stmtIt = block.getStatements().listIterator();
|
||||||
|
while(stmtIt.hasNext()) {
|
||||||
|
Statement statement = stmtIt.next();
|
||||||
|
if(statement instanceof StatementReturn) {
|
||||||
|
final Scope blockScope = getScope().getScope(block.getScope());
|
||||||
|
if(blockScope instanceof Procedure) {
|
||||||
|
Procedure procedure = (Procedure) blockScope;
|
||||||
|
final SymbolType returnType = procedure.getReturnType();
|
||||||
|
if(!SymbolType.VOID.equals(returnType) && Procedure.CallingConvention.VAR_CALL.equals(procedure.getCallingConvention())) {
|
||||||
|
final RValue value = ((StatementReturn) statement).getValue();
|
||||||
|
//stmtIt.previous();
|
||||||
|
//generateStackReturnValues(value, returnType, returnOffsetConstant, statement.getSource(), statement.getComments(), stmtIt);
|
||||||
|
//stmtIt.next();
|
||||||
|
//((StatementReturn) statement).setValue(null);
|
||||||
|
throw new InternalError(statement.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert xxx = callfinalize to xxx = proc.return
|
||||||
|
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
|
||||||
|
ListIterator<Statement> stmtIt = block.getStatements().listIterator();
|
||||||
|
while(stmtIt.hasNext()) {
|
||||||
|
Statement statement = stmtIt.next();
|
||||||
|
if(statement instanceof StatementCallFinalize) {
|
||||||
|
final StatementCallFinalize call = (StatementCallFinalize) statement;
|
||||||
|
Procedure procedure = getScope().getProcedure(call.getProcedure());
|
||||||
|
final SymbolType returnType = procedure.getReturnType();
|
||||||
|
if(Procedure.CallingConvention.VAR_CALL.equals(procedure.getCallingConvention())) {
|
||||||
|
final StatementSource source = call.getSource();
|
||||||
|
final List<Comment> comments = call.getComments();
|
||||||
|
final LValue lValue = call.getlValue();
|
||||||
|
if(lValue!=null) {
|
||||||
|
Variable returnVar = procedure.getLocalVariable("return");
|
||||||
|
stmtIt.previous();
|
||||||
|
stmtIt.add(new StatementAssignment(lValue, returnVar.getRef(), call.isInitialAssignment(), source, comments));
|
||||||
|
stmtIt.next();
|
||||||
|
}
|
||||||
|
stmtIt.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert callprepare(xxx,yyy,) to proc.param = xxx, ...;
|
||||||
|
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
|
||||||
|
ListIterator<Statement> stmtIt = block.getStatements().listIterator();
|
||||||
|
while(stmtIt.hasNext()) {
|
||||||
|
Statement statement = stmtIt.next();
|
||||||
|
if(statement instanceof StatementCallPrepare) {
|
||||||
|
final StatementCallPrepare call = (StatementCallPrepare) statement;
|
||||||
|
Procedure procedure = getScope().getProcedure(call.getProcedure());
|
||||||
|
if(Procedure.CallingConvention.VAR_CALL.equals(procedure.getCallingConvention())) {
|
||||||
|
stmtIt.previous();
|
||||||
|
final StatementSource source = call.getSource();
|
||||||
|
List<Comment> comments = call.getComments();
|
||||||
|
final List<Variable> parameterDefs = procedure.getParameters();
|
||||||
|
for(int i = 0; i < parameterDefs.size(); i++) {
|
||||||
|
final RValue parameterVal = call.getParameters().get(i);
|
||||||
|
final Variable parameterDef = parameterDefs.get(i);
|
||||||
|
stmtIt.add(new StatementAssignment(parameterDef.getVariableRef(), parameterVal, false, source, comments));
|
||||||
|
comments = Comment.NO_COMMENTS;
|
||||||
|
}
|
||||||
|
stmtIt.next();
|
||||||
|
stmtIt.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -48,6 +48,11 @@ public class TestPrograms {
|
|||||||
// compileAndCompare("unknown-var-problem.c", log().verboseParse());
|
// compileAndCompare("unknown-var-problem.c", log().verboseParse());
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVarCall1() throws IOException, URISyntaxException {
|
||||||
|
compileAndCompare("varcall-1.c", log());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConstVolatileProblem1() throws IOException, URISyntaxException {
|
public void testConstVolatileProblem1() throws IOException, URISyntaxException {
|
||||||
compileAndCompare("const-volatile-problem.c");
|
compileAndCompare("const-volatile-problem.c");
|
||||||
|
12
src/test/kc/varcall-1.c
Normal file
12
src/test/kc/varcall-1.c
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// Test __varcall calling convention
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
setbg(0);
|
||||||
|
setbg(0x0b);
|
||||||
|
}
|
||||||
|
|
||||||
|
char * const BGCOL = 0xd021;
|
||||||
|
|
||||||
|
__varcall void setbg(char col) {
|
||||||
|
*BGCOL = col;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user