/* * This file is part of JOP, the Java Optimized Processor * see <http://www.jopdesign.com/> * * Copyright (C) 2010, Stefan Hepp (stefan@stefant.org). * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.jopdesign.jcopter; import com.jopdesign.common.AppInfo; import com.jopdesign.common.AppSetup; import com.jopdesign.common.EmptyTool; import com.jopdesign.common.code.ControlFlowGraph; import com.jopdesign.common.config.Config; import com.jopdesign.common.config.Config.BadConfigurationException; import com.jopdesign.common.config.OptionGroup; import com.jopdesign.dfa.DFATool; import com.jopdesign.wcet.WCETProcessorModel; import com.jopdesign.wcet.WCETTool; import com.jopdesign.wcet.jop.MethodCache; import org.apache.log4j.Logger; /** * User: Stefan Hepp (stefan@stefant.org) * Date: 18.05.2010 */ public class JCopter extends EmptyTool<JCopterManager> { public static final String VERSION = "0.1"; public static final String CONFIG_FILE_NAME = "jcopter.properties"; public static final String LOG_ROOT = "jcopter"; public static final String LOG_ANALYSIS = "jcopter.analysis"; public static final String LOG_OPTIMIZER = "jcopter.optimizer"; public static final String LOG_INLINE = "jcopter.inline"; private static final Logger logger = Logger.getLogger(LOG_ROOT + ".JCopter"); private final JCopterManager manager; private JCopterConfig config; private PhaseExecutor executor; private DFATool dfaTool; private WCETTool wcetTool; public JCopter() { super(VERSION); manager = new JCopterManager(); } @Override public JCopterManager getEventHandler() { return manager; } @Override public void registerOptions(Config config) { OptionGroup options = config.getOptions(); JCopterConfig.registerOptions(options); // TODO add options/profiles/.. to this so that only a subset of // optimizations/analyses are initialized ? Overwrite PhaseExecutor for this? // Or user simply uses phaseExecutor directly PhaseExecutor.registerOptions(config); } @Override public void onSetupConfig(AppSetup setup) throws Config.BadConfigurationException { OptionGroup options = setup.getConfig().getOptions(); config = new JCopterConfig(options); // Silence the CFG.. We know that ATHROW is bad. ControlFlowGraph.setIgnoreATHROW(true); } @Override public void onSetupAppInfo(AppSetup setup, AppInfo appInfo) throws BadConfigurationException { OptionGroup options = setup.getConfig().getOptions(); config.initialize(); executor = new PhaseExecutor(this, options); } public JCopterConfig getJConfig() { return config; } public PhaseExecutor getExecutor() { return executor; } public DFATool getDfaTool() { return dfaTool; } public void setDfaTool(DFATool dfaTool) { this.dfaTool = dfaTool; } public WCETTool getWcetTool() { return wcetTool; } public WCETProcessorModel getWCETProcessorModel() { return wcetTool.getWCETProcessorModel(); } public MethodCache getMethodCache() { return getWCETProcessorModel().getMethodCache(); } public void setWcetTool(WCETTool wcetTool) { this.wcetTool = wcetTool; } public boolean useDFA() { return dfaTool != null; } public boolean useWCA() { return getJConfig().useWCA(); } /** * Run all configured optimizations and perform the required analyses. */ public void optimize() { executor.setUpdateDFA(useDFA() && dfaTool.doUseCache() ); // - (optional) perform receiver type DFA: reduce callgraph, maybe eliminate // some nullpointer-checks. This is used only for the SimpleInliner since this will // invalidate the results (see Callgraph#merge). We skip this for -O1 to save some time. boolean firstPassDFA = useDFA() && !config.doOptimizeFastOnly(); if (firstPassDFA) { executor.dataflowAnalysis(false); } // - build callgraph: uses DFA results if available, else do some simple thinning executor.buildCallGraph(firstPassDFA); executor.dumpCallgraph("callgraph"); // - Kill'em all, since we do not use them and we do not update them so they will get out-of-date executor.removeDebugAttributes(); // - perform simple, guaranteed optimizations (everything to reduce code size!) // (inline 2/3 byte methods, load/store eliminate, peephole, dead-code elimination, constant folding, ..) // Be aware that we only have DFA receiver analysis data here (no loopbounds)! executor.cleanupMethodCode(); // - perform simple inlining: guaranteed not to increase worst case executor.performSimpleInline(); if (getJConfig().doOptimizeNormal()) { // - Rebuild callgraph and rerun DFA analyses since SimpleInliner changed the callstrings // and we do not have an implementation for Callgraph#merge and a framework to notify analyses // of callstring/callgraph changes (yet..) if (useDFA()) { executor.dataflowAnalysis(true); } executor.buildCallGraph(useDFA()); // - Now we have full DFA results (if enabled) and an updated callgraph, now would be the time // for some cleanup optimizations before we start the WCA (but we may not have Loopbounds yet) // - perform inlining (check previous analysis results to avoid creating nullpointer checks), // duplicate/rename/.. methods, perform method extraction/splitting too? executor.performGreedyOptimizer(); } else { // we need an up-to-date call graph for code cleanup, but we skip the second full-blown DFA run if // we only optimize at O1 executor.buildCallGraph(false); } // - perform code cleanup optimizations (load/store/param-passing, constantpool cleanup, // remove unused members, constant folding, dead-code elimination (remove some more NP-checks,..), // remove NOPs, ... ) executor.cleanupMethodCode(); executor.removeUnusedMembers(); executor.relinkInvokesuper(); executor.cleanupConstantPool(); if (getJConfig().doOptimizeNormal()) { // We need to write the DFA results first. This modifies the CP and creates new entries // due to dumb bcel creating debug attributes, but we need them in the class files, else the CP will // not match up, since they might be created in a different order on load executor.writeResults(); } } public static void main(String[] args) { // setup some defaults AppSetup setup = new AppSetup(); setup.setUsageInfo("jcopter", "A WCET driven Java bytecode optimizer."); setup.setVersionInfo(VERSION); // We do not load a config file automatically, user has to specify it explicitly to avoid // unintentional misconfiguration //setup.setConfigFilename(CONFIG_FILE_NAME); DFATool dfaTool = new DFATool(); WCETTool wcetTool = new WCETTool(); JCopter jcopter = new JCopter(); wcetTool.setAvailableOptions(false, true, false, false); setup.registerTool("dfa", dfaTool, true, false); setup.registerTool("wca", wcetTool); setup.registerTool("jcopter", jcopter); setup.addSourceLineOptions(true); setup.initAndLoad(args, true, true, true); if (setup.useTool("dfa")) { wcetTool.setDfaTool(dfaTool); jcopter.setDfaTool(dfaTool); } jcopter.setWcetTool(wcetTool); wcetTool.getEventHandler().setIgnoreMissingLoopBounds(!jcopter.useWCA()); // run optimizations jcopter.optimize(); // write results setup.writeClasses(); } }