diff --git a/src/dk/camelot64/kickc/icl/PassGenerateSingleStaticAssignmentForm.java b/src/dk/camelot64/kickc/icl/PassGenerateSingleStaticAssignmentForm.java index f6e58b996..e745f3d19 100644 --- a/src/dk/camelot64/kickc/icl/PassGenerateSingleStaticAssignmentForm.java +++ b/src/dk/camelot64/kickc/icl/PassGenerateSingleStaticAssignmentForm.java @@ -21,12 +21,16 @@ public class PassGenerateSingleStaticAssignmentForm { public void generate() { versionAllAssignments(); versionAllUses(); + boolean done = false; + do { + System.out.println("Completing Phi functions..."); + done = completePhiFunctions(); + } while (!done); } - /** - * Version all non-versioned non-intermediary being assigned a value. - */ + + /** Version all non-versioned non-intermediary being assigned a value. */ private void versionAllAssignments() { for (ControlFlowBlock block : controlFlowGraph.getAllBlocks()) { for (Statement statement : block.getStatements()) { @@ -43,9 +47,7 @@ public class PassGenerateSingleStaticAssignmentForm { } } - /** - * Version all uses of non-versioned non-intermediary variables - */ + /** Version all uses of non-versioned non-intermediary variables */ private void versionAllUses() { for (ControlFlowBlock block : controlFlowGraph.getAllBlocks()) { // Newest version of variables in the block. @@ -82,6 +84,14 @@ public class PassGenerateSingleStaticAssignmentForm { } + /** + * Find and return the latest version of an rValue (if it is a non-versioned symbol). + * If a version is needed and no version is found a new version is created as a phi-function. + * @param rValue The rValue to examine + * @param blockVersions The current version defined in the block for each symbol. + * @param blockNewPhis New versions to be created as phi-functions. Modified if a new phi-function needs to be created. + * @return Null if the rValue does not need versioning. The versioned symbol to use if it does. + */ private Symbol findOrCreateVersion(RValue rValue, Map blockVersions, Map blockNewPhis) { Symbol version = null; if (rValue instanceof Symbol) { @@ -103,5 +113,94 @@ public class PassGenerateSingleStaticAssignmentForm { return version; } + /** Look through all new phi-functions and fill out their parameters. + * @return true if all phis were completely filled out. + * false if new phis were added , meaning another iteration is needed. + * */ + private boolean completePhiFunctions() { + Map> newPhis = new HashMap<>(); + Map> symbolMap = buildSymbolMap(); + for (ControlFlowBlock block : this.controlFlowGraph.getAllBlocks()) { + for (Statement statement : block.getStatements()) { + if (statement instanceof StatementPhi) { + StatementPhi phi = (StatementPhi) statement; + if (phi.getPreviousVersions().isEmpty()) { + Symbol versioned = phi.getlValue(); + Symbol unversioned = versioned.getVersionOf(); + for (ControlFlowBlock predecessor : block.getPredecessors()) { + Symbol previousSymbol = symbolMap.get(predecessor.getLabel()).get(unversioned); + if (previousSymbol == null) { + // No previous symbol found in predecessor block. Look in new a phi functions. + Map predecessorNewPhis = newPhis.get(predecessor); + if (predecessorNewPhis == null) { + predecessorNewPhis = new HashMap<>(); + newPhis.put(predecessor, predecessorNewPhis); + } + previousSymbol = predecessorNewPhis.get(unversioned); + if (previousSymbol == null) { + // No previous symbol found in predecessor block. Add a new phi function to the predecessor. + previousSymbol = symbols.createVersion(unversioned); + predecessorNewPhis.put(unversioned, previousSymbol); + } + } + phi.addPreviousVersion(predecessor, previousSymbol); + } + } + } + } + } + // Ads new phi functions to blocks + for (ControlFlowBlock block : controlFlowGraph.getAllBlocks()) { + Map blockNewPhis = newPhis.get(block); + if(blockNewPhis!=null) { + for (Symbol symbol : blockNewPhis.keySet()) { + block.addPhiStatement(blockNewPhis.get(symbol)); + } + } + } + return (newPhis.size()==0); + } + + /** + * Builds a map of all which versions each symbol has in each block. + * Maps Control Flow Block Label -> ( Unversioned Symbol -> Versioned Symbol) for all relevant symbols. + */ + private Map> buildSymbolMap() { + Map> symbolMap = new HashMap<>(); + for (ControlFlowBlock block : this.controlFlowGraph.getAllBlocks()) { + for (Statement statement : block.getStatements()) { + if(statement instanceof StatementAssignment) { + StatementAssignment assignment = (StatementAssignment) statement; + LValue lValue = assignment.getlValue(); + if(lValue instanceof Symbol) { + Symbol symbol = (Symbol) lValue; + if(symbol.isVersioned()) { + Symbol label = block.getLabel(); + Symbol unversioned = symbol.getVersionOf(); + Symbol versioned = symbol; + Map blockMap = symbolMap.get(label); + if(blockMap == null) { + blockMap = new HashMap<>(); + symbolMap.put(label, blockMap); + } + blockMap.put(unversioned, versioned); + } + } + } else if(statement instanceof StatementPhi) { + StatementPhi phi = (StatementPhi) statement; + Symbol versioned = phi.getlValue(); + Symbol unversioned = versioned.getVersionOf(); + Symbol label = block.getLabel(); + Map blockMap = symbolMap.get(label); + if(blockMap == null) { + blockMap = new HashMap<>(); + symbolMap.put(label, blockMap); + } + blockMap.put(unversioned, versioned); + } + } + } return symbolMap; + } + } diff --git a/src/dk/camelot64/kickc/icl/StatementPhi.java b/src/dk/camelot64/kickc/icl/StatementPhi.java index 1fe4ab2f8..848d14533 100644 --- a/src/dk/camelot64/kickc/icl/StatementPhi.java +++ b/src/dk/camelot64/kickc/icl/StatementPhi.java @@ -15,23 +15,46 @@ public class StatementPhi implements Statement { private Symbol lValue; /** The previous version of the symbol from predeccesor control blocks. */ - private List previousVersions; - + private List previousVersions; public StatementPhi(Symbol lValue) { this.lValue = lValue; this.previousVersions = new ArrayList<>(); } - public LValue getlValue() { + /** + * A previous version of the symbol that the phi function might take its value from. + * Which value is chosen depends on which block transition was made. + */ + public static class PreviousSymbol { + private ControlFlowBlock block; + private Symbol symbol; + + public PreviousSymbol(ControlFlowBlock block, Symbol symbol) { + this.block = block; + this.symbol = symbol; + } + + public ControlFlowBlock getBlock() { + return block; + } + + public Symbol getSymbol() { + return symbol; + } + + + } + + public Symbol getlValue() { return lValue; } - public void addPreviousVersion(Symbol previousVersion) { - previousVersions.add(previousVersion); + public void addPreviousVersion(ControlFlowBlock block, Symbol symbol) { + previousVersions.add(new PreviousSymbol(block, symbol)); } - public List getPreviousVersions() { + public List getPreviousVersions() { return previousVersions; } @@ -39,8 +62,8 @@ public class StatementPhi implements Statement { public String toString() { StringBuilder out = new StringBuilder(); out.append(lValue + " ← " + "phi("); - for (Symbol previousVersion : previousVersions) { - out.append(" "+previousVersion.toString()); + for (PreviousSymbol previousSymbol : previousVersions) { + out.append(" "+previousSymbol.getBlock().getLabel().getName()+"/"+previousSymbol.getSymbol().getName()); } out.append(" )"); return out.toString(); diff --git a/src/dk/camelot64/kickc/icl/SymbolManager.java b/src/dk/camelot64/kickc/icl/SymbolManager.java index 6c79a90ef..bf5f995c1 100644 --- a/src/dk/camelot64/kickc/icl/SymbolManager.java +++ b/src/dk/camelot64/kickc/icl/SymbolManager.java @@ -1,7 +1,6 @@ package dk.camelot64.kickc.icl; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.*; /** * Manages symbols (variables, labels) @@ -58,7 +57,10 @@ public class SymbolManager { @Override public String toString() { StringBuffer out = new StringBuffer(); - for (String name : symbols.keySet()) { + Set names = symbols.keySet(); + List sortedNames = new ArrayList<>(names); + Collections.sort(sortedNames); + for (String name : sortedNames) { Symbol symbol = symbols.get(name); out.append(symbol.toString() + "\n"); }