diff --git a/src/main/java/dk/camelot64/kickc/model/VariableReferenceInfos.java b/src/main/java/dk/camelot64/kickc/model/VariableReferenceInfos.java index 94e95a65f..fe05700f2 100644 --- a/src/main/java/dk/camelot64/kickc/model/VariableReferenceInfos.java +++ b/src/main/java/dk/camelot64/kickc/model/VariableReferenceInfos.java @@ -69,7 +69,7 @@ public class VariableReferenceInfos { this.referencedSymbol = referencedSymbol; } - Integer getStatementIdx() { + public Integer getStatementIdx() { return statementIdx; } @@ -102,7 +102,7 @@ public class VariableReferenceInfos { this.referencedSymbol = referencedSymbol; } - SymbolVariableRef getReferencingSymbol() { + public SymbolVariableRef getReferencingSymbol() { return referencingSymbol; } @@ -296,6 +296,41 @@ public class VariableReferenceInfos { return stmts; } + + /** + * Get all constants (or symbol definitions) referencing another constant + * + * @param constRef The constant to look for + * @return All constants (or symbol definitions) that reference the constant in their value + */ + public Collection getConstRefSymbols(ConstantRef constRef) { + Collection refs = symbolVarReferences.get(constRef); + LinkedHashSet constRefs = new LinkedHashSet<>(); + if(refs != null) { + refs.stream() + .filter(referenceToSymbolVar -> ReferenceToSymbolVar.ReferenceType.USE.equals(referenceToSymbolVar.getReferenceType())) + .filter(referenceToSymbolVar -> referenceToSymbolVar instanceof ReferenceInSymbol) + .forEach(referenceToSymbolVar -> constRefs.add(((ReferenceInSymbol) referenceToSymbolVar).getReferencingSymbol())); + } + return constRefs; + } + + /** + * Get all usages of a constant. (only returns places where the constant is used, not where it is defined) + * @param constRef The constant to look for + * @return All statements or other constants that use the constant + */ + public Collection getConstRefAllUses(ConstantRef constRef) { + Collection refs = symbolVarReferences.get(constRef); + if(refs != null) { + final List allUses = refs.stream() + .filter(referenceToSymbolVar -> ReferenceToSymbolVar.ReferenceType.USE.equals(referenceToSymbolVar.getReferenceType())) + .collect(Collectors.toList()); + return allUses; + } else + return new ArrayList<>(); + } + /** * Get the index of the statement defining a variable. Only returns if there is exactly one defining statement. * @@ -366,22 +401,5 @@ public class VariableReferenceInfos { } - /** - * Get all constants (or symbol definitions) referencing another constant - * - * @param constRef The constant to look for - * @return All constants (or symbol definitions) that reference the constant in their value - */ - public Collection getSymbolRefConsts(ConstantRef constRef) { - Collection refs = symbolVarReferences.get(constRef); - LinkedHashSet constRefs = new LinkedHashSet<>(); - if(refs != null) { - refs.stream() - .filter(referenceToSymbolVar -> ReferenceToSymbolVar.ReferenceType.USE.equals(referenceToSymbolVar.getReferenceType())) - .filter(referenceToSymbolVar -> referenceToSymbolVar instanceof ReferenceInSymbol) - .forEach(referenceToSymbolVar -> constRefs.add(((ReferenceInSymbol) referenceToSymbolVar).getReferencingSymbol())); - } - return constRefs; - } } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java index a6c8024b1..3e72fab50 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java @@ -80,6 +80,22 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor comments = ensureUnusedComments(getCommentsSymbol(ctx)); RValue rVal = exprVal; if(exprVal instanceof LValue) { @@ -1270,7 +1286,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor comments = ensureUnusedComments(getCommentsSymbol(ctx)); if(elseStmt == null) { // If without else - skip the entire section if condition not met - SymbolVariableRef notExprVar = getCurrentScope().addVariableIntermediate().getRef(); + SymbolVariableRef notExprVar = addIntermediateVar().getRef(); addStatement(new StatementAssignment((LValue) notExprVar, null, Operators.LOGIC_NOT, rValue, true, StatementSource.ifThen(ctx), comments)); Label endJumpLabel = getCurrentScope().addLabelIntermediate(); addStatement(new StatementConditionalJump(notExprVar, endJumpLabel.getRef(), StatementSource.ifThen(ctx), Comment.NO_COMMENTS)); @@ -1394,7 +1410,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor(); } - Variable tmpVar = getCurrentScope().addVariableIntermediate(); + Variable tmpVar = addIntermediateVar(); SymbolVariableRef tmpVarRef = tmpVar.getRef(); String procedureName; @@ -2362,7 +2378,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor(procAsm.getReferenced()), procAsm.getDeclaredClobber(), procAsm.getSource(), Comment.NO_COMMENTS); inlinedStatement = inlinedAsm; + } else if(procStatement instanceof StatementKickAsm) { + StatementKickAsm procKasm = (StatementKickAsm) procStatement; + StatementKickAsm inlinedAsm = new StatementKickAsm(procKasm.getKickAsmCode(), procKasm.getLocation(), procKasm.getBytes(), procKasm.getCycles(), procKasm.getUses(), procKasm.getDeclaredClobber(), procKasm.getSource(), Comment.NO_COMMENTS); + inlinedStatement = inlinedAsm; } else if(procStatement instanceof StatementConditionalJump) { StatementConditionalJump procConditional = (StatementConditionalJump) procStatement; LabelRef procDestinationRef = procConditional.getDestination(); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index 011b7c194..861ba204b 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -370,7 +370,7 @@ public class Pass4CodeGeneration { } } } - Collection symbolRefConsts = program.getVariableReferenceInfos().getSymbolRefConsts(constantVar.getConstantRef()); + Collection symbolRefConsts = program.getVariableReferenceInfos().getConstRefSymbols(constantVar.getConstantRef()); if(symbolRefConsts != null) { for(SymbolVariableRef symbolRefConst : symbolRefConsts) { Variable symbolRefVar = (Variable) program.getScope().getSymbol(symbolRefConst); diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNEliminateEmptyProcedure.java b/src/main/java/dk/camelot64/kickc/passes/PassNEliminateEmptyProcedure.java index 347a5f05c..b5e078023 100644 --- a/src/main/java/dk/camelot64/kickc/passes/PassNEliminateEmptyProcedure.java +++ b/src/main/java/dk/camelot64/kickc/passes/PassNEliminateEmptyProcedure.java @@ -2,9 +2,15 @@ package dk.camelot64.kickc.passes; import dk.camelot64.kickc.model.ControlFlowBlock; import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.StatementInfos; +import dk.camelot64.kickc.model.VariableReferenceInfos; 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.Symbol; +import dk.camelot64.kickc.model.symbols.Variable; import dk.camelot64.kickc.model.values.ProcedureRef; +import dk.camelot64.kickc.model.values.SymbolVariableRef; import java.util.Collection; import java.util.HashSet; @@ -24,16 +30,58 @@ public class PassNEliminateEmptyProcedure extends Pass2SsaOptimization { boolean optimized = false; for(Procedure procedure : allProcedures) { if(hasEmptyBody(procedure.getRef())) { - // Remove all calls - removeAllCalls(procedure.getRef()); - // Remove the procedure - Pass2EliminateUnusedBlocks.removeProcedure(procedure.getRef(), new HashSet<>(), getProgram()); - optimized = true; + if(!hasExternalUsages(procedure.getRef(), getProgram())) { + // Remove all calls + removeAllCalls(procedure.getRef()); + // Remove the procedure + Pass2EliminateUnusedBlocks.removeProcedure(procedure.getRef(), new HashSet<>(), getProgram()); + optimized = true; + } } } return optimized; } + /** + * Examines whether there are any constants inside a procedure with external usages + * + * @param procedureRef + * @return + */ + protected static boolean hasExternalUsages(ProcedureRef procedureRef, Program program) { + program.clearVariableReferenceInfos(); + program.clearStatementInfos(); + new PassNStatementIndices(program).execute(); + final VariableReferenceInfos variableReferenceInfos = program.getVariableReferenceInfos(); + final StatementInfos statementInfos = program.getStatementInfos(); + + final Procedure startProc = program.getScope().getProcedure(procedureRef); + final Collection startConsts = startProc.getAllConstants(true); + for(Variable startConst : startConsts) { + final Collection uses = variableReferenceInfos.getConstRefAllUses(startConst.getConstantRef()); + for(VariableReferenceInfos.ReferenceToSymbolVar use : uses) { + if(use instanceof VariableReferenceInfos.ReferenceInStatement) { + final Integer statementIdx = ((VariableReferenceInfos.ReferenceInStatement) use).getStatementIdx(); + final ControlFlowBlock block = statementInfos.getBlock(statementIdx); + final Procedure useProcedure = block.getProcedure(program); + if(!procedureRef.equals(useProcedure.getRef())) { + // Usage in a another procedure + return true; + } + } else if(use instanceof VariableReferenceInfos.ReferenceInSymbol) { + final SymbolVariableRef referencingSymbolRef = ((VariableReferenceInfos.ReferenceInSymbol) use).getReferencingSymbol(); + final Symbol referencingSymbol = program.getScope().getSymbol(referencingSymbolRef); + final Scope referencingScope = referencingSymbol.getScope(); + if(!procedureRef.equals(referencingScope.getRef())) { + // Usage in a another constant + return true; + } + } + } + } + return false; + } + private void removeAllCalls(ProcedureRef ref) { for(ControlFlowBlock block : getGraph().getAllBlocks()) { final ListIterator stmtIt = block.getStatements().listIterator(); diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNEliminateEmptyStart.java b/src/main/java/dk/camelot64/kickc/passes/PassNEliminateEmptyStart.java index f67530b04..45fd1ce15 100644 --- a/src/main/java/dk/camelot64/kickc/passes/PassNEliminateEmptyStart.java +++ b/src/main/java/dk/camelot64/kickc/passes/PassNEliminateEmptyStart.java @@ -23,10 +23,13 @@ public class PassNEliminateEmptyStart extends Pass2SsaOptimization { final ProcedureRef startProcRef = new ProcedureRef(SymbolRef.START_PROC_NAME); StatementCall singleCall = getSingleCall(startProcRef); if(singleCall != null) { - // Start only has a single call - getProgram().setStartProcedure(singleCall.getProcedure()); - Pass2EliminateUnusedBlocks.removeProcedure(startProcRef, new HashSet<>(), getProgram()); - return true; + // Are there any constants in the scope that are used elsewhere? + if(!PassNEliminateEmptyProcedure.hasExternalUsages(startProcRef, getProgram())) { + // Start only has a single call - and no external usages + getProgram().setStartProcedure(singleCall.getProcedure()); + Pass2EliminateUnusedBlocks.removeProcedure(startProcRef, new HashSet<>(), getProgram()); + return true; + } } return false; } @@ -49,7 +52,7 @@ public class PassNEliminateEmptyStart extends Pass2SsaOptimization { // Another call already encountered return null; final StatementCall call = (StatementCall) statement; - if(call.getParameters()==null && call.getlValue() == null) + if(call.getParameters() == null && call.getlValue() == null) // Call is no-args no-return singleCall = call; else