1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2025-04-08 14:37:40 +00:00

Implemented function inlining. Closes #63

This commit is contained in:
jespergravgaard 2018-05-01 00:09:51 +02:00
parent b15a88f27e
commit 6bd3a2dedf
9 changed files with 856 additions and 397 deletions

View File

@ -128,6 +128,8 @@ public class Compiler {
new Pass1AssertReturn(program).execute();
new Pass1AssertUsedVars(program).execute();
new Pass1ProcedureInline(program).execute();
new Pass1EliminateUncalledProcedures(program).execute();
new PassNEliminateUnusedVars(program).execute();
new Pass1ExtractInlineStrings(program).execute();
@ -140,8 +142,6 @@ public class Compiler {
new Pass1ModifiedVarsAnalysis(program).execute();
getLog().append(program.getProcedureModifiedVars().toString(program));
new Pass1ProcedureInline(program).execute();
new Pass1ProcedureCallParameters(program).generate();
//getLog().append("CONTROL FLOW GRAPH WITH ASSIGNMENT CALL");
//getLog().append(program.getGraph().toString(program));

View File

@ -226,7 +226,7 @@ public class ControlFlowGraph {
}
/**
* Get all blocks stat are part of the execution of a specific scope. (mostly a procedure)
* Get all blocks that are part of the execution of a specific scope. (mostly a procedure)
*
* @param scope The scope to find blocks for
* @return All blocks that are part of the execution of the scope

View File

@ -45,6 +45,12 @@ public class AliasReplacer implements ValueReplacer.Replacer {
if(alias != null) {
return new ConstantUnary(constantUnary.getOperator(), alias);
}
} else if(rValue instanceof ConstantCastValue) {
ConstantCastValue constantCastValue = (ConstantCastValue) rValue;
ConstantValue alias = (ConstantValue) getReplacement(constantCastValue.getValue(), aliases);
if(alias != null) {
return new ConstantCastValue(constantCastValue.getToType(), alias);
}
} else if(rValue instanceof ConstantBinary) {
ConstantBinary constantBinary = (ConstantBinary) rValue;
ConstantValue aliasLeft = (ConstantValue) getReplacement(constantBinary.getLeft(), aliases);

View File

@ -1,24 +1,20 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.CompileError;
import dk.camelot64.kickc.model.ControlFlowBlock;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.statements.StatementAssignment;
import dk.camelot64.kickc.model.statements.StatementCall;
import dk.camelot64.kickc.model.statements.StatementReturn;
import dk.camelot64.kickc.model.symbols.*;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.values.LValue;
import dk.camelot64.kickc.model.values.ProcedureRef;
import dk.camelot64.kickc.model.values.RValue;
import dk.camelot64.kickc.model.values.VariableRef;
import dk.camelot64.kickc.model.values.*;
import java.util.Collection;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
/** Pass that modifies a control flow graph to inline any procedures declared as inline */
public class Pass1ProcedureInline extends Pass1Base {
public class Pass1ProcedureInline extends Pass1Base {
public Pass1ProcedureInline(Program program) {
super(program);
@ -27,7 +23,9 @@ public class Pass1ProcedureInline extends Pass1Base {
@Override
public boolean step() {
List<ControlFlowBlock> allBlocks = getGraph().getAllBlocks();
for(ControlFlowBlock block : allBlocks) {
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()) {
@ -37,10 +35,80 @@ public class Pass1ProcedureInline extends Pass1Base {
ProcedureRef procedureRef = call.getProcedure();
Procedure procedure = getScope().getProcedure(procedureRef);
if(procedure.isDeclaredInline()) {
Scope callScope = getScope().getScope(block.getScope());
// Remove call
statementsIt.remove();
// Find call serial number (handles when multiple calls to the same procedure is made in the call scope)
int serial = nextSerial(procedure, callScope);
// Copy all procedure symbols
inlineSymbols(procedure, callScope, serial);
// Generate parameter assignments
generateParameterAssignments(statementsIt, call, procedure, callScope, serial);
// Create a new block label for the rest of the calling block
Label restBlockLabel = callScope.addLabelIntermediate();
// Copy all procedure blocks
List<ControlFlowBlock> procedureBlocks = getGraph().getScopeBlocks(procedure.getRef());
for(ControlFlowBlock procedureBlock : procedureBlocks) {
LabelRef procBlockLabelRef = procedureBlock.getLabel();
Symbol procBlockLabel = getScope().getSymbol(procBlockLabelRef);
Label inlinedBlockLabel;
if(procedure.equals(procBlockLabel)) {
inlinedBlockLabel = callScope.getLabel(procedure.getLocalName() + serial);
} else {
String inlinedBlockLabelName = getInlineSymbolName(procedure, procBlockLabel, serial);
inlinedBlockLabel = callScope.getLabel(inlinedBlockLabelName);
}
ControlFlowBlock inlineBlock = new ControlFlowBlock(inlinedBlockLabel.getRef(), callScope.getRef());
blocksIt.add(inlineBlock);
for(Statement procStatement : procedureBlock.getStatements()) {
Statement inlinedStatement = inlineStatement(procStatement, procedure, callScope, serial);
if(inlinedStatement != null) {
inlineBlock.addStatement(inlinedStatement);
}
}
// Set successors
if(procedureBlock.getDefaultSuccessor() != null) {
LabelRef procBlockSuccessorRef = procedureBlock.getDefaultSuccessor();
if(procBlockSuccessorRef.getFullName().equals(SymbolRef.PROCEXIT_BLOCK_NAME)) {
// Set successor of the @return block to the rest block
inlineBlock.setDefaultSuccessor(restBlockLabel.getRef());
} else {
Label procBlockSuccessor = getScope().getLabel(procBlockSuccessorRef);
String inlinedSuccessorName = getInlineSymbolName(procedure, procBlockSuccessor, serial);
Label inlinedSuccessorLabel = callScope.getLabel(inlinedSuccessorName);
inlineBlock.setDefaultSuccessor(inlinedSuccessorLabel.getRef());
}
}
if(procedureBlock.getConditionalSuccessor() != null) {
throw new CompileError("Not implemented Inline function conditional successors");
}
}
// Create a new block for the rest of the calling block
ControlFlowBlock restBlock = new ControlFlowBlock(restBlockLabel.getRef(), callScope.getRef());
blocksIt.add(restBlock);
// Generate return assignment
throw new CompileError("Inline functions not implemented!");
if(call.getlValue() != null) {
Variable procReturnVar = procedure.getVariable("return");
String inlinedReturnVarName = getInlineSymbolName(procedure, procReturnVar, serial);
Variable inlinedReturnVar = callScope.getVariable(inlinedReturnVarName);
restBlock.addStatement(new StatementAssignment(call.getlValue(), inlinedReturnVar.getRef()));
}
// Copy the rest of the calling block to the new block
while(statementsIt.hasNext()) {
Statement restStatement = statementsIt.next();
statementsIt.remove();
restBlock.addStatement(restStatement);
}
// Set the successors for the rest block
restBlock.setDefaultSuccessor(block.getDefaultSuccessor());
restBlock.setConditionalSuccessor(block.getConditionalSuccessor());
// Set default successor to the original block to the inlined procedure block
Label inlinedProcLabel = callScope.getLabel(procedure.getLocalName() + serial);
block.setDefaultSuccessor(inlinedProcLabel.getRef());
// Log the inlining
getLog().append("Inlined call " + call.toString(getProgram(), false));
// Exit and restart
return true;
}
}
}
@ -48,70 +116,152 @@ public class Pass1ProcedureInline extends Pass1Base {
return false;
}
/*
public StatementCall visitCall(StatementCall origCall) {
// Procedure strategy implemented is currently variable-based transfer of parameters/return values
// Generate parameter passing assignments
ProcedureRef procedureRef = origCall.getProcedure();
Procedure procedure = getScope().getProcedure(procedureRef);
/**
* Find the next inline serial number for the procedure being inlined in the calling scope.
* The serial number handles when the same procedure is inlined many times in the same scope.
* Each inlineng gets its own serial - which generates inlined symbol names with different names.
*
* @param procedure The procedure being inlined
* @param callScope the scope it is being inlined into
* @return the next available serial number
*/
private int nextSerial(Procedure procedure, Scope callScope) {
int serial = 1;
while(true) {
String localName = procedure.getLocalName() + serial;
if(callScope.getLabel(localName) == null) {
return serial;
}
serial++;
}
}
/**
* Inline a statement from a procedure being inlined
*
* @param procStatement The statement to inline
* @param procedure The procedure being inlined
* @param callScope The scope where the procedure is being inlined
* @param serial The serial number (counted up for each inlined call to the same function within the called calling scope).
* @return A new statement that has inlined all references to variables etc.
*/
private Statement inlineStatement(Statement procStatement, Procedure procedure, Scope callScope, int serial) {
Statement inlinedStatement;
if(procStatement instanceof StatementAssignment) {
StatementAssignment procAssignment = (StatementAssignment) procStatement;
LValue inlineLValue = inlineLValue(procAssignment.getlValue(), procedure, callScope, serial);
RValue inlineRValue1 = inlineRValue(procAssignment.getrValue1(), procedure, callScope, serial);
RValue inlineRValue2 = inlineRValue(procAssignment.getrValue2(), procedure, callScope, serial);
inlinedStatement = new StatementAssignment(inlineLValue, inlineRValue1, procAssignment.getOperator(), inlineRValue2);
} else if(procStatement instanceof StatementReturn) {
// No statement needed
return null;
} else {
throw new CompileError("Statement type of Inline function not handled " + procStatement);
}
return inlinedStatement;
}
/**
* Find/create a new LValue to use when inlining an LValue from a procedure
*
* @param lValue The LValue bein used inside the procedure being inlined
* @param serial The serial number (counted up for each inlined call to the same function within the called calling scope).
* @return The LValue to use where the procedure is inlined.
*/
private LValue inlineLValue(LValue lValue, Procedure procedure, Scope callScope, int serial) {
return (LValue) inlineRValue(lValue, procedure, callScope, serial);
}
/**
* Find/create a new RValue to use when inlining an RValue from a procedure
*
* @param rValue The RValue bein used inside the procedure being inlined
* @param serial The serial number (counted up for each inlined call to the same function within the called calling scope).
* @return The RValue to use where the procedure is inlined.
*/
private RValue inlineRValue(RValue rValue, Procedure procedure, Scope callScope, int serial) {
if(rValue == null) {
return null;
} else if(rValue instanceof VariableRef) {
VariableRef procVarRef = (VariableRef) rValue;
Variable procVar = getScope().getVariable(procVarRef);
if(procVar.getScope().equals(procedure)) {
String inlineSymbolName = getInlineSymbolName(procedure, procVar, serial);
Variable inlineVar = callScope.getVariable(inlineSymbolName);
return inlineVar.getRef();
} else {
return procVarRef;
}
} else if(rValue instanceof ConstantValue) {
return rValue;
} else {
throw new CompileError("Symbol Type of Inline function not handled " + rValue);
}
}
/**
* Generate assignments for the parameters of the inlined function.
*
* @param statementsIt The statements iterator to add the assignments for
* @param call The call to the procedure being inlined
* @param procedure The procedure being inlined
* @param callScope The scope where the function is being inlined
* @param serial The serial number (counted up for each inlined call to the same function within the called calling scope).
*/
private void generateParameterAssignments(ListIterator<Statement> statementsIt, StatementCall call, Procedure procedure, Scope callScope, int serial) {
List<Variable> parameterDecls = procedure.getParameters();
List<RValue> parameterValues = origCall.getParameters();
List<RValue> parameterValues = call.getParameters();
for(int i = 0; i < parameterDecls.size(); i++) {
Variable parameterDecl = parameterDecls.get(i);
String inlineParameterVarName = getInlineSymbolName(procedure, parameterDecl, serial);
Variable inlineParameterVar = callScope.getVariable(inlineParameterVarName);
RValue parameterValue = parameterValues.get(i);
addStatementToCurrentBlock(new StatementAssignment(parameterDecl.getRef(), parameterValue));
statementsIt.add(new StatementAssignment(inlineParameterVar.getRef(), parameterValue));
}
String procedureName = origCall.getProcedureName();
Variable procReturnVar = procedure.getVariable("return");
VariableRef procReturnVarRef = null;
if(procReturnVar != null) {
procReturnVarRef = procReturnVar.getRef();
}
StatementCall copyCall = new StatementCall(procReturnVarRef, procedureName, null);
copyCall.setParametersByAssignment(true);
copyCall.setProcedure(procedureRef);
addStatementToCurrentBlock(copyCall);
getCurrentBlock().setCallSuccessor(procedure.getLabel().getRef());
Symbol currentBlockSymbol = getScope().getSymbol(getCurrentBlock().getLabel());
Scope currentBlockScope;
if(currentBlockSymbol instanceof Procedure) {
currentBlockScope = (Scope) currentBlockSymbol;
} else {
currentBlockScope = currentBlockSymbol.getScope();
}
splitCurrentBlock(currentBlockScope.addLabelIntermediate().getRef());
if(!SymbolType.VOID.equals(procedure.getReturnType()) && origCall.getlValue() != null) {
addStatementToCurrentBlock(new StatementAssignment(origCall.getlValue(), procReturnVarRef));
} else {
// No return type. Remove variable receiving the result.
LValue lValue = origCall.getlValue();
if(lValue instanceof VariableRef) {
VariableRef lValueRef = (VariableRef) lValue;
Variable lValueVar = getScope().getVariable(lValueRef);
lValueVar.getScope().remove(lValueVar);
}
/**
* Copy all symbols of the procedure being inlined to the calling scope.
*
* @param procedure The procedure being inlined
* @param callScope The calling scope
* @param serial The serial number (counted up for each inlined call to the same function within the called calling scope).
*/
private void inlineSymbols(Procedure procedure, Scope callScope, int serial) {
// Add a label for the produre itself
callScope.addLabel(procedure.getLocalName() + serial);
// And copy all procedure symbols
for(Symbol procSymbol : procedure.getAllSymbols()) {
if(procSymbol instanceof Variable) {
Variable procVar = (Variable) procSymbol;
String inlineVarName = getInlineSymbolName(procedure, procSymbol, serial);
VariableUnversioned inlineVar = callScope.addVariable(inlineVarName, procSymbol.getType());
inlineVar.setInferredType(procVar.isInferredType());
inlineVar.setDeclaredAlignment(procVar.getDeclaredAlignment());
inlineVar.setDeclaredConstant(procVar.isDeclaredConstant());
inlineVar.setDeclaredRegister(procVar.getDeclaredRegister());
} else if(procSymbol instanceof Label) {
String inlineLabelName = getInlineSymbolName(procedure, procSymbol, serial);
callScope.addLabel(inlineLabelName);
} else {
throw new CompileError("Symbol Type of Inline function not handled " + procSymbol);
}
}
// Add self-assignments for all variables modified in the procedure
Set<VariableRef> modifiedVars = program.getProcedureModifiedVars().getModifiedVars(procedure.getRef());
for(VariableRef modifiedVar : modifiedVars) {
addStatementToCurrentBlock(new StatementAssignment(modifiedVar, modifiedVar));
}
return null;
}
@Override
public StatementReturn visitReturn(StatementReturn origReturn) {
ControlFlowBlock currentBlock = getCurrentBlock();
String currentProcName = currentBlock.getLabel().getScopeNames();
Procedure procedure = program.getScope().getProcedure(currentProcName);
// Add self-assignments for all variables modified in the procedure
Set<VariableRef> modifiedVars = program.getProcedureModifiedVars().getModifiedVars(procedure.getRef());
for(VariableRef modifiedVar : modifiedVars) {
addStatementToCurrentBlock(new StatementAssignment(modifiedVar, modifiedVar));
}
return super.visitReturn(origReturn);
/**
* Get the new name of a symbol from the procedure being inlined .
* The name is &lt;proc&gt;_&lt;symb&gt;, where &lt;proc&gt; is the name of the procedure and &lt;symb&gt; is the local name of the symbol.
*
* @param procedure The procedure being inlined
* @param procSymbol The symbol from the inlined procedure
* @param serial The serial number (counted up for each inlined call to the same function within the called calling scope).
* @return The new name used for the symbol, where it is inlined.
*/
private String getInlineSymbolName(Procedure procedure, Symbol procSymbol, int serial) {
return procedure.getLocalName() + serial + "_" + procSymbol.getLocalName();
}
*/
}

View File

@ -13,18 +13,14 @@ void main() {
asm { sei }
while(true) {
while(*RASTER!=$ff) {}
//*D018 = toD018(screen, charset1);
*D018 = (byte)(((word)screen/$40)|((word)charset1/$400));
*D018 = toD018(screen, charset1);
*BGCOL = $6;
while(*RASTER!=$62) {}
//*D018 = toD018(screen, charset1);
*D018 = (byte)(((word)screen/$40)|((word)charset2/$400));
*D018 = toD018(screen, charset2);
*BGCOL = $b;
}
}
/*
inline byte toD018( byte* screen, byte* charset) {
return (byte)(((word)screen/$40)|((word)charset/$400));
}
*/
}

View File

@ -9,12 +9,14 @@
.label charset2 = $1800
jsr main
main: {
.const toD0181_return = screen/$40|charset1/$400
.const toD0182_return = screen/$40|charset2/$400
sei
b4:
lda RASTER
cmp #$ff
bne b4
lda #screen/$40|charset1/$400
lda #toD0181_return
sta D018
lda #6
sta BGCOL
@ -22,7 +24,7 @@ main: {
lda RASTER
cmp #$62
bne b7
lda #screen/$40|charset2/$400
lda #toD0182_return
sta D018
lda #$b
sta BGCOL

View File

@ -1,26 +1,32 @@
@begin: scope:[] from
[0] phi() [ ] ( )
to:@1
@1: scope:[] from @begin
to:@2
@2: scope:[] from @begin
[1] phi() [ ] ( )
[2] call main param-assignment [ ] ( )
to:@end
@end: scope:[] from @1
@end: scope:[] from @2
[3] phi() [ ] ( )
main: scope:[main] from @1
main: scope:[main] from @2
asm { sei }
to:main::@4
main::@4: scope:[main] from main main::@4 main::@9
main::@4: scope:[main] from main main::@20 main::@4
[5] if(*((const byte*) RASTER#0)!=(byte/word/signed word/dword/signed dword) 255) goto main::@4 [ ] ( main:2 [ ] )
to:main::@6
main::@6: scope:[main] from main::@4
[6] *((const byte*) D018#0) ← ((byte))(word)(const byte*) screen#0/(byte/signed byte/word/signed word/dword/signed dword) 64|(word)(const byte*) charset1#0/(word/signed word/dword/signed dword) 1024 [ ] ( main:2 [ ] )
[7] *((const byte*) BGCOL#0) ← (byte/signed byte/word/signed word/dword/signed dword) 6 [ ] ( main:2 [ ] )
to:main::toD0181
main::toD0181: scope:[main] from main::@4
[6] phi() [ ] ( main:2 [ ] )
to:main::@19
main::@19: scope:[main] from main::toD0181
[7] *((const byte*) D018#0) ← (const byte) main::toD0181_return#0 [ ] ( main:2 [ ] )
[8] *((const byte*) BGCOL#0) ← (byte/signed byte/word/signed word/dword/signed dword) 6 [ ] ( main:2 [ ] )
to:main::@7
main::@7: scope:[main] from main::@6 main::@7
[8] if(*((const byte*) RASTER#0)!=(byte/signed byte/word/signed word/dword/signed dword) 98) goto main::@7 [ ] ( main:2 [ ] )
to:main::@9
main::@9: scope:[main] from main::@7
[9] *((const byte*) D018#0) ← ((byte))(word)(const byte*) screen#0/(byte/signed byte/word/signed word/dword/signed dword) 64|(word)(const byte*) charset2#0/(word/signed word/dword/signed dword) 1024 [ ] ( main:2 [ ] )
[10] *((const byte*) BGCOL#0) ← (byte/signed byte/word/signed word/dword/signed dword) 11 [ ] ( main:2 [ ] )
main::@7: scope:[main] from main::@19 main::@7
[9] if(*((const byte*) RASTER#0)!=(byte/signed byte/word/signed word/dword/signed dword) 98) goto main::@7 [ ] ( main:2 [ ] )
to:main::toD0182
main::toD0182: scope:[main] from main::@7
[10] phi() [ ] ( main:2 [ ] )
to:main::@20
main::@20: scope:[main] from main::toD0182
[11] *((const byte*) D018#0) ← (const byte) main::toD0182_return#0 [ ] ( main:2 [ ] )
[12] *((const byte*) BGCOL#0) ← (byte/signed byte/word/signed word/dword/signed dword) 11 [ ] ( main:2 [ ] )
to:main::@4

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
(label) @1
(label) @2
(label) @begin
(label) @end
(byte*) BGCOL
@ -12,10 +12,32 @@
(byte*) charset2
(const byte*) charset2#0 charset2 = ((byte*))(word/signed word/dword/signed dword) 6144
(void()) main()
(label) main::@19
(label) main::@20
(label) main::@4
(label) main::@6
(label) main::@7
(label) main::@9
(label) main::toD0181
(word~) main::toD0181_$0
(word/signed dword/dword~) main::toD0181_$1
(word~) main::toD0181_$2
(word/signed dword/dword~) main::toD0181_$3
(word/dword~) main::toD0181_$4
(byte~) main::toD0181_$5
(byte*) main::toD0181_charset
(byte) main::toD0181_return
(const byte) main::toD0181_return#0 toD0181_return = ((byte))(word)(const byte*) screen#0/(byte/signed byte/word/signed word/dword/signed dword) 64|(word)(const byte*) charset1#0/(word/signed word/dword/signed dword) 1024
(byte*) main::toD0181_screen
(label) main::toD0182
(word~) main::toD0182_$0
(word/signed dword/dword~) main::toD0182_$1
(word~) main::toD0182_$2
(word/signed dword/dword~) main::toD0182_$3
(word/dword~) main::toD0182_$4
(byte~) main::toD0182_$5
(byte*) main::toD0182_charset
(byte) main::toD0182_return
(const byte) main::toD0182_return#0 toD0182_return = ((byte))(word)(const byte*) screen#0/(byte/signed byte/word/signed word/dword/signed dword) 64|(word)(const byte*) charset2#0/(word/signed word/dword/signed dword) 1024
(byte*) main::toD0182_screen
(byte*) screen
(const byte*) screen#0 screen = ((byte*))(word/signed word/dword/signed dword) 1024