/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * KeyholeOptimizer.java * Created: Sep 23, 2002 at 3:49:12 PM * By: Raymond Cypher */ package org.openquark.cal.internal.machine.g; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.openquark.cal.compiler.DataConstructor; import org.openquark.cal.internal.machine.CodeGenerationException; import org.openquark.cal.machine.Program.ProgramException; import org.openquark.cal.module.Cal.Core.CAL_Prelude; /** * This is the KeyholeOptimizer class * * Does code optimizations by scanning * instruction sequences for replaceable * patterns. * * @author Raymond Cypher */ class KeyholeOptimizer { /** * Create a KeyholeOptimizer */ public KeyholeOptimizer () { } public Code optimizeCode (Code code) throws CodeGenerationException { InstructionList gp = new InstructionList (); gp.code (code.getInstructions ()); gp = optimizeCode (gp); return new Code (gp); } public InstructionList optimizeCode (InstructionList code) throws CodeGenerationException { try { code = collapseMkaps (code); code = collapseSlides (code); code = removeTrueFalseFunction (code); Iterator<Instruction> it = code.iterator (); while (it.hasNext ()) { Instruction i = it.next (); optimizeInstruction (i); } code = fixupI_Cond (code); code = fixupI_Switch (code); } catch (ProgramException e) { } return code; } /** * Optimize the code contributions of a given instruction (if any). */ private void optimizeInstruction (Instruction i) throws CodeGenerationException { if (i instanceof Instruction.I_Cond) { Instruction.I_Cond i_cond = (Instruction.I_Cond)i; i_cond.setTrueCode (optimizeCode (i_cond.getTrueCode())); i_cond.setFalseCode (optimizeCode (i_cond.getFalseCode())); } else if (i instanceof Instruction.I_Switch) { Instruction.I_Switch i_switch = (Instruction.I_Switch)i; java.util.Iterator<?> keysIterator = i_switch.getMap().keySet().iterator(); while (keysIterator.hasNext()) { Object altTag = keysIterator.next(); Code code = (Code)i_switch.getMap().get(altTag); code = optimizeCode (code); i_switch.getMap().put (altTag, code); } } } /** * Collapse sequential I_Mkap instructions into a single I_MkapN instruction. * @param code InstructionList * @return InstructionList */ private InstructionList collapseMkaps (InstructionList code) { Iterator<Instruction> it = code.iterator (); InstructionList newInstructions = new InstructionList (); Instruction lastI = null; while (it.hasNext ()) { Instruction i = it.next (); if (i instanceof Instruction.I_MkapN && lastI instanceof Instruction.I_MkapN) { Instruction.I_MkapN ni = new Instruction.I_MkapN (lastI.getN() + i.getN()); newInstructions.remove(newInstructions.size() - 1); newInstructions.add (ni); lastI = ni; } else { newInstructions.add (i); lastI = i; } } return newInstructions; } /** * Collapse sequential I_Slide instructions into a single I_Slide instruction. * @param code InstructionList * @return InstructionList */ private InstructionList collapseSlides (InstructionList code) { Iterator<Instruction> it = code.iterator (); InstructionList newInstructions = new InstructionList (); Instruction lastI = null; while (it.hasNext ()) { Instruction i = it.next (); if (i instanceof Instruction.I_Slide && lastI instanceof Instruction.I_Slide) { Instruction.I_Slide ni = new Instruction.I_Slide (lastI.getN() + i.getN()); newInstructions.remove (newInstructions.size() - 1); newInstructions.add (ni); lastI = ni; } else { newInstructions.add (i); lastI = i; } } return newInstructions; } /** * Replace the sequence I_PushGlobal <Prelude.False>, I_Eval or the * sequence I_PushGlobal <Prelude.True>, I_Eval or I_PackCons0 (boolean tag) * with a simple push of a boolean value node. * @param code InstructionList * @throws ProgramException * @return InstructionList */ private InstructionList removeTrueFalseFunction (InstructionList code) throws ProgramException { Iterator<Instruction> it = code.iterator (); InstructionList newInstructions = new InstructionList (); boolean dropped = false; // Remove the I_PushGlobal I_Eval that resolves to a boolean. while (it.hasNext ()) { Instruction i = it.next (); if (i instanceof Instruction.I_PushGlobal && (i.getName().equals (CAL_Prelude.DataConstructors.False) || i.getName().equals (CAL_Prelude.DataConstructors.True))) { dropped = true; if (i.getName().equals (CAL_Prelude.DataConstructors.True)) { newInstructions.add (Instruction.I_PushTrue); } else { newInstructions.add (Instruction.I_PushFalse); } } else { if (i != Instruction.I_Eval) { newInstructions.add (i); } else { if (!dropped) { newInstructions.add (i); } } dropped = false; } } // Remove packCons0 instructions that resolve to a boolean. code = newInstructions; it = code.iterator (); newInstructions = new InstructionList (); while (it.hasNext ()) { Instruction i = it.next (); if (i instanceof Instruction.I_PackCons0) { if (((Instruction.I_PackCons0)i).getInfo() == Boolean.TRUE) { newInstructions.add (Instruction.I_PushTrue); } else if (((Instruction.I_PackCons0)i).getInfo() == Boolean.FALSE) { newInstructions.add (Instruction.I_PushFalse); }else if (((Instruction.I_PackCons0)i).getInfo() instanceof DataConstructor && ((DataConstructor)((Instruction.I_PackCons0)i).getInfo()).getName().equals (CAL_Prelude.DataConstructors.True)) { newInstructions.add (Instruction.I_PushTrue); } else if (((Instruction.I_PackCons0)i).getInfo() instanceof DataConstructor && ((DataConstructor)((Instruction.I_PackCons0)i).getInfo()).getName().equals (CAL_Prelude.DataConstructors.False)) { newInstructions.add (Instruction.I_PushFalse); } else { newInstructions.add (i); } } else { newInstructions.add (i); } } return newInstructions; } private InstructionList fixupI_Cond (InstructionList code) { boolean change = true; InstructionList oldCode = code; while (change) { change = false; Iterator<Instruction> it = oldCode.iterator(); InstructionList newCode = new InstructionList (); while (it.hasNext ()) { Instruction i = it.next (); if (i instanceof Instruction.I_Cond) { change = true; Instruction.I_Cond ic = (Instruction.I_Cond)i; Code thenC = ic.getTrueCode(); Code elseC = ic.getFalseCode(); Instruction.I_CondJ ij = new Instruction.I_CondJ (elseC.getNInstructions() + 1); Instruction.I_Jump jump = new Instruction.I_Jump (thenC.getNInstructions()); newCode.add (ij); newCode.code (elseC.getInstructions()); newCode.add (jump); newCode.code (thenC.getInstructions()); } else { newCode.add (i); } } oldCode = newCode; } return oldCode; } private InstructionList fixupI_Switch (InstructionList code) throws CodeGenerationException { boolean change = true; InstructionList oldCode = code; while (change) { change = false; Iterator<Instruction> it = oldCode.iterator(); InstructionList newCode = new InstructionList (); while (it.hasNext ()) { Instruction inst = it.next (); if (inst instanceof Instruction.I_Switch) { change = true; Instruction.I_Switch is = (Instruction.I_Switch)inst; Map<Object, Integer> jumpMap = new HashMap<Object, Integer> (); Map<Object, Object> codesMap = is.getMap(); newCode.add (new Instruction.I_SwitchJ (jumpMap, is.getErrorInfo())); List<Object> tags = new ArrayList<Object> (); List<Code> codes = new ArrayList<Code> (); int totalLengthOfAltCode = 0; for (final Map.Entry<Object, Object> entry : codesMap.entrySet()) { Object altTag = entry.getKey(); Code altCode = (Code)entry.getValue(); tags.add (altTag); codes.add (altCode); totalLengthOfAltCode += altCode.getNInstructions(); } totalLengthOfAltCode += (tags.size() - 1); int jump = 0; for (int i = 0, nTags = tags.size(); i < nTags; ++i) { Object tag = tags.get (i); if (tag instanceof Boolean) { jumpMap.put(Integer.valueOf(((Boolean)tag).booleanValue() ? 1 : 0), Integer.valueOf(jump)); } else if (tag instanceof DataConstructor) { jumpMap.put(Integer.valueOf(((DataConstructor)tag).getOrdinal()), Integer.valueOf(jump)); } else if (tag instanceof Integer) { jumpMap.put(tag, Integer.valueOf(jump)); } else if (tag instanceof Character) { jumpMap.put(Integer.valueOf(((Character)tag).charValue()), Integer.valueOf(jump)); } else { if (!(tag instanceof String)) { throw new CodeGenerationException("Invalid tag type in switch: " + tag.getClass().getName()); } jumpMap.put(tag, Integer.valueOf(jump)); } jump += (codes.get (i).getNInstructions() + 1); newCode.code (codes.get (i).getInstructions()); if (totalLengthOfAltCode - jump > 0) { newCode.code (new Instruction.I_Jump (totalLengthOfAltCode - jump)); } } } else { newCode.add (inst); } } oldCode = newCode; } return oldCode; } }