package de.fuberlin.optimierung; import java.util.*; import de.fuberlin.optimierung.commands.*; public class LLVM_Block{ // Welche Befehle per RemoveCommonExpressions bearbeitet werden List<String> blacklist = new ArrayList<String>(); // Funktion, zu der der Block gehoert private LLVM_Function function = null; // Erster und letzter Befehl des Blockes private LLVM_GenericCommand firstCommand = null; private LLVM_GenericCommand lastCommand = null; // Ursprüngliches Label des Blockes private String label = ""; // Vorgaenger- und Nachfolgerbloecke // Hieraus entsteht der Flussgraph zwischen den Bloecken private LinkedList<LLVM_Block> nextBlocks = new LinkedList<LLVM_Block>(); private LinkedList<LLVM_Block> previousBlocks = new LinkedList<LLVM_Block>(); // def- und usemenge an Speicheradressen fuer globale Lebendigkeitsanalyse private LinkedList<String> def = new LinkedList<String>(); private LinkedList<String> use = new LinkedList<String>(); // IN und OUT Mengen fuer globale Lebendigkeitsanalyse auf Speicherzellen private LinkedList<String> inLive = new LinkedList<String>(); private LinkedList<String> outLive = new LinkedList<String>(); // gen- und killmengen fuer globale Reaching Analyse private LinkedList<LLVM_GenericCommand> gen = new LinkedList<LLVM_GenericCommand>(); private LinkedList<LLVM_GenericCommand> kill = new LinkedList<LLVM_GenericCommand>(); // IN und OUT Mengen fuer globale Reachinganalyse private LinkedList<LLVM_GenericCommand> inReaching = new LinkedList<LLVM_GenericCommand>(); private LinkedList<LLVM_GenericCommand> outReaching = new LinkedList<LLVM_GenericCommand>(); // Kompletter Code des Blocks als String private String blockCode; public LLVM_Block(String blockCode, LLVM_Function function) throws LLVM_OptimizationException{ this.function = function; this.blockCode = blockCode; // blacklist für removeCommonExpressions blacklist.add(LLVM_Operation.ALLOCA.toString()); blacklist.add(LLVM_Operation.CALL.toString()); blacklist.add(LLVM_Operation.BR.toString()); blacklist.add(LLVM_Operation.BR_CON.toString()); //blacklist.add(LLVM_Operation.LOAD.toString()); blacklist.add(LLVM_Operation.STORE.toString()); blacklist.add(LLVM_Operation.RET.toString()); blacklist.add(LLVM_Operation.RET_CODE.toString()); this.createCommands(); } /** * Löscht alle doppelten Befehle in einem Block * Bei Änderungen wird ConstantPropagation aufgerufen * Doppelte Befehle werden nur überprüft, falls nicht in Blacklist */ public void removeCommonExpressions() throws LLVM_OptimizationException{ LinkedList<LLVM_GenericCommand> changed = new LinkedList<LLVM_GenericCommand>(); HashMap<String, LinkedList<LLVM_GenericCommand>> commonex = new HashMap<String, LinkedList<LLVM_GenericCommand>>(); for (LLVM_GenericCommand i = this.firstCommand; i != null; i=i.getSuccessor()){ // Nur Kommandos aus der Whitelist optimieren if (blacklist.contains(i.getOperation().name())) continue; // Spezialbehandlung für Load if(i.getOperation() == LLVM_Operation.LOAD) { String registerName = i.getOperands().getFirst().getName(); LLVM_GenericCommand def = this.function.getRegisterMap().getDefinition(registerName); if(!(def!=null && def.getOperation()!=LLVM_Operation.GETELEMENTPTR && !(def.getOperation()==LLVM_Operation.ALLOCA && def.getTarget().getTypeString().startsWith("[")) && !(def.getOperation()==LLVM_Operation.ALLOCA && def.getTarget().getTypeString().startsWith("%")))) { // Ignoriere Load, falls komplexe Strukturen benutzt werden continue; } } if (commonex.containsKey(i.getOperation().name())){ // Kommando-Hash existiert boolean matched = false; LinkedList<LLVM_GenericCommand> commands = commonex.get(i.getOperation().name()); for (LLVM_GenericCommand command : commands){ if (matchCommands(i, command)){ // gleiches Kommando gefunden // ersetze aktuelles Kommando mit Bestehendem matched = true; if (LLVM_Optimization.DEBUG) System.out.println("same command at " + command.getTarget().getName() + ", command replaced : " + i.toString()); this.function.getRegisterMap().deleteCommand(i); // neues Kommando generieren mit Parametern LLVM_GenericCommand replacement = new LLVM_BinaryCommand(); replacement.setOperation(LLVM_Operation.ADD); replacement.setTarget(new LLVM_Parameter(i.getTarget().getName(), i.getTarget().getTypeString())); LinkedList<LLVM_Parameter> params = new LinkedList<LLVM_Parameter>(); params.add(new LLVM_Parameter(command.getTarget().getName(), command.getTarget().getTypeString())); params.add(new LLVM_Parameter("0", command.getTarget().getTypeString())); replacement.setOperands(params); // Kommando ersetzen i.replaceCommand(replacement); this.function.getRegisterMap().addCommand(replacement); changed.add(replacement); //changed.add(i); } } if (!matched){ // Kein übereinstimmendes Kommando gefunden // füge aktuelles Kommando zum Kommando-Hash hinzu commands.add(i); commonex.put(i.getOperation().name(), commands); } } else{ // Kommando-Hash existiert nicht LinkedList<LLVM_GenericCommand> tmp = new LinkedList<LLVM_GenericCommand>(); tmp.add(i); commonex.put(i.getOperation().name(), tmp); } } if (changed.size() > 0){ this.function.constantPropagation(changed); removeCommonExpressions(); } } private boolean matchCommands(LLVM_GenericCommand com1, LLVM_GenericCommand com2){ int i = 0; // Gleichviele Parameter? if (com1.getOperands().size() != com2.getOperands().size()) return false; // Gleiche Operation? if (com1.getOperation() != com2.getOperation()) return false; // Gleiche Parameter? for (LLVM_Parameter para1 : com1.getOperands()){ LLVM_Parameter para2 = com2.getOperands().get(i); if (para1.getType() == para2.getType() && para1.getName().equals(para2.getName())){ // Nothing }else{ // Parameter matchen nicht return false; } i++; } return true; } /* * ********************************************************* * *********** Live Variable Analysis ********************** * ********************************************************* */ /** * Entferne ueberfluessige Stores * Vorraussetzung: IN und OUT mengen der globalen lebendigkeitsanalyse sind gesetzt */ public void deleteDeadStores() { LinkedList<String> active = (LinkedList<String>) this.outLive.clone(); LinkedList<LLVM_GenericCommand> deletedCommands = new LinkedList<LLVM_GenericCommand>(); // Gehe Befehle von hinten durch LLVM_GenericCommand c = this.lastCommand; for(;c!=null; c = c.getPredecessor()) { if(c.getOperation()==LLVM_Operation.STORE) { String registerName = c.getOperands().get(1).getName(); if(!active.contains(registerName)) { LLVM_GenericCommand def = this.function.getRegisterMap(). getDefinition(registerName); // auf Arrays/Structs ist Aktion nicht einfach moeglich, // wird daher ausgeschlossen // bis auf null und getelementptr-abfrage kann der rest wahrscheinlich weg if(def!=null && def.getOperation()!=LLVM_Operation.GETELEMENTPTR && !(def.getOperation()==LLVM_Operation.ALLOCA && def.getTarget().getTypeString().startsWith("[")) && !(def.getOperation()==LLVM_Operation.ALLOCA && def.getTarget().getTypeString().startsWith("%"))) { // c kann geloescht werden this.function.getRegisterMap().deleteCommand(c); c.deleteCommand("deleteDeadStores"); deletedCommands.add(c); } } else { // jetzt ist es nicht mehr aktiv active.remove(registerName); } } if(c.getOperation()==LLVM_Operation.LOAD) { active.add(c.getOperands().getFirst().getName()); } } // Teste, ob geloeschter Befehl Operanden hatte, der nun keine Verwendung mehr hat // Dann kann die Definition entfernt werden while(!deletedCommands.isEmpty()) { deletedCommands = this.function.eliminateDeadRegistersFromList(deletedCommands); } } /** * Erstelle def und use Mengen dieses Blockes fuer globale Lebendigkeitsanalyse * def : store i32 1, i32* %a -> %a wird hinzugefuegt, falls es keine vorherige * Verwendung von a in diesem Block gibt * use : %5 = load i32* %a -> %a wird hinzugefuegt, falls es keine vorherige * Definition von a in diesem Block gibt */ public void createDefUseSets() { if(!this.isEmpty()) { LLVM_GenericCommand c = this.firstCommand; while(c!=null) { if(LLVM_Operation.STORE==c.getOperation()) { // Register mit Speicheradresse steht in zweitem Operanden LLVM_Parameter p = c.getOperands().get(1); String registerName = p.getName(); // registerName muss in this.def, falls es keine vorherige Verwendung // gab, also falls registerName nicht in this.use enthalten ist if(!this.use.contains(registerName)) { this.def.add(registerName); } } else if(LLVM_Operation.LOAD==c.getOperation()) { // Register mit Speicheradresse steht in erstem Operanden LLVM_Parameter p = c.getOperands().getFirst(); String registerName = p.getName(); // registerName muss in this.use, falls es keine vorherige Definition // gab, also falls registerName nicht in this.def enthalten ist if(!this.def.contains(registerName)) { this.use.add(registerName); } } c = c.getSuccessor(); } } } /** * Aktualisiere IN und OUT Mengen fuer globale Lebendigkeitsanalyse * Voraussetzung: def und use sind gesetzt * @return true, falls IN veraendert wurde */ public boolean updateInOutLiveVariables() { // this.out = in-Mengen aller Nachfolger zusammenfuegen this.outLive.clear(); for(LLVM_Block b : this.nextBlocks) { LinkedList<String> inNextBlock = b.getInLive(); for(String s : inNextBlock) { if(!this.outLive.contains(s)) { this.outLive.add(s); } } } // this.in = this.use + (this.out - this.def) //this.inLive.clear(); LinkedList<String> inLiveOld = this.inLive; this.inLive = (LinkedList<String>) this.outLive.clone(); // gibt doch neues obj zurueck? for(String s : this.def) { this.inLive.remove(s); } for(String s : this.use) { if(!this.inLive.contains(s)) { this.inLive.add(s); } } return !(this.compareLists(inLiveOld, this.inLive)); } /* * ********************************************************* * *********** Reaching Analysis *************************** * ********************************************************* */ public void clearReaching() { this.inReaching.clear(); this.outReaching.clear(); this.gen.clear(); this.kill.clear(); } /** * Load-Befehle, die nur von einem Store erreicht werden koennen, * werden zu Registerzuweisung. * Diese wird hier weiterpropagiert. * Koennen tote Stores entstehen. */ public void foldStoreLoad() { HashMap<String,LinkedList<LLVM_GenericCommand>> reaching = new HashMap<String,LinkedList<LLVM_GenericCommand>>(); //LinkedList<LLVM_GenericCommand> reaching = (LinkedList<LLVM_GenericCommand>) this.inReaching.clone(); LinkedList<LLVM_GenericCommand> changed = new LinkedList<LLVM_GenericCommand>(); for(LLVM_GenericCommand c : this.inReaching) { String registerName = c.getOperands().get(1).getName(); LinkedList<LLVM_GenericCommand> stores = reaching.get(registerName); if(stores==null) { stores = new LinkedList<LLVM_GenericCommand>(); } // Fuege ein, falls der Befehl noch nicht enthalten ist if(!stores.contains(c)) { stores.add(c); } reaching.put(registerName, stores); } // Gehe Befehle von vorne durch LLVM_GenericCommand c = this.firstCommand; for(;c!=null; c = c.getSuccessor()) { // falls store, fuege zu liste hinzu if(c.getOperation()==LLVM_Operation.STORE) { String registerName = c.getOperands().get(1).getName(); LinkedList<LLVM_GenericCommand> stores = reaching.get(registerName); if(stores==null) { stores = new LinkedList<LLVM_GenericCommand>(); } else { stores.clear(); } // Fuege ein, falls der Befehl noch nicht enthalten ist //if(!stores.contains(c)) { stores.add(c); //} reaching.put(registerName, stores); } // falls load, teste ob es nur von einer definition erreicht werden kann // dann ersetze load befehl // store koennte danach tot sein if(c.getOperation()==LLVM_Operation.LOAD) { String registerName = c.getOperands().getFirst().getName(); LinkedList<LLVM_GenericCommand> stores = reaching.get(registerName); if(stores!=null) { if(stores.size()==1) { // Gibt es nur ein erreichendes Store? LLVM_GenericCommand def = this.function.getRegisterMap(). getDefinition(registerName); // auf Arrays/Structs ist Aktion nicht einfach moeglich, // wird daher ausgeschlossen // bis auf null und getelementptr-abfrage kann der rest wahrscheinlich weg if(def!=null && def.getOperation()!=LLVM_Operation.GETELEMENTPTR && !(def.getOperation()==LLVM_Operation.ALLOCA && def.getTarget().getTypeString().startsWith("[")) && !(def.getOperation()==LLVM_Operation.ALLOCA && def.getTarget().getTypeString().startsWith("%"))) { LLVM_GenericCommand store = stores.getFirst(); // Veraendere Load Befehl, store ist einzige Definition, die Load erreicht this.function.getRegisterMap().deleteCommand(c); // Erstelle neuen Befehl LLVM_GenericCommand newCommand = new LLVM_BinaryCommand(); newCommand.setOperation(LLVM_Operation.ADD); LinkedList<LLVM_Parameter> parameterList = new LinkedList<LLVM_Parameter>(); LLVM_Parameter newParameter = store.getOperands().getFirst(); parameterList.add(new LLVM_Parameter(newParameter.getName(), newParameter.getTypeString())); parameterList.add(new LLVM_Parameter("0",newParameter.getTypeString())); newCommand.setOperands(parameterList); newCommand.setTarget(c.getTarget()); newCommand.setBlock(c.getBlock()); newCommand.setPredecessor(c.getPredecessor()); newCommand.setSuccessor(c.getSuccessor()); c.replaceCommand(newCommand); this.function.getRegisterMap().addCommand(newCommand); changed.add(newCommand); } } } } } this.function.constantPropagation(changed); } /** * Erstelle gen und kill Mengen dieses Blockes fuer globale Lebendigkeitsanalyse * gen : store i32 1, i32* %a -> Befehl wird hinzugefuegt, falls es kein spaeteres * store auf a gibt (in diesem Block) * kill : store i32 1, i32* %a -> alle anderen stores auf a werden hinzugefuegt * (aus allen Bloecken) */ public void createGenKillSets() { if(!this.isEmpty()) { LLVM_GenericCommand c = this.lastCommand; while(c!=null) { if(LLVM_Operation.STORE==c.getOperation()) { // Register mit Speicheradresse steht in zweitem Operanden LLVM_Parameter p = c.getOperands().get(1); String registerName = p.getName(); // Falls es vorheriges Store auf diesem Register gab, so ist der // aktuelle Befehl in der kill-Menge diese Blockes enthalten if(!this.kill.contains(c)) { this.gen.add(c); } // Suche alle anderen Stores auf diesem Register und fuege diese // Befehle der kill-Menge hinzu LinkedList<LLVM_GenericCommand> uses = this.function.getRegisterMap(). getUses(registerName); if(uses != null){ for(LLVM_GenericCommand u : uses) { if(LLVM_Operation.STORE==u.getOperation() && u!=c) { this.kill.add(u); } } } } c = c.getPredecessor(); } } } /** * Aktualisiere IN und OUT Mengen fuer Reachinganalyse * Voraussetzung: gen und kill sind gesetzt * @return true, falls OUT veraendert wurde */ public boolean updateInOutReaching() { // this.in = out-Mengen aller Vorgaenger zusammenfuegen this.inReaching.clear(); for(LLVM_Block b : this.previousBlocks) { LinkedList<LLVM_GenericCommand> outPreviousBlock = b.getOutReaching(); for(LLVM_GenericCommand c : outPreviousBlock) { if(!this.inReaching.contains(c)) { this.inReaching.add(c); } } } // this.out = this.gen + (this.in - this.kill) LinkedList<LLVM_GenericCommand> outReachingOld = this.outReaching; this.outReaching = (LinkedList<LLVM_GenericCommand>) this.inReaching.clone(); // gibt doch neues obj zurueck? for(LLVM_GenericCommand c : this.kill) { this.outReaching.remove(c); } for(LLVM_GenericCommand c : this.gen) { if(!this.outReaching.contains(c)) { this.outReaching.add(c); } } return !(this.compareLists(outReachingOld, this.outReaching)); } /* * ********************************************************* * *********** Umgang mit Befehlen ************************* * ********************************************************* */ private boolean labelCheck(String label) { String _label = label.trim(); if(_label.charAt(0) == ';') { return false; }else{ if(_label.contains(":")){ if(_label.indexOf(';') > 0){ if(_label.indexOf(';') > _label.indexOf(':')){ String[] splitedLabel = label.split(":"); if(splitedLabel[0].matches("[a-zA-Z0-9]*")){ this.label = "%"+splitedLabel[0]; return true; } } }else{ String[] splitedLabel = label.split(":"); if(splitedLabel[0].matches("[a-zA-Z0-9]*")){ this.label = "%"+splitedLabel[0]; return true; } } } } return false; } private void createCommands() throws LLVM_OptimizationException{ String commandsArray[] = this.blockCode.split("\n"); LLVM_GenericCommand predecessor = null; for(int i=0; i<commandsArray.length; i++) { // Leerzeilen ignorieren if(commandsArray[i].length() == 0){ continue; } // Checking for label if(labelCheck(commandsArray[i])){ continue; } // Kommentare ignoriern if (commandsArray[i].trim().startsWith(";")){ continue; } LLVM_GenericCommand c = mapCommands(commandsArray[i].trim(), predecessor); if(firstCommand == null){ firstCommand = c; predecessor = c; }else{ predecessor = c; } } this.lastCommand = predecessor; } // Ermittelt Operation und erzeugt Command mit passender Klasse private LLVM_GenericCommand mapCommands(String cmdLine, LLVM_GenericCommand predecessor) throws LLVM_OptimizationException{ // command handling if(cmdLine.startsWith("store ")){ return new LLVM_StoreCommand(cmdLine, predecessor, this); }else if(cmdLine.startsWith("ret ")){ return new LLVM_ReturnCommand(cmdLine, predecessor, this); }else if(cmdLine.startsWith("br ")){ return new LLVM_BranchCommand(cmdLine, predecessor, this); }else if(cmdLine.contains(" = insertvalue ") || cmdLine.contains(" = extractvalue ")){ return new LLVM_InsertExtractValueCommand(cmdLine, predecessor, this); }else if(cmdLine.contains(" = alloca ")){ return new LLVM_AllocaCommand(cmdLine, predecessor, this); }else if(cmdLine.contains(" = and ") || cmdLine.contains(" = or ") || cmdLine.contains(" = xor ") || cmdLine.contains(" = shl ") || cmdLine.contains(" = lshr ") || cmdLine.contains(" = ashr ") || cmdLine.contains(" = add ") || cmdLine.contains(" = fadd ") || cmdLine.contains(" = sub ") || cmdLine.contains(" = fsub ") || cmdLine.contains(" = mul ") || cmdLine.contains(" = fmul ") || cmdLine.contains(" = udiv ") || cmdLine.contains(" = sdiv ") || cmdLine.contains(" = fdiv ") || cmdLine.contains(" = urem ") || cmdLine.contains(" = srem ") || cmdLine.contains(" = frem ")){ return new LLVM_BinaryCommand(cmdLine, predecessor, this); }else if(cmdLine.contains(" = load ")){ return new LLVM_LoadCommand(cmdLine, predecessor, this); }else if(cmdLine.contains(" = getelementptr ")){ return new LLVM_GetElementPtrCommand(cmdLine, predecessor, this); }else if(cmdLine.contains(" = call ") || cmdLine.contains(" = tail call ") || cmdLine.startsWith("call ")){ return new LLVM_CallCommand(cmdLine, predecessor, this); }else if(cmdLine.contains(" = icmp ") || cmdLine.contains(" = fcmp ")){ return new LLVM_XcmpCommand(cmdLine, predecessor, this); }else{ throw new LLVM_OptimizationException("Nicht implementiertes LLVM_Kommando: " + cmdLine); } } /* * ********************************************************* * *********** Hilfsfunktionen ***************************** * ********************************************************* */ /** * Hilfsfunktion, um zwei String-Listen zu vergleichen * Gibt true zurueck, wenn sie die gleichen Strings enthalten (Reihenfolge egal), * sonst false * @param <T> * @param l1 Liste 1 * @param l2 Liste 2 * @return */ /*private boolean compareLists(LinkedList<String> l1, LinkedList<String> l2) { if(l1.size()!=l2.size()) { return false; } for(String s : l1) { if(!l2.contains(s)) { return false; } } return true; }*/ private <T> boolean compareLists(LinkedList<T> l1, LinkedList<T> l2) { if(l1.size()!=l2.size()) { return false; } for(T s : l1) { if(!l2.contains(s)) { return false; } } return true; } public void deleteBlock() { for(LLVM_Block nextBlock : this.nextBlocks) { nextBlock.removeFromPreviousBlocks(this); } } public boolean isEmpty() { return (this.firstCommand==null); } public boolean hasPreviousBlocks() { return !(this.previousBlocks.isEmpty()); } public int countCommands() { int count = 0; LLVM_GenericCommand tmp = getFirstCommand(); while(tmp != null){ count++; tmp = tmp.getSuccessor(); } return count; } /* * ********************************************************* * *********** Setter / Getter / toString ****************** * ********************************************************* */ public void setFirstCommand(LLVM_GenericCommand first) { this.firstCommand = first; } public void setLastCommand(LLVM_GenericCommand last) { this.lastCommand = last; } public LLVM_GenericCommand getFirstCommand() { return firstCommand; } public LLVM_GenericCommand getLastCommand() { return lastCommand; } public LinkedList<LLVM_Block> getNextBlocks() { return nextBlocks; } public void appendToNextBlocks(LLVM_Block block) { this.nextBlocks.add(block); } public void removeFromNextBlocks(LLVM_Block block) { this.nextBlocks.remove(block); } public LinkedList<LLVM_Block> getPreviousBlocks() { return previousBlocks; } public void appendToPreviousBlocks(LLVM_Block block) { this.previousBlocks.add(block); } public void removeFromPreviousBlocks(LLVM_Block block) { this.previousBlocks.remove(block); } public String getLabel() { return label; } public void setLabel(String label) { this.label = label; } public LinkedList<String> getInLive() { return inLive; } public LinkedList<LLVM_GenericCommand> getOutReaching() { return outReaching; } public String toString() { String code = ""; if(!this.label.matches("%[1-9][0-9]*") && !this.label.equals("")) { code = label.substring(1)+":\n"; } LLVM_GenericCommand tmp = firstCommand; while(tmp != null){ code += "\t"+tmp.toString(); tmp = tmp.getSuccessor(); } return code; } public String toGraph() { String graph = "\""+label+"\" [ style = \"filled, bold\" penwidth = 5 fillcolor = \"white\" fontname = \"Courier New\" shape = \"Mrecord\" label =<<table border=\"0\" cellborder=\"0\" cellpadding=\"3\" bgcolor=\"white\"><tr><td bgcolor=\"black\" align=\"center\" colspan=\"2\"><font color=\"white\">"+label+"</font></td></tr>"; LLVM_GenericCommand tmp = firstCommand; while(tmp != null){ graph += "<tr><td align=\"left\">"+ tmp.toString() +"</td></tr>"; tmp = tmp.getSuccessor(); } return graph + "</table>> ];"; } }