mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-06-03 07:29:37 +00:00
330 lines
17 KiB
Java
330 lines
17 KiB
Java
package dk.camelot64.kickc.passes;
|
|
|
|
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 inline any procedures declared as inline */
|
|
public class Pass1ProcedureInline extends Pass1Base {
|
|
|
|
public Pass1ProcedureInline(Program program) {
|
|
super(program);
|
|
}
|
|
|
|
@Override
|
|
public boolean step() {
|
|
for(var procedureCompilation : getProgram().getProcedureCompilations())
|
|
if(inlineProcedure(procedureCompilation))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
private boolean inlineProcedure(ProcedureCompilation procedureCompilation) {
|
|
final ControlFlowGraph procedureGraph = procedureCompilation.getGraph();
|
|
if(procedureGraph!=null) {
|
|
final List<Graph.Block> procedureBlocks = new ArrayList<>(procedureGraph.getAllBlocks());
|
|
ListIterator<Graph.Block> blocksIt = procedureBlocks.listIterator();
|
|
while(blocksIt.hasNext()) {
|
|
Graph.Block block = blocksIt.next();
|
|
List<Statement> blockStatements = block.getStatements();
|
|
ListIterator<Statement> statementsIt = blockStatements.listIterator();
|
|
while(statementsIt.hasNext()) {
|
|
Statement statement = statementsIt.next();
|
|
if(statement instanceof StatementCall call) {
|
|
ProcedureRef procedureRef = call.getProcedure();
|
|
Procedure procedure = getProgramScope().getProcedure(procedureRef);
|
|
if(procedure.isDeclaredInline()) {
|
|
procedure.setCallingConvention(Procedure.CallingConvention.PHI_CALL);
|
|
if(procedure.getInterruptType() != null) {
|
|
throw new CompileError("Error! Interrupts cannot be inlined. " + procedure.getRef().toString());
|
|
}
|
|
inlineProcedureCall(call, procedure, statementsIt, block, blocksIt);
|
|
// Update the procedure graph
|
|
procedureCompilation.setGraph(new ControlFlowGraph(procedureBlocks));
|
|
// Exit and restart
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Inline a specific call to a procedure.
|
|
*
|
|
* @param call The call to inline
|
|
* @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 inlineProcedureCall(StatementCall call, Procedure procedure, ListIterator<Statement> statementsIt, Graph.Block block, ListIterator<Graph.Block> blocksIt) {
|
|
Scope callScope = getProgramScope().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
|
|
inlineParameterAssignments(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<Graph.Block> procedureBlocks = getGraph().getScopeBlocks(procedure.getRef());
|
|
for(var procedureBlock : procedureBlocks) {
|
|
LabelRef procBlockLabelRef = procedureBlock.getLabel();
|
|
Symbol procBlockLabel = getProgramScope().getSymbol(procBlockLabelRef);
|
|
Label inlinedBlockLabel;
|
|
if(procedure.equals(procBlockLabel)) {
|
|
inlinedBlockLabel = callScope.getLocalLabel(procedure.getLocalName() + serial);
|
|
} else {
|
|
String inlinedBlockLabelName = getInlineSymbolName(procedure, procBlockLabel, serial);
|
|
inlinedBlockLabel = callScope.getLocalLabel(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();
|
|
LabelRef inlinedSuccessor = inlineSuccessor(procBlockSuccessorRef, procedure, callScope, serial, restBlockLabel);
|
|
inlineBlock.setDefaultSuccessor(inlinedSuccessor);
|
|
}
|
|
if(procedureBlock.getConditionalSuccessor() != null) {
|
|
LabelRef procBlockSuccessorRef = procedureBlock.getConditionalSuccessor();
|
|
LabelRef inlinedSuccessor = inlineSuccessor(procBlockSuccessorRef, procedure, callScope, serial, restBlockLabel);
|
|
inlineBlock.setConditionalSuccessor(inlinedSuccessor);
|
|
}
|
|
}
|
|
// 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
|
|
if(!procedure.getReturnType().equals(SymbolType.VOID)) {
|
|
Variable procReturnVar = procedure.getLocalVariable("return");
|
|
String inlinedReturnVarName = getInlineSymbolName(procedure, procReturnVar, serial);
|
|
Variable inlinedReturnVar = callScope.getLocalVariable(inlinedReturnVarName);
|
|
restBlock.addStatement(new StatementAssignment(call.getlValue(), inlinedReturnVar.getRef(), false, call.getSource(), Comment.NO_COMMENTS));
|
|
} else {
|
|
// Remove the tmp var receiving the result
|
|
LValue lValue = call.getlValue();
|
|
if(lValue instanceof VariableRef) {
|
|
callScope.remove(getProgramScope().getVariable((VariableRef) lValue));
|
|
call.setlValue(null);
|
|
}
|
|
}
|
|
// 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.getLocalLabel(procedure.getLocalName() + serial);
|
|
block.setDefaultSuccessor(inlinedProcLabel.getRef());
|
|
// Set conditional successor of original block to null (as any condition has been moved to the rest block)
|
|
block.setConditionalSuccessor(null);
|
|
// Log the inlining
|
|
getLog().append("Inlined call " + call.toString(getProgram(), false));
|
|
}
|
|
|
|
private LabelRef inlineSuccessor(LabelRef procBlockSuccessorRef, Procedure procedure, Scope callScope, int serial, Label restBlockLabel) {
|
|
LabelRef inlinedSuccessor;
|
|
if(procBlockSuccessorRef.getFullName().equals(SymbolRef.PROCEXIT_BLOCK_NAME)) {
|
|
// Set successor of the @return block to the rest block
|
|
inlinedSuccessor = restBlockLabel.getRef();
|
|
} else {
|
|
Label procBlockSuccessor = getProgramScope().getLabel(procBlockSuccessorRef);
|
|
String inlinedSuccessorName = getInlineSymbolName(procedure, procBlockSuccessor, serial);
|
|
Label inlinedSuccessorLabel = callScope.getLocalLabel(inlinedSuccessorName);
|
|
inlinedSuccessor = inlinedSuccessorLabel.getRef();
|
|
}
|
|
return inlinedSuccessor;
|
|
}
|
|
|
|
|
|
/**
|
|
* 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.getLocalLabel(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 procAssignment) {
|
|
inlinedStatement = new StatementAssignment(procAssignment.getlValue(), procAssignment.getrValue1(), procAssignment.getOperator(), procAssignment.getrValue2(), procAssignment.isInitialAssignment(), procAssignment.getSource(), Comment.NO_COMMENTS);
|
|
} else if(procStatement instanceof StatementCall procCall) {
|
|
StatementCall inlinedCall = new StatementCall(procCall.getlValue(), procCall.getProcedureName(), new ArrayList<>(procCall.getParameters()), procCall.getSource(), procCall.getComments());
|
|
inlinedCall.setProcedure(procCall.getProcedure());
|
|
inlinedStatement = inlinedCall;
|
|
} else if(procStatement instanceof StatementAsm procAsm) {
|
|
inlinedStatement = new StatementAsm(procAsm.getAsmLines(), new LinkedHashMap<>(procAsm.getReferenced()), procAsm.getDeclaredClobber(), procAsm.getSource(), Comment.NO_COMMENTS);
|
|
} else if(procStatement instanceof StatementKickAsm procKasm) {
|
|
inlinedStatement = new StatementKickAsm(procKasm.getKickAsmCode(), procKasm.getBytes(), procKasm.getCycles(), procKasm.getUses(), procKasm.getDeclaredClobber(), procKasm.getSource(), Comment.NO_COMMENTS);
|
|
} else if(procStatement instanceof StatementConditionalJump procConditional) {
|
|
LabelRef procDestinationRef = procConditional.getDestination();
|
|
Label procDestination = getProgramScope().getLabel(procDestinationRef);
|
|
Label inlinedDest = procDestination;
|
|
if(procDestination.getScope().equals(procedure)) {
|
|
String inlineSymbolName = getInlineSymbolName(procedure, procDestination, serial);
|
|
inlinedDest = callScope.getLocalLabel(inlineSymbolName);
|
|
}
|
|
StatementConditionalJump inlinedConditionalJump = new StatementConditionalJump(procConditional.getrValue1(), procConditional.getOperator(), procConditional.getrValue2(), inlinedDest.getRef(), procConditional.getSource(), Comment.NO_COMMENTS);
|
|
inlinedConditionalJump.setDeclaredUnroll(procConditional.isDeclaredUnroll());
|
|
inlinedStatement = inlinedConditionalJump;
|
|
} else if(procStatement instanceof StatementReturn) {
|
|
// No statement needed
|
|
return null;
|
|
} else {
|
|
throw new CompileError("Statement type of Inline function not handled " + procStatement, procStatement.getSource());
|
|
}
|
|
ProgramValueIterator.execute(inlinedStatement, new RValueInliner(procedure, serial, callScope), null, null);
|
|
return inlinedStatement;
|
|
}
|
|
|
|
/**
|
|
* Ensures that all VariableRefs pointing to variables in the procedure being inlined are converted to refs to the new inlined variables
|
|
* Also copies all intermediate RValue objects to ensure they are not references to objects from the original statements in the procedure being inlined
|
|
*/
|
|
private class RValueInliner implements ProgramValueHandler {
|
|
|
|
/** The scope where the precedure is being inlined into. */
|
|
private final Scope callScope;
|
|
/** The procedure being inlined. */
|
|
private final Procedure procedure;
|
|
/** The serial number (counted up for each inlined call to the same function within the called calling scope) */
|
|
private final int serial;
|
|
|
|
public RValueInliner(Procedure procedure, int serial, Scope callScope) {
|
|
this.procedure = procedure;
|
|
this.serial = serial;
|
|
this.callScope = callScope;
|
|
}
|
|
|
|
@Override
|
|
public void execute(ProgramValue programValue, Statement currentStmt, ListIterator<Statement> stmtIt, Graph.Block currentBlock) {
|
|
Value rValue = programValue.get();
|
|
if(rValue instanceof VariableRef procVarRef) {
|
|
Variable procVar = Pass1ProcedureInline.this.getProgramScope().getVariable(procVarRef);
|
|
if(procVar.getScope().equals(procedure)) {
|
|
String inlineSymbolName = Pass1ProcedureInline.this.getInlineSymbolName(procedure, procVar, serial);
|
|
Variable inlineVar = callScope.getLocalVariable(inlineSymbolName);
|
|
programValue.set(inlineVar.getRef());
|
|
}
|
|
} else if(rValue instanceof PointerDereferenceSimple) {
|
|
programValue.set(new PointerDereferenceSimple(((PointerDereferenceSimple) rValue).getPointer()));
|
|
} else if(rValue instanceof PointerDereferenceIndexed) {
|
|
programValue.set(new PointerDereferenceIndexed(((PointerDereferenceIndexed) rValue).getPointer(), ((PointerDereferenceIndexed) rValue).getIndex()));
|
|
} else if(rValue instanceof CastValue) {
|
|
programValue.set(new CastValue(((CastValue) rValue).getToType(), ((CastValue) rValue).getValue()));
|
|
} else if(rValue instanceof ValueList) {
|
|
programValue.set(new ValueList(new ArrayList<>(((ValueList) rValue).getList())));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 inlineParameterAssignments(ListIterator<Statement> statementsIt, StatementCall call, Procedure procedure, Scope callScope, int serial) {
|
|
List<Variable> parameterDecls = procedure.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.getLocalVariable(inlineParameterVarName);
|
|
RValue parameterValue = parameterValues.get(i);
|
|
statementsIt.add(new StatementAssignment((VariableRef)inlineParameterVar.getRef(), parameterValue, true, call.getSource(), Comment.NO_COMMENTS));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 procVar) {
|
|
String inlineVarName = getInlineSymbolName(procedure, procSymbol, serial);
|
|
Variable inlineVar = Variable.createCopy(inlineVarName, callScope, procVar);
|
|
callScope.add(inlineVar);
|
|
} 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the new name of a symbol from the procedure being inlined .
|
|
* The name is <proc>_<symb>, where <proc> is the name of the procedure and <symb> 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();
|
|
}
|
|
|
|
}
|